Porównywanie kolumn, które mogą zawierać wartości NULLS - czy istnieje bardziej elegancki sposób?


16

Wiem, że nie można porównać wartości z wartością NULL i oczekiwać wyniku bez dodania czegoś takiego jak w poniższym kodzie ...

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) 

Moje pytanie brzmi:

Czy istnieje bardziej elegancki sposób testowania zmienionych wartości w dwóch tabelach, w których każda z nich może być pusta?

Rozwiązanie musi działać jednakowo we wszystkich typach danych.

Oto kod do konfiguracji tabel testowych ...

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)

Odpowiedzi:


24

Możesz użyć tego podejścia z artykułu Paula White'a Nieudokumentowane plany zapytań: porównania równości

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

To świetnie, przetestowałem to i wydaje się zachowywać dokładnie tak, jak tego potrzebuję. Dziękuję bardzo.
GWR,

1
Zrób to, pobiłeś mnie! Chociaż muszę wyznać, że nauczyłeś mnie tej metody. To jest takie piękne. :)
ErikE

Martin, czy używasz tego podejścia do porównywania tabel, które są zbyt duże do porównywania danych?
AK

3

Standardowy SQL, obsługiwany w SQL Server 2005 i nowszych wersjach:

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

Jest to podejście globalne, więc nie przepisałem poprawionego zapytania z pytania. Chcę tylko podzielić się użytecznym punktem, może pomóc, więc poniższy skrypt sprawdzi, czy Kolumna1 i Kolumna2 są równe, Dodatkowo użyłem Konwersji na VARBINARY, aby porównać wielkość liter i możesz ją usunąć, jeśli nie jest to konieczne.

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

Możesz zmienić koniec wyrażenia na, IS NOT NULLaby sprawdzić nierówny warunek.

Mam nadzieję, że to pomoże.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.