2010-11-22 29 views
14

Estoy trabajando con una tabla que es un extracto de un conjunto de otras tablas. Todas las filas de la tabla de extracción deben ser únicas según las claves D1, D2 y D3. Ellos no son. Parece que un desarrollador anterior intentó resolver este problema utilizando un SELECT DISTINCT en todas las columnas consultadas desde esta tabla. Esto funcionará, pero solo si cada fila que es un duplicado en (D1, D2, D3) también es un duplicado en las columnas sin clave (ignorando la columna de IDENTIDAD que se agregó a la tabla de extracción).Cómo encontrar filas que son duplicados por una clave pero no duplicados en todas las columnas?

En otras palabras, las filas de la forma siguiente:

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

continuación

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

"funcionará", ya que no hay diferencia entre las filas, que están duplicados en (D1, D2, D3) Pero si la tabla contiene

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

luego SELECT DISTINCT devolvería dos filas para la llave (A, B, C). Además, tendríamos que decidir cuál de X3 o X4 era el valor "correcto".

Sé cómo encontrar los duplicados en (D1, D2, D3). Incluso sé cómo encontrar los duplicados en todas las columnas (excepto la columna de identidad):

; 
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 

La pregunta es, ¿cómo puedo encontrar el subconjunto del conjunto de resultados por encima del cual están duplicados en (D1, D2, D3), ​​pero no duplicados en (D1, D2, D3, C4, C5, C6)?

+1

¿Puedes dar una versión TL; DR? – WhatIsOpenID

+4

@WhatIs: podría si supiera lo que significa "TL: DR" :-) –

+2

¿Qué pasa con la votación negativa de la pregunta? ¿Cómo puedo mejorar la pregunta si no dice cuál es el problema? –

Respuesta

2

¿Por qué no creas otra expresión de tabla para abarcar más campos y unirte a esa?

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

Estás muy cerca, pero quería ver cuáles eran "duplicados clave" pero ** no ** "duplicados completos". Lamento no saber el término correcto para estos. –

+0

@John Derecho, por supuesto. Actualicé la respuesta En el segundo CTE que tiene Count (*) = 1 –

3

No he tenido la oportunidad de probar la respuesta de Conrad, pero se me ocurrió una. Es un momento "duh".

lo tanto, si usted quiere encontrar todas las filas de conjunto A excepción de los que están en el conjunto B, se utiliza el operador 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 

Esto parece estar mostrándome 1500 filas que son duplicados across (D1, D2, D3), pero que solo son duplicados en un subconjunto de (D1, D2, D3, C4, C5, C6). De hecho, parece que son duplicados (D1, D2, D3, C4, C5).

Cómo confirmar eso será el tema de otra pregunta.

+0

Una corrección menor al código anterior: La última línea "ORDER BY D1, D2, D3, D4, C5, C6" debe decir "ORDER BY D1, D2" , D3, C4, C5, C6 " –

+0

Gracias por la captura. –

+0

+1 Buen uso de excepto –

2

esto habría limitaciones de rendimiento, pero es mucho más fácil de entender:

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) 

No se ha podido probar en SQL-Server, espero que la sintaxis es buena.

Una vez más, no está seguro de si tiene funciones analíticas en SQL Server, pero éste funciona en Oracle y puede ser que sea más rápido:

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 

gustaría llegar abajo a una sola consulta ....

+0

Gracias. Voy a investigar eso. Sí, tenemos las funciones analíticas (COUNT, ROW_COUNT, etc.). –

+0

Lo importante es poder particionar por las diferentes agrupaciones, de modo que solo tiene que hacer una pasada, supongo que hay una forma de hacerlo, simplemente no sé si la sintaxis es estándar. – orbfish

+0

Su primera consulta no funcionará en SQL Server. – ErikE

4

Puede hacerlo uniéndose a la mesa en sí mismo, diciendo que las D's son todas iguales y que al menos una de las C no es igual.

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; 

da los siguientes resultados:

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 

También tenga en cuenta que esto es compatible con MS SQL 2000 a medida que más adelante se indica que se requiere en How to Convert a SQL Query using Common Table Expressions to One Without (for SQL Server 2000).

+0

mi tarea para la hora del almuerzo es probar esto. Todavía estoy trabajando con estas mismas consultas, aunque se me ha permitido realizar pruebas unitarias mientras las arreglo (y, de hecho, TDD). –

2

Sé que esta es una pregunta antigua, pero vi actividad en la pregunta y la técnica que siempre uso para estos no se presenta aquí como una respuesta, y es realmente bastante simple, así que pensé en presentarla.

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) 

Esto mostrará todas las claves de duplicados de clave, pero con diferencias en los no-llaves, con la gama de diferencias duplicados.

Para ver todas las filas dentro de eso, deberá volver a unir BAD_TABLE como su ejemplo en la pregunta original.

Cuestiones relacionadas