เหตุใดจึงต้องทำการเชื่อมโยงภายในข้อความค้นหาย่อยส่งกลับค่า 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 มีลักษณะดังนี้:

"ไปหาเฟอร์รารีทั้งหมดในโรงรถของคุณสำหรับเฟอร์รารี่แต่ละอันขอหมายเลขทะเบียนรถหรือถ้าไม่มีหมายเลขทะเบียนให้ฉัน 'NO FERRARIS FOUND'"

ข้อความค้นหาจะกลับมาโดยไม่มีแถว - เพราะไม่มีเฟอร์รารีในโรงรถ (อย่างน้อยก็ไม่พบแถวใด ๆ ในโรงรถของฉันเอง)

ข้อความค้นหา 2 แตกต่างกัน:

"ไปที่โรงรถถ้าคุณเจอป้ายทะเบียนบนรถเฟอร์รารี่ให้ฉันเถอะ - ไม่งั้นก็ขอให้ฉัน 'ไม่พบเฟอร์รารี"

นั่นเป็นสาเหตุที่การรวมกันต้องอยู่นอกการดำเนินการค้นหา: คุณต้องการให้มันเกิดขึ้นแม้ว่าจะไม่มีแถวในชุดผลลัพธ์

ตอนนี้เรามาดูคำถามของคุณ

ฉันจะลบข้อความค้นหาออกด้วยตัวเองและฉันจะใช้ค่าฮาร์โค้ดสำหรับหนึ่งในแถวที่คุณต้องการให้ 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 ฉันใช้รหัสยาก = 1 และ DateTest> '2017-02-03 00: 00: 00.000' เมื่อรันเคียวรีนี้จะไม่มีผลลัพธ์:

ไม่พบ Ferraris

นั่นเป็นเหตุผลที่ถ่านหินไม่ทำงาน: ไม่มีแถวในชุดผลลัพธ์นี้และไม่มี Ferraris ในโรงรถของคุณ ต้นแบบแนวคิดนั้นและคุณจะมีเฟอร์รารีในของคุณ ... รอสักครู่ ... ฉันเข้าใจแนวคิดนั้นแล้วและไม่มีเฟอร์รารีในโรงรถของฉัน ...


3
ฮ่าฮ่าฮ่าดูอย่างระมัดระวังคุณแน่ใจหรือว่าไม่มี360 Modenaอยู่ที่นั่น?
McNets

3
@ เครือข่ายฉันควรจะไปตรวจสอบอีกครั้งเพื่อความปลอดภัย
Brent Ozar
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.