Сравнение столбцов, которые могут содержать 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

Вы можете использовать этот подход из статьи Пола Уайта « Недокументированные планы запросов: Сравнения равенства».

SELECT * 
FROM   A 
       INNER JOIN B 
         ON A.ID = B.ID 
            AND EXISTS(SELECT A.* 
                       EXCEPT 
                       SELECT B.*) 
Мартин Смит
источник
Это здорово, я проверил это и, похоже, ведет себя именно так, как мне нужно. Огромное спасибо.
GWR
1
Драт это ты побил меня к этому! Хотя должен признаться, ты научил меня этому методу. Это так красиво. :)
ErikE
Мартин, вы используете этот подход для сравнения таблиц, которые слишком велики для Data Compare?
АК
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;
onedaywhen
источник
0

Это глобальный подход, поэтому я не переписывал исправленный запрос из вопроса. Я просто хочу поделиться полезным пунктом, может быть, поможет, так что ниже скрипт будет проверять, равны ли Column1 и Column2, дополнительно использовал Преобразование в 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 для проверки неравных условий.

Надеюсь, это поможет.

Qmaster
источник