Почему 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

Вещи в select возвращаются, только если в операторе FROM возвращены строки.

Во-первых, давайте подумаем об этом концептуально.

Запрос 1 похож на:

«Найдите все Ferrari в своем гараже. Для каждого Ferrari, дайте мне номерной знак, или, если у него нет номера, дайте мне« NO FERRARIS FOUND ».»

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

Запрос 2 отличается:

«Идите в гараж. Если вы найдете номерной знак на Ferrari, дайте мне это - иначе, дайте мне« НИКАКОГО ФЕРРАРИСА НАЙДЕНО »».

Вот почему объединение должно быть за пределами операции поиска: это необходимо, даже если в наборе результатов нет строк.

Теперь давайте посмотрим на ваш запрос.

Я собираюсь вывести подзапрос самостоятельно, и я собираюсь жестко кодировать значения для одной из строк, где вы хотите, чтобы 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 не работает: в этом наборе результатов не было строк, и в вашем гараже не было Ferrari. Освойте эту концепцию, и у вас будет Ferrari в вашем ... подождите минуту ... Я освоил эту концепцию, и в моем гараже нет Ferrari ...


3
Хахаха, смотри внимательно, ты уверен, что там нет 360 Моден ?
Макнетс

3
@McNets Я, наверное, должен проверить еще раз, чтобы быть в безопасности.
Брент Озар
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.