2010-06-10 9 views
1

J'ai un problème de liste des lignes dupliquées qui incluent des colonnes NULL. Lemme montre mon problème en premier.Comment lister toutes les lignes dupliquées qui peuvent inclure des colonnes 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 

Maintenant, je veux afficher toutes les lignes qui ont d'autres lignes dupliquées avec eux, j'utilise la requête suivante.

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

qui nous donnera le résultat:

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

Maintenant, si je veux énumérer toutes les lignes qui apportent une contribution à la duplication, j'utilise cette requête:

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) 

qui donnera moi le résultat:

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

Comme vous pouvez le voir, toutes les lignes incluent les valeurs NULL sont filtrées. La raison pour laquelle je pensais est que j'utilise le signe égal pour tester la condition (dbo.t.a = duplicate.a ET dbo.t.b = duplicate.b), et que les valeurs NULL ne peuvent pas être comparées utilisent un signe égal. Ainsi, afin d'inclure les lignes qui incluent NULLs en elle dans le dernier résultat, je modifier la requête précitée à

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) 

Et cette requête me donnera la réponse que je voulais:

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

Maintenant, ma question est, comme vous pouvez le voir, cette requête ne comprend que deux colonnes, afin d'inclure des valeurs nulles dans le dernier résultat, vous devez utiliser plusieurs instructions de test de condition dans la requête. À mesure que le nombre de colonnes augmente, les instructions de test de condition dont vous avez besoin dans votre requête augmentent de façon étonnante. Comment puis-je résoudre ce problème?

Merci beaucoup.

Répondre

2

Vous pouvez utiliser la clause OVER à la place:

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

De cette façon, vous pouvez éviter toutes les conditions, il suffit d'ajouter tous vos champs dans la sous-requête et les récupérer dans la principale sélection.

1

Si le problème réside en effet dans le signe égal et NULL valeurs, SET ANSI_NULLS (en le définissant à OFF) devrait/pourrait faire l'affaire.

SET ANSI_NULLS OFF