Альтернатива использованию GROUP BY без агрегатов для получения отличного «лучшего» результата - PullRequest
3 голосов
/ 17 января 2011

Я пытаюсь получить «наилучшую» запись из таблицы SQL.

Рассмотрим таблицу, содержащую сериалы: id, title, эпизод, is_hidef, is_verified например:

id title         ep hidef verified
1  The Simpsons  1  True  False
2  The Simpsons  1  True  True
3  The Simpsons  1  True  True
4  The Simpsons  2  False False
5  The Simpsons  2  True  False

Могут быть повторяющиеся строки для одного заголовка и эпизода, которые могут иметь или не иметь разные значения для логических полей. Может быть больше столбцов, содержащих дополнительную информацию, но это неважно.

Мне нужен набор результатов, который дает мне лучшую строку (так что is_hidef и is_verified оба "true", где это возможно) для каждого эпизода. Для строк, которые считаются «равными», я хочу самую последнюю строку (естественное упорядочение или упорядочение по случайному столбцу datetime).

3  The Simpsons  1  True  True
5  The Simpsons  2  True  False

В прошлом я бы использовал следующий запрос:

SELECT * FROM shows WHERE title='The Simpsons' GROUP BY episode ORDER BY is_hidef, is_verified

Это работает в MySQL и SQLite, но идет вразрез со спецификацией SQL (GROUP BY требует расширений и т. Д. И т. Д.). Мне не очень интересно снова слышать, почему MySQL настолько плох, что позволяет это; но я очень заинтересован в поиске альтернативного решения, которое будет работать и на других движках (бонусные баллы, если вы сможете дать мне код ORM django для него).

Спасибо =)

Ответы [ 2 ]

2 голосов
/ 17 января 2011

В некотором роде похож на Andomar, но этот действительно работает.

select C.*
FROM
(
    select min(ID) minid
    from (
        select distinct title, ep, max(hidef*1 + verified*1) ord
        from tbl
        group by title, ep) a
    inner join tbl b on b.title=a.title and b.ep=a.ep and b.hidef*1 + b.verified*1 = a.ord
    group by a.title, a.ep, a.ord
) D inner join tbl C on D.minid = C.id

Прерывание связи первого уровня преобразует биты (SQL Server) или MySQL в логическое целое значение, используя * 1, и столбцы добавляютсяпроизвести «лучшее» значение.Вы можете дать им вес, например, если hidef> проверено, тогда используйте hidef * 2 + Verified * 1 , что может дать 3,2,1 или 0.

2-й уровень выглядит среди тех,«лучшего» сценария и извлекает минимальный идентификатор (или какой-либо другой столбец тай-брейка).Это важно для сокращения набора результатов нескольких совпадений до одной записи.

В этом конкретном случае (схема таблицы) внешний выбор использует прямой ключ для извлечения сопоставленных записей.

1 голос
/ 17 января 2011

Это, по сути, форма задачи группового максимума со связями .Я не думаю, что существует стандартное решение SQL.Подобное решение будет работать хорошо:

SELECT  s2.id
,       s2.title
,       s2.episode
,       s2.is_hidef
,       s2.is_verified
FROM    (
        select  distinct title
        ,       episode
        from    shows
        where   title = 'The Simpsons' 
        ) s1
JOIN    shows s2
ON      s2.id = 
        (
        select  id
        from    shows s3
        where   s3.title = s1.title
                and s3.episode = s1.episode
        order by
                s3.is_hidef DESC
        ,       s3.is_verified DESC
        limit   1
        )

Но, учитывая стоимость читабельности, я бы придерживался вашего исходного запроса.

...