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.
2
Answers
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
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()
andPARTITION
(MySQL8.0 Demo) into a couple of nested subqueries — it’s not what I consider beautiful.SQL (Demo)
You can modify the outer
SELECT
as you wish, but I just wanted to show what theCOALESCE()
function was generating. Effectively, I’m joining media table onto itself so that I can obtain the lowestseries_order
value for a givenseries_id
. Thetitle
in the THAT row represents the "base title" to be used in the first rule of the sorting algorithm — unless it isNULL
, in which case, we just use thetitle
from the parent query.For your application output, you will want to use the
m1.title
and them1.series_order
.