Этот вопрос возникает из дискуссии о том, использовать ли функцию ранжирования SQL или нет в конкретном случае .
Любая распространенная СУБД включает в себя некоторые функции ранжирования, то есть ее язык запросов имеет такие элементы, как TOP n ... ORDER BY key
, ROW_NUMBER() OVER (ORDER BY key)
или ORDER BY key LIMIT n
( обзор ).
Они отлично справляются с задачей повышения производительности, если вы хотите представить только небольшой фрагмент из огромного количества записей. Но они также вносят серьезную ошибку: если key
не является уникальным, результаты являются недетерминированными. Рассмотрим следующий пример:
users
user_id name
1 John
2 Paul
3 George
4 Ringo
logins
login_id user_id login_date
1 4 2009-08-17
2 1 2009-08-18
3 2 2009-08-19
4 3 2009-08-20
Предполагается, что запрос вернет человека, который вошел последним:
SELECT TOP 1 users.*
FROM
logins JOIN
users ON logins.user_id = users.user_id
ORDER BY logins.login_date DESC
Как и ожидалось, возвращается George
, и все выглядит хорошо. Но затем в таблицу logins
вставляется новая запись:
1 4 2009-08-17
2 1 2009-08-18
3 2 2009-08-19
4 3 2009-08-20
5 4 2009-08-20
Что теперь возвращает запрос выше? Ringo
? George
? Ты не можешь сказать. Насколько я помню например MySQL 4.1 возвращает первую физически созданную запись, которая соответствует критериям, то есть результат будет George
. Но это может варьироваться от версии к версии и от СУБД к СУБД. Что должно было быть возвращено? Кто-то может сказать Ringo
, поскольку он, по-видимому, вошел последним, но это чистая интерпретация По моему мнению, оба должны были быть возвращены, потому что вы не можете однозначно определиться с доступными данными.
Таким образом, этот запрос соответствует требованиям:
SELECT users.*
FROM
logins JOIN
users ON
logins.user_id = users.user_id AND
logins.login_date = (
SELECT max(logins.login_date)
FROM
logins JOIN
users ON logins.user_id = users.user_id)
В качестве альтернативы некоторые СУБД предоставляют специальные функции (например, Microsoft SQL Server 2005 вводит TOP n WITH TIES ... ORDER BY key
(предлагается gbn ), RANK
и DENSE_RANK
для этой самой цели).
Если вы ищете SO, например, ROW_NUMBER
вы найдете множество решений, которые предлагают использовать функциональность ранжирования и упустить, чтобы указать на возможные проблемы.
Вопрос: Какой совет следует дать, если предлагается решение, включающее функции ранжирования?