Чому вперше сортуються NULL?


20

Чому так, що коли у нас стовпчик NULL і впорядковується за значенням, що збільшується, NULLs сортуються спочатку?

select 1 as test
union all
select 2
union all
select NULL
union all
select 3
union all
select 4
order by test

призводить до

NULL
1
2
3
4

Я продовжую думати, що NULL означав "Індетермінант" або можливий "Невідомий". Якщо це правда, то хіба вони б не сортувались останніми, оскільки значення може бути більшим за всі інші значення? (Або це десь варіант сортування?)

Я перебуваю на SQL Server 2008R2, але я підозрюю, що це справедливо на всіх SQL серверах, і, ймовірно, на всіх RDBMS.


1
Oracle списує його останнім. Це мене накрутило один раз, вважаючи, що він повинен вести себе як SQL Server.
Андрій Ронеа

2
"Якщо це правда, чи не сортували б їх останніми, оскільки значення може бути більшим, ніж усі інші значення". Значення може бути менше, ніж усі інші значення. Для мене інтуїтивно зрозуміло, що значення фальси, як null, має знаходитись у нижній частині. І практично, оскільки на практиці ви часто хочете використовувати descзамовлення, щоб показати найбільші або найсвіжіші речі, і в такому випадку я буду радий, коли нульові речі будуть останніми.
mahemoff

База даних робить те, що ви їй говорите. Якщо ви знаєте, що ваші дані містять нулі, і у вас є певна бізнес-причина для сортування даних певним чином, то вам потрібно вказати це або в запиті, або в коді / перегляді, який обробляє / відображає дані. Ніколи не залишайте сортування за типовою поведінкою бази даних.
нічогонеобхідного

Відповіді:


19

BOL : значення NULL вказує на те, що значення невідоме. Значення NULL відрізняється від порожнього або нульового значення. Немає двох нульових значень рівних. Порівняння двох нульових значень або між NULL та будь-яким іншим значенням повертається невідомим, оскільки значення кожного NULL невідоме.

NULL означає невідомий. Жодна інша інтерпретація не є дійсною.

Якщо це правда, то хіба вони б не сортувались останніми, оскільки значення може бути більшим за всі інші значення?

Не може бути . Немає потенційної цінності. Невідомо невідомо невідомо.

Щодо того, чому воно з’являється першим, а не останнім, це не задовольняється опублікованими стандартами SQL і, на жаль, залишається на розсуд постачальника RDBMS:

Вікіпедія : Стандарт SQL не чітко визначає порядок сортування за замовчуванням для Nulls. Натомість у відповідних системах Nulls можна сортувати до або після всіх значень даних, використовуючи відповідно NULLS FIRST або NULLS LAST у списку ORDER BY відповідно. Однак не всі постачальники СУБД реалізують цю функціональність. Постачальники, які не реалізують цю функціональність, можуть вказувати різні способи сортування Null в СУБД.


Отже, це виклик рішення. Це має багато сенсу. Спасибі!
Річард

6

Ви правильні, що NULLможе означати "Індетермінант" або "Невідомий" або "Ще невідомо" або "Не застосовується". Але немає підстав ставити нулів першими чи останніми. Якщо ми не знаємо фактичних значень, то вони можуть бути невеликими або великими.

Я думаю, що стандартом для визначення бажаної поведінки Nulls під час сортування є:

ORDER BY 
    test NULLS LAST                      --- or NULLS FIRST for the opposite

На жаль, SQL-сервер ще не прийняв цей синтаксис. Якщо я не помиляюся, PostgreSQL і Oracle мають це.

Одне рішення:

ORDER BY 
     CASE WHEN test IS NOT NULL 
            THEN 0 
          ELSE 1 
     END 
   , test

Ще одне рішення, яке потребує коригування залежно від типу даних - але воно не буде добре виконано, оскільки не може використовувати індекс на (test):

ORDER BY 
    COALESCE(test, 2147483647)               --- if it's a 4-byte signed integer

Таким чином ЗАМОВЛЕННЯ ПО КОАЛЕССІ (тест, 2147483647) сервер SQL не може використовувати Index.
Ардалан Шахголі

3

Я не знаю, чому це робиться саме так, але за визначенням NULLS не можна порівнювати з non-NULLS, тому вони або повинні йти на початку або в кінці (відповідь Марка охоплює це набагато детальніше).

Отримати потрібну вам поведінку - наскільки я знаю, немає варіанту сортування, щоб оставити нулі останніми, тому вам доведеться перебирати їх за допомогою обчисленої колонки, щоб змусити їх тривати. Однак у SQL Server ви не можете замовити обчислений стовпець ( CASE WHEN ...), коли ваші дані містять набір операторів ( UNION ALL). Так:

CREATE TABLE #sorttest(test int)
INSERT INTO #sorttest values(1)
INSERT INTO #sorttest values(5)
INSERT INTO #sorttest values(4)
INSERT INTO #sorttest values(NULL)
INSERT INTO #sorttest values(3)
INSERT INTO #sorttest values(2)
SELECT test
FROM #sorttest
ORDER BY CASE WHEN test IS NULL THEN 1 ELSE 0 END, test

DROP TABLE #sorttest

Буде працювати над сортуванням нулів останнім часом. Якщо вам доведеться використовувати UNION(або EXCEPTабо INTERSECTS) для створення набору даних, тоді скиньте свої дані у тимчасову таблицю, як описано вище.


... або використовувати вихідний UNIONed як похідну таблицю.
Андрій М

0

Якщо ви маєте справу з номерами, ви також можете використовувати

ORDER BY -test DESC

NULLє найменшими можливими значеннями, тому DESCставить їх у кінці. Тим часом ненульові значення мають перевернутий знак, так що DESCнасправді це значення є ASCреальними. Це має бути швидше, ніж CASEя вважаю, що оптимізатор запитів також може використовувати індекси на testстовпці.


3
Ні, він не зможе використовувати індекс для сортування. Якщо у вас немає індексу на обчислений вираз (- test).
ypercubeᵀᴹ

1
Розумна, хоч і обмежена лише числовими даними (все одно підходить для прикладу ОП). Я не впевнений, чи справді це буде швидше, ніж використання CASE, але я впевнений, що він не використовував би індекс (якщо тільки не те, що говорить @ ypercubeᵀᴹ - але тоді вираз CASE може бути індексований точно так само).
Андрій М
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.