Чому COALESCE в підзапиті повертає NULL?


15

З огляду на цю схему:

CREATE TABLE #TEST_COALESCE
(
    Id int NOT NULL,
    DateTest datetime NOT NULL,
    PRIMARY KEY (Id, DateTest)
);

INSERT INTO #TEST_COALESCE VALUES
(1, '20170201'),
(1, '20170202'),
(1, '20170203'),
(2, '20170204'),
(2, '20170205'),
(2, '20170206');

Якщо я використовую COALESCE в підзапиті, він повертає NULL.

SELECT  t1.Id, t1.DateTest,
        (SELECT TOP 1 COALESCE(t2.DateTest, t1.DateTest)
         FROM         #TEST_COALESCE t2
         WHERE        t2.Id = t1.Id
         AND          t2.DateTest > t1.DateTest
         ORDER BY     t2.Id, t2.DateTest) NextDate
FROM    #TEST_COALESCE t1;

+----+---------------------+---------------------+
| Id | DateTest            | NextDate            |
+----+---------------------+---------------------+
| 1  | 01.02.2017 00:00:00 | 02.02.2017 00:00:00 |
| 1  | 02.02.2017 00:00:00 | 03.02.2017 00:00:00 |
| 1  | 03.02.2017 00:00:00 | NULL                |
| 2  | 04.02.2017 00:00:00 | 05.02.2017 00:00:00 |
| 2  | 05.02.2017 00:00:00 | 06.02.2017 00:00:00 |
| 2  | 06.02.2017 00:00:00 | NULL                |
+----+---------------------+---------------------+

Однак якщо він розміщений поза підзапитом:

SELECT  t1.Id, t1.DateTest,
        COALESCE((SELECT TOP 1 t2.DateTest
                 FROM         #TEST_COALESCE t2
                 WHERE        t2.Id = t1.Id
                 AND          t2.DateTest > t1.DateTest
                 ORDER BY     t2.Id, t2.DateTest), t1.DateTest) NextDate
FROM    #TEST_COALESCE t1;

+----+---------------------+---------------------+
| Id | DateTest            | NextDate            |
+----+---------------------+---------------------+
| 1  | 01.02.2017 00:00:00 | 02.02.2017 00:00:00 |
| 1  | 02.02.2017 00:00:00 | 03.02.2017 00:00:00 |
| 1  | 03.02.2017 00:00:00 | 03.02.2017 00:00:00 |
| 2  | 04.02.2017 00:00:00 | 05.02.2017 00:00:00 |
| 2  | 05.02.2017 00:00:00 | 06.02.2017 00:00:00 |
| 2  | 06.02.2017 00:00:00 | 06.02.2017 00:00:00 |
+----+---------------------+---------------------+

Чому перший підзапит не повертається t1.DateTest:?

http://rextester.com/CNDOO40877


3
ВИКЛИЧНО використання демо-таблиці та запиту репро, до речі. Я не збирався публікувати відповідь, але тоді я пішов: "Він поставив всю цю роботу під питання, щонайменше, що я міг би зробити, це вкласти якусь роботу у відповідь, ха-ха".
Брент Озар

Привіт @BrentOzar, дякую за детальну відповідь, це кристально ясно.
McNets

Відповіді:


16

Речі у виділеному повертаються лише у тому випадку, якщо в операторі FROM є рядки, повернуті.

Спочатку давайте подумаємо про це концептуально.

Запит 1 такий:

"Відшукайте всі Ferraris у вашому гаражі. Для кожного Ferrari дайте мені номер номерного знака, або якщо у нього немає номерного знака, дайте мені" NO FERRARIS FOUND "."

Запит повернеться без рядків - бо в гаражі не було Ferrari. (Принаймні, в моєму гаражі не знайдено жодного ряду.)

Запит 2 відрізняється:

"Ідіть у гараж. Якщо ви знайдете номерний знак на Ferrari, дайте мені це - інакше дайте мені" NO FERRARIS FOUND "."

Ось чому з'єднання повинне бути поза операцією пошуку: потрібно, щоб це сталося навіть тоді, коли в наборі результатів немає рядків.

Тепер давайте розглянемо ваш запит.

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

SELECT TOP 1 COALESCE(t2.DateTest, 'NO FERRARIS FOUND')
     FROM         #TEST_COALESCE t2
     WHERE        t2.Id = 1
     AND          t2.DateTest > '2017-02-03 00:00:00.000'
     ORDER BY     t2.Id, t2.DateTest

У пункті WHERE я чітко зашифрований Id = 1 та DateTest> '2017-02-03 00: 00: 00.000'. Коли цей запит запускається, він не дає результатів:

Ферраріс не знайдено

Ось чому COALESCE не працює: у цьому наборі результатів не було рядків, а у вашому гаражі не було Ferraris. Оволодійте цією концепцією, і у вас з'явиться Ферраріс у вашому ... зачекайте хвилинку ... Я освоїв цю концепцію, і в моєму гаражі немає Ферраріса ...


3
Ха-ха-ха, подивись уважно, ти впевнений, що там немає 360 Модена ?
McNets

3
@McNets Мені, мабуть, варто спробувати перевірити ще раз, щоб бути в безпеці.
Брент Озар
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.