Tại sao COALESCE trong truy vấn con trả về NULL?


15

Cho lược đồ này:

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');

Nếu tôi sử dụng COALESCE trong truy vấn con, nó sẽ trả về 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                |
+----+---------------------+---------------------+

Tuy nhiên, nếu nó được đặt bên ngoài truy vấn con:

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 |
+----+---------------------+---------------------+

Tại sao truy vấn con đầu tiên không trở lại : t1.DateTest?

http://rextester.com/CNDOO40877


3
Nhân tiện, sử dụng bảng demo và truy vấn repro. Tôi sẽ không đăng một câu trả lời, nhưng sau đó tôi đã nói, "Anh ấy đặt tất cả công việc này vào câu hỏi, điều tối thiểu tôi có thể làm là đưa một số công việc vào một câu trả lời, hahaha."
Brent Ozar

Xin chào @BrentOzar, cảm ơn câu trả lời chi tiết của bạn, nó rất rõ ràng.
McNets

Câu trả lời:


16

Những thứ trong phần chọn chỉ được trả về nếu có hàng được trả về trong câu lệnh TỪ.

Đầu tiên, hãy nghĩ về nó một cách khái niệm.

Truy vấn 1 giống như:

"Đi tìm tất cả những chiếc Ferraris trong nhà để xe của bạn

Truy vấn sẽ trở lại mà không có hàng - vì không có chiếc Ferrari trong nhà để xe. (Ít nhất, không có bất kỳ hàng nào được tìm thấy trong nhà để xe của riêng tôi.)

Truy vấn 2 là khác nhau:

"Đi đến nhà để xe. NẾU bạn tìm thấy biển số xe trên một chiếc Ferrari, hãy đưa cho tôi cái đó - nếu không, hãy cho tôi 'KHÔNG CÓ FERRARIS FOUND.'"

Đó là lý do tại sao sự kết hợp phải nằm ngoài hoạt động tìm kiếm: bạn cần nó xảy ra ngay cả khi không có hàng trong tập kết quả.

Bây giờ, hãy nhìn vào truy vấn của bạn.

Tôi sẽ tự mình thực hiện truy vấn con và tôi sẽ chuyển sang các giá trị mã cứng cho một trong các hàng mà bạn muốn COALESCE hoạt động, nhưng không thể:

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

Trong mệnh đề WHERE, tôi đã mã hóa cứng Id = 1 và DateTest> '2017/02/03 00: 00: 00.000'. Khi truy vấn này chạy, nó không trả về kết quả:

Không tìm thấy Ferraris

Đó là lý do tại sao COALESCE không hoạt động: không có hàng nào trong tập kết quả này và không có Ferraris trong nhà để xe của bạn. Nắm vững khái niệm đó, và bạn sẽ có Ferraris trong ... chờ một chút ... Tôi đã thành thạo khái niệm đó và không có Ferraris trong nhà để xe của tôi ...


3
Hahaha, nhìn kỹ xem, bạn có chắc là không có 360 Modena nào ở đó không?
McNets

3
@McNets Có lẽ tôi nên đi kiểm tra lại để đảm bảo an toàn.
Brent Ozar
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.