การเปรียบเทียบคอลัมน์ที่มี NULLS - มีวิธีที่สวยงามกว่านี้ไหม


16

ฉันรู้ว่าคุณไม่สามารถเปรียบเทียบค่ากับ NULL และคาดหวังผลลัพธ์โดยไม่ต้องเพิ่มบางอย่างในรหัสต่อไปนี้ ...

SELECT
    *
FROM 
    A INNER JOIN 
    B ON A.ID = B.ID
WHERE
    A.STRING <> B.STRING OR (A.STRING IS NULL AND B.STRING IS NOT NULL) OR (A.STRING IS NOT NULL AND B.STRING IS NULL) OR 
    A.DT <> B.DT OR (A.DT IS NULL AND B.DT IS NOT NULL) OR (A.DT IS NOT NULL AND B.DT IS NULL) OR 
    A.B <> B.B OR (A.B IS NULL AND B.B IS NOT NULL) OR (A.B IS NOT NULL AND B.B IS NULL) OR 
    A.NUM <> B.NUM OR (A.NUM IS NULL AND B.NUM IS NOT NULL) OR (A.NUM IS NOT NULL AND B.NUM IS NULL) 

คำถามของฉันคือ:

มีวิธีที่สง่างามกว่านี้ในการทดสอบค่าที่เปลี่ยนแปลงไปทั่วทั้งสองตารางที่ตารางใดอันหนึ่งอาจเป็นโมฆะ?

โซลูชันต้องทำงานอย่างสม่ำเสมอในประเภทข้อมูล

นี่คือรหัสสำหรับตั้งค่าตารางทดสอบ ...

CREATE TABLE A
(
    ID INT IDENTITY(1,1) NOT NULL,
    STRING VARCHAR(20) NULL,
    DT DATETIME NULL,
    B BIT NULL,
    NUM INT NULL
)

CREATE TABLE B
(
    ID INT IDENTITY(1,1) NOT NULL,
    STRING VARCHAR(20) NULL,
    DT DATETIME NULL,
    B BIT NULL,
    NUM INT NULL
)


INSERT INTO A (STRING, DT, B, NUM) VALUES ('TEST', '2012-03-16 16:39:04.893', 0, 23)
INSERT INTO A (STRING, DT, B, NUM) VALUES (NULL, '2012-03-16 16:39:04.893', 0, 23)
INSERT INTO A (STRING, DT, B, NUM) VALUES ('TEST', NULL, 0, 23)
INSERT INTO A (STRING, DT, B, NUM) VALUES ('TEST', '2012-03-16 16:39:04.893', NULL, 23)
INSERT INTO A (STRING, DT, B, NUM) VALUES ('TEST', '2012-03-16 16:39:04.893', 0, NULL)
INSERT INTO A (STRING, DT, B, NUM) VALUES ('TEST', '2012-03-16 16:39:04.893', 0, 23)
INSERT INTO A (STRING, DT, B, NUM) VALUES ('TEST', '2012-03-16 16:39:04.893', 0, 23)
INSERT INTO A (STRING, DT, B, NUM) VALUES ('TEST', '2012-03-16 16:39:04.893', 0, 23)
INSERT INTO A (STRING, DT, B, NUM) VALUES ('TEST', '2012-03-16 16:39:04.893', 0, 23)
INSERT INTO A (STRING, DT, B, NUM) VALUES ('TEST', '2012-03-16 16:39:04.893', 0, 23)


INSERT INTO B (STRING, DT, B, NUM) VALUES ('TEST', '2012-03-16 16:39:04.893', 0, 23)
INSERT INTO B (STRING, DT, B, NUM) VALUES ('TEST', '2012-03-16 16:39:04.893', 0, 23)
INSERT INTO B (STRING, DT, B, NUM) VALUES ('TEST', '2012-03-16 16:39:04.893', 0, 23)
INSERT INTO B (STRING, DT, B, NUM) VALUES ('TEST', '2012-03-16 16:39:04.893', 0, 23)
INSERT INTO B (STRING, DT, B, NUM) VALUES ('TEST', '2012-03-16 16:39:04.893', 0, 23)
INSERT INTO B (STRING, DT, B, NUM) VALUES ('STAGE', '2012-03-16 16:39:04.893', 0, 23)
INSERT INTO B (STRING, DT, B, NUM) VALUES ('TEST', '2555-11-11 00:00:00.000', 0, 23)
INSERT INTO B (STRING, DT, B, NUM) VALUES ('TEST', '2012-03-16 16:39:04.893', 0, 23)
INSERT INTO B (STRING, DT, B, NUM) VALUES ('TEST', '2012-03-16 16:39:04.893', 0, 23)
INSERT INTO B (STRING, DT, B, NUM) VALUES ('TEST', '2012-03-16 16:39:04.893', 0, 999)

คำตอบ:


24

คุณสามารถใช้วิธีการนี้ได้จากบทความของPaul Whiteที่ไม่มีเอกสารเกี่ยวกับแผนการสืบค้น: Equality Equarisons

SELECT * 
FROM   A 
       INNER JOIN B 
         ON A.ID = B.ID 
            AND EXISTS(SELECT A.* 
                       EXCEPT 
                       SELECT B.*) 

นี่ยอดเยี่ยมมากฉันทดสอบมันออกมาและดูเหมือนว่าจะทำงานได้อย่างที่ฉันต้องการ ขอบคุณมาก ๆ.
GWR

1
คุณลองตีฉันที! แม้ว่าฉันจะต้องสารภาพว่าคุณสอนฉันวิธีนี้ มันสวยงามมาก. :)
ErikE

มาร์ตินคุณใช้วิธีนี้ในการเปรียบเทียบตารางที่มีขนาดใหญ่เกินไปสำหรับการเปรียบเทียบข้อมูลหรือไม่
AK

3

SQL มาตรฐานรองรับ SQL Server 2005 และดีกว่า:

WITH A_MINUS_B 
     AS
     (
      SELECT * 
        FROM A
      EXCEPT 
      SELECT *
        FROM B
     )
SELECT * 
  FROM A_MINUS_B AS T 
       JOIN B ON T.ID = B.ID;

0

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

--c1 = Length of Column1
--c2 = Length of Column2

ISNULL(NULLIF(CONVERT(VARBINARY(cl), LTRIM(RTRIM(Column1))),
              CONVERT(VARBINARY(c2), LTRIM(RTRIM(Column2)))),
       NULLIF(CONVERT(VARBINARY(c2), LTRIM(RTRIM(Column2))),
              CONVERT(VARBINARY(c1), LTRIM(RTRIM(Column1))))) IS NULL

คุณสามารถเปลี่ยนจุดสิ้นสุดของนิพจน์เป็นIS NOT NULLเพื่อตรวจสอบสภาพที่ไม่เท่ากัน

หวังว่าความช่วยเหลือนี้

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