Reputation: 167
Essentially I want to order by "title", while grouping movies with the same, non-NULL "series_id" together within that order. Titles where series_id IS NULL should order by title only, while non NULL series_ids should order by "series_order" and be listed in the results based on "series"."title" within the original columns sort. I would prefer to achieve this within the query rather then loading the entire database and sorting from there.
What I have tried (MySQL 5.7.17):
ORDER BY title, series_id
SELECT * FROM media
ORDER BY title, series_id, series_order
LIMIT 0, 30
Does not account for titles in a series that are not alphabetically the same (see sample below).
ORDER BY CASE using CONCAT to sort by 'series'.'title' + 'media'.'series_order'
SELECT * FROM media
ORDER BY CASE
WHEN series_id IS NULL THEN title
ELSE CONCAT((SELECT title FROM series WHERE id = media.series_id), series_order)
END
LIMIT 0, 30
Th results are correctly ordered in a SQL Fiddle, but not on the dev server. To be fair this is still not the desired result as the 'series'.'title' may differ from the original movies title.
LEFT JOIN to include the 'series' table for sorting using the same idea
SELECT media.*, series.title, series.id FROM media
LEFT JOIN series ON media.series_id = series.id
LIMIT 0, 30
This does not order the data correctly, either.
Sample Data:
title | series_id | series_order |
---|---|---|
88 Minutes | NULL | NULL |
Live Free or Die Hard | 100094 | 4 |
3rd Rock from the Sun | 100000 | 2 |
2 Guns | NULL | NULL |
Die Hard | 100094 | 1 |
Evil Dead | NULL | NULL |
A Good Day to Die Hard | 100094 | 5 |
3rd Rock from the Sun | 100000 | 1 |
Desired Result | Order |
---|---|
2 Guns | NULL |
3rd Rock from the Sun | 1 |
3rd Rock from the Sun | 2 |
88 Minutes | NULL |
Die Hard | 1 |
Live Free or Die Hard | 4 |
A Good Day to Die Hard | 5 |
Evil Dead | NULL |
Primary Table: media Relevant Columns: title, series_id, series_order
Series Table: series Relevant Columns: id, title
Fiddle: http://sqlfiddle.com/#!9/efca7c/3
On the fiddle, option 2 appears to be working. On the dev version it only partially orders things.
EDIT: As it turns out the PHP code I was using to store the data before converting it to JSON was inevitably re-ordering the results by the Primary ID.
Upvotes: 1
Views: 690
Reputation: 47904
Based on the required convolution to achieve your desired sorting, if this was my application, I'd probably create a new column which contains the "base title" for the series and populate that value during insertion, then you could sort on that without any voodoo or eye-strain.
In the absence of modifying your table structure, I managed to downgrade a solution that was using ROW_NUMBER()
and PARTITION
(MySQL8.0 Demo) into a couple of nested subqueries -- it's not what I consider beautiful.
SQL (Demo)
SELECT m2.title grouping_title, m1.title, COALESCE(m2.title, m1.title), m1.series_order
FROM media m1
LEFT JOIN (
SELECT series_id, title
FROM media m3
WHERE series_order = (SELECT MIN(series_order) FROM media WHERE series_id = m3.series_id)
) m2 ON m1.series_id = m2.series_id
ORDER BY COALESCE(m2.title, m1.title), m1.series_order
You can modify the outer SELECT
as you wish, but I just wanted to show what the COALESCE()
function was generating. Effectively, I'm joining media table onto itself so that I can obtain the lowest series_order
value for a given series_id
. The title
in the THAT row represents the "base title" to be used in the first rule of the sorting algorithm -- unless it is NULL
, in which case, we just use the title
from the parent query.
For your application output, you will want to use the m1.title
and the m1.series_order
.
Upvotes: 1
Reputation: 11556
Use a CASE
expression to check whether seris_order value is null, if it is null then take only titlw, otherwise concatenate title with the series_order.
Query
Select case when series_order is null then title else concat(title, ' : Season ', series_order) end as title
From table_name
Order by 1;
Upvotes: 0