Как вывести список всех дублированных строк, которые могут содержать столбцы NULL? - PullRequest
1 голос
/ 10 июня 2010

У меня проблема с перечислением дублированных строк, содержащих столбцы NULL. Позвольте мне сначала показать мою проблему.

USE [tempdb];
GO

IF OBJECT_ID(N'dbo.t') IS NOT NULL
BEGIN
    DROP TABLE dbo.t
END
GO

CREATE TABLE dbo.t
(
    a NVARCHAR(8),
    b NVARCHAR(8)
);
GO

INSERT t VALUES ('a', 'b');
INSERT t VALUES ('a', 'b');
INSERT t VALUES ('a', 'b');
INSERT t VALUES ('c', 'd');
INSERT t VALUES ('c', 'd');
INSERT t VALUES ('c', 'd');
INSERT t VALUES ('c', 'd');
INSERT t VALUES ('e', NULL);
INSERT t VALUES (NULL, NULL);
INSERT t VALUES (NULL, NULL);
INSERT t VALUES (NULL, NULL);
INSERT t VALUES (NULL, NULL);
GO

Теперь я хочу показать все строки, дублирующие другие строки, я использую следующий запрос.

SELECT a, b
FROM   dbo.t
GROUP
    BY a, b
HAVING count(*) > 1

, который даст нам результат:

a        b
-------- --------
NULL     NULL
a        b
c        d

Теперь, если я хочу перечислить все строки, которые вносят вклад в дублирование, я использую этот запрос:

WITH
duplicate (a, b) AS
(
    SELECT a, b
    FROM   dbo.t
    GROUP
        BY a, b
    HAVING count(*) > 1
)
SELECT dbo.t.a, dbo.t.b
FROM   dbo.t
       INNER JOIN duplicate
           ON (dbo.t.a = duplicate.a
           AND dbo.t.b = duplicate.b)

Что даст мне результат:

a        b
-------- --------
a        b
a        b
a        b
c        d
c        d
c        d
c        d

Как видите, все строки, содержащие NULL, фильтруются. Причина, по которой я подумал, заключается в том, что я использую знак равенства для проверки условия (dbo.t.a = duplicate.a AND dbo.t.b = duplicate.b), и значения NULL нельзя сравнивать с использованием знака равенства. Итак, чтобы включить строки с пустыми значениями в последнем результате, я изменил вышеупомянутый запрос на

WITH
duplicate (a, b) AS
(
    SELECT a, b
    FROM   dbo.t
    GROUP
        BY a, b
    HAVING count(*) > 1
)
SELECT dbo.t.a, dbo.t.b
FROM   dbo.t
       INNER JOIN duplicate
           ON (dbo.t.a = duplicate.a
               AND dbo.t.b = duplicate.b)
           OR
           (dbo.t.a IS NULL
               AND duplicate.a IS NULL
               AND dbo.t.b = duplicate.b)
           OR
           (dbo.t.b IS NULL
               AND duplicate.b IS NULL
               AND dbo.t.a = duplicate.a)
           OR
           (dbo.t.a IS NULL
               AND duplicate.a IS NULL
               AND dbo.t.b IS NULL
               AND duplicate.b IS NULL)

И этот запрос даст мне ответ, как я хотел:

a        b
-------- --------
NULL     NULL
NULL     NULL
NULL     NULL
NULL     NULL
a        b
a        b
a        b
c        d
c        d
c        d
c        d

Теперь мой вопрос, как вы можете видеть, состоит в том, что этот запрос включает в себя только два столбца. Чтобы включить NULL в последний результат, вам нужно использовать в запросе много операторов проверки условий. По мере того как число столбцов увеличивается, операторы проверки условий, которые вам нужны в вашем запросе, удивительно увеличиваются. Как я могу решить эту проблему?

Большое спасибо.

Ответы [ 2 ]

2 голосов
/ 10 июня 2010

Вместо этого вы можете использовать предложение OVER:

select a, b from
(
    select a, b,
        COUNT(*) over (partition by a, b) Cnt
    from dbo.t
) TheResult
where Cnt > 1

Таким образом, вы можете избежать всех условий, просто добавьте все свои поля в подзапрос и извлеките их в главном выборе.

1 голос
/ 10 июня 2010

Если проблема действительно заключается в знаке равенства и значениях NULL, SET ANSI_NULLS (установив его в OFF) должны / могли бы добиться цели.

SET ANSI_NULLS OFF
...