Как найти строки, которые являются дубликатами по ключу, но не дублируются во всех столбцах? - PullRequest
14 голосов
/ 22 ноября 2010

Я работаю с таблицей, которая является выдержкой из набора других таблиц.Все строки таблицы извлечения должны быть уникальными в соответствии с ключами D1, D2 и D3.Они не.Похоже, что более ранний разработчик пытался решить эту проблему, используя SELECT DISTINCT во всех столбцах, запрашиваемых из этой таблицы.Это будет работать, но только если каждая строка, которая является дубликатом (D1, D2, D3), также является дубликатом для неключевых столбцов (игнорируя столбец IDENTITY, добавленный в таблицу извлечения).

Другими словами, данные строки выглядят следующим образом:

D1  D2  D3  C4  C5  C6
=== === === === === ===
A   B   C   X1  X2  X3
A   B   C   X1  X2  X3

, тогда

SELECT DISTINCT D1, D2, D3, C4, C5, C6
FROM BAD_TABLE

будет «работать», так как нет разницы между строками, на которых дублируются (D1, D2,D3).Но если таблица содержит

D1  D2  D3  C4  C5  C6
=== === === === === ===
A   B   C   X1  X2  X3
A   B   C   X1  X2  X4

, тогда SELECT DISTINCT вернет две строки для ключа (A, B, C).Кроме того, мы должны решить, какой из X3 или X4 был «правильным» значением.

Я знаю, как найти дубликаты (D1, D2, D3).Я даже знаю, как найти дубликаты во всех столбцах (кроме столбца IDENTITY):

;
WITH DUPLICATES(D1,D2,D3) AS
(
    SELECT D1, D2, D3
    FROM SOURCE
    GROUP BY D1, D2, D3
    HAVING COUNT(*)>1
)
SELECT S.D1, S.D2, S.D3, S.C4, S.C5, S.C6
FROM SOURCE S
INNER JOIN DUPLICATES D
    ON S.D1 = D.D1 AND S.D2 = D.D2 AND S.D3 = D.D3
ORDER BY S.D1, S.D2, S.D3, S.C4, S.C5, S.C6

Вопрос в том, как найти подмножество вышеуказанного набора результатов, которые являются дубликатами (D1,D2, D3), но дубликаты не включены (D1, D2, D3, C4, C5, C6)?

Ответы [ 5 ]

4 голосов
/ 18 февраля 2011

Вы можете сделать это, присоединившись к таблице, заявив, что все D равны, и хотя бы один из C не равен.

CREATE TABLE #Source (
    D1 VARCHAR(2),
    D2 VARCHAR(2),
    D3 VARCHAR(2),
    C4 VARCHAR(2),
    C5 VARCHAR(2),
    C6 VARCHAR(2) );

INSERT INTO #Source VALUES ('A', 'B', 'C', 'X1', 'X2', 'X3');
INSERT INTO #Source VALUES ('A', 'B', 'C', 'X1', 'X2', 'X4');
INSERT INTO #Source VALUES ('A', 'B', 'D', 'X1', 'X2', 'X3');
INSERT INTO #Source VALUES ('A', 'B', 'D', 'X1', 'X2', 'X3');

SELECT S1.D1, S1.D2, S1.D3, S1.C4 C4_1, S2.C4 C4_2, S1.C5 C5_1, S2.C5 C5_2, S1.C6 C6_1, S2.C6 C6_2
FROM
    #Source S1
            INNER JOIN
    #Source S2
            ON
        (       S1.D1 = S2.D1 
            AND S1.D2 = S2.D2
            AND S1.D3 = S2.D3
            AND (   S1.C4 <> S2.C4
                 OR S1.C5 <> S2.C5
                 OR S1.C6 <> S2.C6
                 )
        );

DROP TABLE #Source;

Дает следующие результаты:

D1   D2   D3   C4_1 C4_2 C5_1 C5_2 C6_1 C6_2
---- ---- ---- ---- ---- ---- ---- ---- ----
A    B    C    X1   X1   X2   X2   X4   X3
A    B    C    X1   X1   X2   X2   X3   X4

Также обратите внимание, что это совместимо с MS SQL 2000, как вы позже указали, требуется в Как преобразовать запрос SQL с помощью выражений общих таблиц в один без (для SQL Server 2000) .

3 голосов
/ 22 ноября 2010

У меня еще не было возможности попробовать ответ Конрада, но я придумал один из них. Это скорее "дух" момент.

Итак, если вы хотите найти все строки в наборе A, кроме тех, которые находятся в наборе B, вы используете оператор EXCEPT:

; 
WITH KEYDUPLICATES(D1,D2,D3) AS 
( 
    SELECT D1, D2, D3 
    FROM SOURCE 
    GROUP BY D1, D2, D3 
    HAVING COUNT(*)>1 
),
KEYDUPLICATEROWS AS
( 
    SELECT S.D1, S.D2, S.D3, S.C4, S.C5, S.C6 
    FROM SOURCE S 
    INNER JOIN KEYDUPLICATES D 
        ON S.D1 = D.D1 AND S.D2 = D.D2 AND S.D3 = D.D3 
),
FULLDUPLICATES AS
(
    SELECT S.D1, S.D2, S.D3, S.C4, S.C5, S.C6 
    FROM SOURCE S
    GROUP BY S.D1, S.D2, S.D3, S.C4, S.C5, S.C6 
    HAVING COUNT(*)>1
)
SELECT KR.D1, KR.D2, KR.D3, KR.C4, KR.C5, KR.C6
FROM KEYDUPLICATEROWS AS KR
EXCEPT
SELECT FD.D1, FD.D2, FD.D3, FD.C4, FD.C5, FD.C6
FROM FULLDUPLICATES AS FD
ORDER BY D1, D2, D3, C4, C5, C6

Это, кажется, показывает мне 1500 строк, которые являются дубликатами (D1, D2, D3), но являются дубликатами только для поднабора (D1, D2, D3, C4, C5, C6). Фактически, кажется, что они являются дубликатами (D1, D2, D3, C4, C5).

Как подтвердить, что это будет предметом другого вопроса.

2 голосов
/ 01 февраля 2013

Я знаю, что это старый вопрос, но я видел активность по этому вопросу, и техника, которую я всегда использую для них, не представлена ​​здесь в качестве ответа, и это действительно довольно просто, поэтому я решил, что представлю это.

SELECT D1, D2, D3, MIN(C4), MAX(C4), MIN(C5), MAX(C5), MIN(C6), MAX(C6)
FROM BAD_TABLE
GROUP BY D1, D2, D3
HAVING MIN(C4) <> MAX(C4)
    OR MIN(C5) <> MAX(C5)
    OR MIN(C6) <> MAX(C6)

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

Чтобы увидеть все строки в этом, вам нужно присоединиться к BAD_TABLE в качестве примера в исходном вопросе.

2 голосов
/ 23 ноября 2010

Это может иметь ограничения по производительности, но гораздо проще понять:

SELECT D1, D2, D3
FROM TEST_KEY TK
WHERE (D1, D2, D3) IN
        (SELECT D1, D2, D3 FROM TEST_KEY TK2
         GROUP BY D1, D2, D3
         HAVING COUNT(*) > 1)
  AND (D1, D2, D3) IN
        (SELECT D1, D2, D3 FROM TEST_KEY TK2
         GROUP BY D1, D2, D3, C4, C5, C6
         HAVING COUNT(*) < 2)

Невозможно протестировать на SQL-сервере, надеюсь, что синтаксис хороший.

Опять же, не уверен, чтоу вас есть аналитические функции в SQL-Server, но эта работает в Oracle и может быть быстрее:

WITH BAD_DUP AS (
SELECT TK.*,
       COUNT(1) OVER (PARTITION BY D1, D2, D3, C4, C5, C6 ORDER BY D1) FULL_DUP,
       COUNT(1) OVER (PARTITION BY D1, D2, D3 ORDER BY D1) KEY_DUP
FROM TEST_KEY TK)
SELECT * FROM BAD_DUP
WHERE FULL_DUP < KEY_DUP

Хотели бы получить ее в одном запросе ....

2 голосов
/ 22 ноября 2010

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

WITH DUPLICATEKEY(D1,D2,D3) AS
(
    SELECT D1, D2, D3
    FROM SOURCE
    GROUP BY D1, D2, D3
    HAVING COUNT(*)>1
)
WITH NODUPES(D1,D2,D3,C4,C5,C6) AS
(
SELECT 
S.D1, S.D2, S.D3, S.C4, S.C5, S.C6
FROM SOURCE S
GROUP BY
 S.D1, S.D2, S.D3, S.C4, S.C5, S.C6
HAVING COUNT(*)=1
)

SELECT S.D1, S.D2, S.D3, S.C4, S.C5, S.C6
FROM SOURCE S
INNER JOIN DUPLICATEKEY D
    ON S.D1 = D.D1 AND S.D2 = D.D2 AND S.D3 = D.D3

INNER JOIN NODUPES D2
    ON S.D1 = D2.D1 AND S.D2 = D2.D2 AND S.D3 = D2.D3

ORDER BY S.D1, S.D2, S.D3, S.C4, S.C5, S.C6
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...