Получить максимальную/минимальную запись

Довольно сложный SQL-запрос, над которым я работал, заставил меня задуматься об ограничении (ANSI) SQL:

Есть ли способ получить запись, которая является максимальной или минимальной по отношению к произвольному порядку?

Другими словами:

Учитывая такой запрос:

SELECT * FROM mytable WHERE <various conditions> ORDER BY <order clause>

можно ли написать запрос, который возвращает только первую строку (возможно, путем преобразования предложения порядка во что-то другое)?

Я знаю, что вы можете сделать это, используя LIMIT (MySQL)/ROWNUM (Oracle) или подобное, но это не стандартный SQL.

Я также знаю, что вы можете сделать это, выбрав максимальное/минимальное значение, которое вас интересует, в подзапросе (используя MIN()/MAX()), а затем использовать этот результат в качестве критерия в вашем основном SELECT, т.е.:

SELECT * FROM mytable WHERE <various conditions> AND myMaxColumn=(
  SELECT MAX(myMaxColumn) FROM mytable WHERE <various conditions>
)

Но это работает, только если я хочу отсортировать по одному столбцу. Я не вижу способа обобщить это на несколько столбцов (кроме вложения приведенного выше решения, но это будет означать 2 ^ n SELECT при упорядочении по n столбцам).

Итак, есть ли лучший способ в стандартном SQL, чем вложение нескольких подзапросов?

Связанный вопрос задан в Создайте запрос SQL для получения самых последних записи. Однако ответы там предполагают либо использование LIMIT и друзей, либо использование подзапроса с MAX(), как описано выше, оба из которых не являются решениями моего вопроса.


person sleske    schedule 13.07.2009    source источник
comment
Примечание. Только что нашел похожий вопрос: stackoverflow.com/questions/121387/   -  person sleske    schedule 28.03.2012


Ответы (2)


SQL:2003 определяет концепцию оконных функций, одна из которых:

SELECT  *
FROM    (
        SELECT  *, ROW_NUMBER() OVER (ORDER BY col1, col2, col3) AS rn
        FROM    mytable
        ) q
WHERE   rn = 1

вернет вам эту первую запись.

На данный момент его поддерживают SQL Server, Oracle, а начиная с Jul 01, 2009PostgreSQL 8.4.

Однако обратите внимание, что ROW_NUMBER() в Oracle менее эффективен, чем собственный способ ограничения количества записей (т. е. ROWNUM).

Смотрите эту статью в моем блоге для сравнения производительности:

SQL:2008 предлагает для этого еще один пункт:

SELECT  *
FROM    mytable
ORDER BY
        col1, col2, col3
FETCH FIRST 1 ROW ONLY

, но на данный момент именно этот синтаксис поддерживается только DB2 (AFAIK).

person Quassnoi    schedule 13.07.2009
comment
Это работает в базах данных, реализующих стандарт SQL2003. Он еще не поддерживается во многих базах данных, включая MySQL и PostgreSQL. Это определенно один из самых простых способов написания запроса такого типа. - person LBushkin; 13.07.2009
comment
Примечание. PostgreSQL теперь поддерживает как ROW_NUMBER(), так и FETCH FIRST/LAST... (представлено в PostgreSQL 8.4). Конечно, с MySQL еще не все в порядке. - person sleske; 15.10.2010
comment
И придирка: FETCH FIRST/LAST.. — это SQL 2008. Остальное — это SQL 2003, я полагаю. - person sleske; 15.10.2010

Если я вас правильно понял, я думаю, вы ищете предложение OVER, которое позволяет вам разбивать наборы результатов, определенные как часть Стандарт ANSI SQL 2003.

Он не очень последовательно реализован на платформах РСУБД.

person Ed Harper    schedule 13.07.2009