Home > Software design >  MySQL: Order by 2nd column--if not null--while maintaining 1st column order
MySQL: Order by 2nd column--if not null--while maintaining 1st column order

Time:10-26

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

This does not account for titles in a series that are not alphabetically the same (see sample data 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 mostly ordered by title and are grouped by 'series_id', but not all results are in order.

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

The problem I am running into here is, with a LIMIT it selects the first 30 from media, and then only shows the 4 that have a series_id. And regardless of using LIMIT it groups all media with the same series_id together as one result.

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

Looking at the fiddle, option 2 appears to be working. In the dev version it only partially orders things. So I removed the LIMIT from my dev version and it is still out of order

Dev Server SS w/ LIMIT: https://prnt.sc/1xcyq4u Dev Server SS w/o LIMIT: https://prnt.sc/1xd0a2x

With LIMIT: 3rd Rock appears grouped, but out of order, and 2012 Doomsday always appears at the end regardless of the LIMIT being 0, 27 or 12, 20

W/O LIMIT: The results are showing up sorted by the primary_id.

CodePudding user response:

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;

CodePudding user response:

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() and PARTITION 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.

  • Related