2010-05-20 21 views
7

estoy usando SQL Server 2008. Tengo una tabla¿Cómo encontrar valores duplicados en SQL Server

Customers 

customer_number int 

field1 varchar 

field2 varchar 

field3 varchar 

field4 varchar 

... y mucho más columnas, que no tienen importancia para mis consultas.

Columna número_cliente es pk. Estoy tratando de encontrar valores duplicados y algunas diferencias entre ellos.

Por favor, ayúdame a encontrar todas las filas que tienen el mismo

1) campo1, campo2, field3, campo4

2) sólo 3 columnas son iguales y uno de ellos no es (excepto filas de la lista 1)

3) sólo 2 columnas iguales y dos de ellos no son (excepto las filas de la lista 1 y la lista 2)

Al final, tendré 3 tablas con estos resultados y groupId adicional, que será igual para un grupo similar (por ejemplo, para 3 columnas iguales, las filas que tienen 3 mismas columnas iguales serán un grupo separado)

Gracias.

Respuesta

4

Lo más fácil probablemente sería escribir un procedimiento almacenado para iterar sobre cada grupo de clientes con duplicados e insertar los correspondientes por número de grupo, respectivamente.

Sin embargo, he pensado al respecto y probablemente puedas hacer esto con una subconsulta. Espero no haberlo hecho más complicado de lo que debería, pero esto debería darte lo que estás buscando para la primera tabla de duplicados (los cuatro campos). Tenga en cuenta que esto no se ha probado, por lo que podría necesitar un pequeño ajuste.

Básicamente, obtiene cada grupo de campos donde hay duplicados, un número de grupo para cada uno, luego obtiene todos los clientes con esos campos y asigna el mismo número de grupo.

INSERT INTO FourFieldsDuplicates(group_no, customer_no) 
SELECT Groups.group_no, custs.customer_no 
FROM (SELECT ROW_NUMBER() OVER(ORDER BY c.field1) AS group_no, 
      c.field1, c.field2, c.field3, c.field4 
     FROM Customers c 
     GROUP BY c.field1, c.field2, c.field3, c.field4 
     HAVING COUNT(*) > 1) Groups 
INNER JOIN Customers custs ON custs.field1 = Groups.field1 
          AND custs.field2 = Groups.field2 
          AND custs.field3 = Groups.field3 
          AND custs.field4 = Groups.field4 

Los otros son un poco más complicados, sin embargo, ya que tendrá que ampliar las posibilidades. Los grupos de tres campos serían entonces:

INSERT INTO ThreeFieldsDuplicates(group_no, customer_no) 
SELECT Groups.group_no, custs.customer_no 
FROM (SELECT ROW_NUMBER() OVER(ORDER BY GroupsInner.field1) AS group_no, 
      GroupsInner.field1, GroupsInner.field2, 
      GroupsInner.field3, GroupsInner.field4 
     FROM (SELECT c.field1, c.field2, c.field3, NULL AS field4 
      FROM Customers c 
      WHERE NOT EXISTS(SELECT d.customer_no 
         FROM FourFieldsDuplicates d 
         WHERE d.customer_no = c.customer_no) 
      GROUP BY c.field1, c.field2, c.field3 
      UNION ALL 
      SELECT c.field1, c.field2, NULL AS field3, c.field4 
      FROM Customers c 
      WHERE NOT EXISTS(SELECT d.customer_no 
          FROM FourFieldsDuplicates d 
          WHERE d.customer_no = c.customer_no) 
      GROUP BY c.field1, c.field2, c.field4 
      UNION ALL 
      SELECT c.field1, NULL AS field2, c.field3, c.field4 
      FROM Customers c 
      WHERE NOT EXISTS(SELECT d.customer_no 
          FROM FourFieldsDuplicates d 
          WHERE d.customer_no = c.customer_no) 
      GROUP BY c.field1, c.field3, c.field4 
      UNION ALL 
      SELECT NULL AS field1, c.field2, c.field3, c.field4 
      FROM Customers c 
      WHERE NOT EXISTS(SELECT d.customer_no 
          FROM FourFieldsDuplicates d 
          WHERE d.customer_no = c.customer_no) 
      GROUP BY c.field2, c.field3, c.field4) GroupsInner 
     GROUP BY GroupsInner.field1, GroupsInner.field2, 
       GroupsInner.field3, GroupsInner.field4 
     HAVING COUNT(*) > 1) Groups 
INNER JOIN Customers custs ON (Groups.field1 IS NULL OR custs.field1 = Groups.field1) 
          AND (Groups.field2 IS NULL OR custs.field2 = Groups.field2) 
          AND (Groups.field3 IS NULL OR custs.field3 = Groups.field3) 
          AND (Groups.field4 IS NULL OR custs.field4 = Groups.field4) 

Esperamos que esto produce los resultados correctos y yo a dejar el último como un ejercicio. :-D

+0

@Ic ¿Es correcto escribir "c.field1 como group_no"? group_no es int y field1 es varchar. ¿Tal vez debería usar algunas tablas temporales? – hgulyan

+0

@hgulyan En realidad es ROW_NUMBER() como group_no. –

+0

@Ic, Sí, ya hice un cambio en eso. Todavía estoy tratando de ejecutar el primer script ... – hgulyan

2

No estoy seguro de si necesita una verificación de igualdad en diferentes campos (como field1 = field2).
De lo contrario, esto podría ser suficiente.

Editar

dude en ajustar el testdata que nos proporcione entradas que dan una salida mal de acuerdo a sus especificaciones.

Los datos de prueba

DECLARE @Customers TABLE (
    customer_number INTEGER IDENTITY(1, 1) 
    , field1 INTEGER 
    , field2 INTEGER 
    , field3 INTEGER 
    , field4 INTEGER) 

INSERT INTO @Customers 
      SELECT 1, 1, 1, 1 
UNION ALL SELECT 1, 1, 1, 1 
UNION ALL SELECT 1, 1, 1, NULL 
UNION ALL SELECT 1, 1, 1, 2 
UNION ALL SELECT 1, 1, 1, 3 
UNION ALL SELECT 2, 1, 1, 1 

todos iguales

SELECT ROW_NUMBER() OVER (ORDER BY c1.customer_number) 
     , c1.field1 
     , c1.field2 
     , c1.field3 
     , c1.field4 
FROM @Customers c1 
     INNER JOIN @Customers c2 ON c2.customer_number > c1.customer_number 
            AND ISNULL(c2.field1, 0) = ISNULL(c1.field1, 0) 
            AND ISNULL(c2.field2, 0) = ISNULL(c1.field2, 0) 
            AND ISNULL(c2.field3, 0) = ISNULL(c1.field3, 0) 
            AND ISNULL(c2.field4, 0) = ISNULL(c1.field4, 0) 

un campo diferente

SELECT ROW_NUMBER() OVER (ORDER BY field1, field2, field3, field4) 
     , field1 
     , field2 
     , field3 
     , field4 
FROM (
      SELECT DISTINCT c1.field1 
        , c1.field2 
        , c1.field3 
        , field4 = NULL 
      FROM @Customers c1 
        INNER JOIN @Customers c2 ON c2.customer_number > c1.customer_number 
              AND c2.field1 = c1.field1 
              AND c2.field2 = c1.field2 
              AND c2.field3 = c1.field3 
              AND ISNULL(c2.field4, 0) <> ISNULL(c1.field4, 0) 
      UNION ALL 
      SELECT DISTINCT c1.field1 
        , c1.field2 
        , NULL 
        , c1.field4 
      FROM @Customers c1 
        INNER JOIN @Customers c2 ON c2.customer_number > c1.customer_number 
              AND c2.field1 = c1.field1 
              AND c2.field2 = c1.field2 
              AND ISNULL(c2.field3, 0) <> ISNULL(c1.field3, 0) 
              AND c2.field4 = c1.field4 
      UNION ALL 
      SELECT DISTINCT c1.field1 
        , NULL 
        , c1.field3 
        , c1.field4 
      FROM @Customers c1 
        INNER JOIN @Customers c2 ON c2.customer_number > c1.customer_number 
              AND c2.field1 = c1.field1 
              AND ISNULL(c2.field2, 0) <> ISNULL(c1.field2, 0) 
              AND c2.field3 = c1.field3 
              AND c2.field4 = c1.field4 
      UNION ALL 
      SELECT DISTINCT NULL 
        , c1.field2 
        , c1.field3 
        , c1.field4 
      FROM @Customers c1 
        INNER JOIN @Customers c2 ON c2.customer_number > c1.customer_number 
              AND ISNULL(c2.field1, 0) <> ISNULL(c1.field1, 0) 
              AND c2.field2 = c1.field2 
              AND c2.field3 = c1.field3 
              AND c2.field4 = c1.field4 
    ) c 
+0

¿Funcionará INNER JOIN si hay valores nulos? – hgulyan

+0

Esto es bueno y en realidad lo que tenía al principio, pero el problema es volver a salir de los grupos para insertarlos en la nueva tabla ... –

+0

¿No hay alguna forma de agregar un número a esta consulta? – hgulyan

52

Aquí hay una mano q uery para encontrar duplicados en una tabla. Suponga que desea encontrar todas las direcciones de correo electrónico en una mesa que existen más de una vez:

SELECT email, COUNT(email) AS NumOccurrences 
FROM users 
GROUP BY email 
HAVING (COUNT(email) > 1) 

También puede utilizar esta técnica para encontrar las filas que se producen exactamente una vez:

SELECT email 
FROM users 
GROUP BY email 
HAVING (COUNT(email) = 1) 
+2

Respuesta simple y hermosa. Pude haber pensado en esto, pero le pregunté a google porque no quería pensar. No estaba decepcionado. Esta es la verdadera respuesta. –

+0

Bien jugado señor. – Induster

+0

Simple y funcionó muy bien. ¡Muchas gracias! – Migs

0

usted puede escribir simplemente algo al igual que contar las entradas duplicadas, creo que está funcionando:

use *DATABASE_NAME* 
go 
SELECT  *YOUR_FIELD*, COUNT(*) AS dupes 
FROM   *YOUR_TABLE_NAME* 
GROUP BY *YOUR_FIELD* 
HAVING  (COUNT(*) > 1) 

Disfrute

+0

Duplicado de respuesta anterior –

0

Hay una forma limpia de hacer esto con CUBE(), que agruparse por todas las posibles combinaciones de columnas

SELECT 
    field1,field2,field3,field4 
,duplicate_row_count = COUNT(*) 
,grp_id = GROUPING_ID(field1,field2,field3,field4) 
INTO #duplicate_rows 
FROM table_name 
GROUP BY CUBE(field1,field2,field3,field4) 
HAVING COUNT(*) > 1 
    AND GROUPING_ID(field1,field2,field3,field4) IN (0,1,2,4,8,3,5,6,9,10,12) 

Los números (0,1,2,4,8,3,5,6, 9,10,12) son solo las máscaras de bits (0000,0001,0010,0100, ..., 1010,1100) de los conjuntos de agrupación que nos interesan, aquellos con 4, 3 o 2 coincidencias.

luego unirse a esta vuelta a la mesa de originales utilizando una técnica que trata los nulos en #duplicate_rows como comodines

SELECT a.* 
FROM table_name a 
INNER JOIN #duplicate_rows b 
    ON NULLIF(b.field1,a.field1) IS NULL 
    AND NULLIF(b.field2,a.field2) IS NULL 
    AND NULLIF(b.field3,a.field3) IS NULL 
    AND NULLIF(b.field4,a.field4) IS NULL 
--WHERE grp_id IN (0)    --Use this for 4 matches 
--WHERE grp_id IN (1,2,4,8)  --Use this for 3 matches 
--WHERE grp_id IN (3,5,6,9,10,12) --Use this for 2 matches 
Cuestiones relacionadas