2011-03-03 18 views
5

Aquí hay un ejemplo simplificado de mi problema. Tengo una tabla donde hay una columna "Nombre" con entradas duplicadas:Renombrar filas duplicadas

ID Name 
--- ---- 
1 AAA 
2 AAA 
3 AAA 
4 BBB 
5 CCC 
6 CCC 
7 DDD 
8 DDD 
9 DDD 
10 DDD 

Haciendo un GROUP BY como SELECT Name, COUNT(*) AS [Count] FROM Table GROUP BY Name resultados en este:

Name Count 
---- ----- 
AAA 3 
BBB 1 
CCC 2 
DDD 4 

Sólo estoy preocupado por los duplicados, así que 'll añadir una cláusula HAVING, SELECT Name, COUNT(*) AS [Count] FROM Table GROUP BY Name HAVING COUNT(*) > 1:

Name Count 
---- ----- 
AAA 3 
CCC 2 
DDD 4 

Trivial hasta el momento, pero ahora las cosas se complican: necesito una consulta para conseguirme todos los registros duplicados, pero con un buen incrementación en dicator agregado a la columna Nombre. El resultado debe ser algo como esto:

ID Name 
--- -------- 
1 AAA 
2 AAA (2) 
3 AAA (3) 
5 CCC 
6 CCC (2) 
7 DDD 
8 DDD (2) 
9 DDD (3) 
10 DDD (4) 

Nota fila 4 se excluye a "BBB", y el primer duplicado mantiene el nombre original.

El uso de una declaración EXISTS me proporciona todos los registros que necesito, pero ¿cómo hago para crear el nuevo valor de Nombre?

SELECT * FROM Table AS T1 
WHERE EXISTS (
    SELECT Name, COUNT(*) AS [Count] 
    FROM Table 
    GROUP BY Name 
    HAVING (COUNT(*) > 1) AND (Name = T1.Name)) 
ORDER BY Name 

Necesito crear una instrucción UPDATE que solucionará todos los duplicados, es decir, cambiar el nombre de acuerdo con este patrón.

Actualización: Lo resolvió ahora. Era la cláusula PARTITION BY que me faltaba.

Respuesta

10
With Dups As 
    (
    Select Id, Name 
     , Row_Number() Over (Partition By Name Order By Id) As Rnk 
    From Table 
    ) 
Select D.Id 
    , D.Name + Case 
       When D.Rnk > 1 Then ' (' + Cast(D.Rnk As varchar(10)) + ')' 
       Else '' 
       End As Name 
From Dups As D 

Si desea una instrucción de actualización se puede utilizar más o menos la misma estructura:

With Dups As 
    (
    Select Id, Name 
     , Row_Number() Over (Partition By Name Order By Id) As Rnk 
    From Table 
    ) 
Update Table 
Set Name = T.Name + Case 
        When D.Rnk > 1 Then ' (' + Cast(D.Rnk As varchar(10)) + ')' 
        Else '' 
        End 
From Table As T 
    Join Dups As D 
     On D.Id = T.Id 
+0

Excavo los CTE. Desearía usarlos más seguido – SQLMason

+0

Su solución se ve más ordenada, pero simplemente no puedo hacer que funcione. –

+0

@Jakob Gade- ¿Está recibiendo un error o no está produciendo los resultados que desea? – Thomas

1
SELECT ROW_NUMBER() OVER(ORDER BY Name) AS RowNum, 
     Name, 
     Name + '(' + ROW_NUMBER() OVER(PARTITION BY Name ORDER BY Name) + ')' concatenatedName 
FROM Table 
WHERE Name IN 
(
    SELECT Name 
    FROM Table 
    GROUP BY Name 
    HAVING COUNT(*) > 1 
) 

Esto le dará lo que originalmente solicitó. Para la instrucción de actualización, tendrá que hacer un tiempo y actualizar la parte superior 1

DECLARE @Pointer VARCHAR(20), @Count INT 

WHILE EXISTS(SELECT Name FROM Table GROUP BY Name HAVING COUNT(1) > 1) 
BEGIN 
    SELECT TOP 1 @Pointer = Name, @Count = COUNT(1) FROM Table GROUP BY Name HAVING COUNT(1) > 1 
    UPDATE TOP (1) TABLE 
    SET Name = Name + '(' + @Count + ')' 
    WHERE Name = @Pointer 
END 
+0

En este momento, voy por memoria. No tengo SSMS en mi computadora portátil. Puede envolver el nombre concatenado en un caso para eliminar el primero (1). – SQLMason

+0

¡Excelente, funciona! Solo tuve que agregar un CAST para el número de fila y el CASE como sugirió. –

+0

Gracias y perdón por haber olvidado el CAST. Si tuviera SSMS instalado aquí, lo habría probado. – SQLMason

0

No es necesario hacer un UPDATE en absoluto. Lo siguiente creará la mesa para INSERT según se desee

SELECT 
    ROW_NUMBER() OVER(ORDER BY tb2.Id) Id, 
    tb2.Name + CASE WHEN COUNT(*) > 1 THEN ' (' + CONVERT(VARCHAR, Count(*)) + ')' ELSE '' END [Name] 
FROM 
    tb tb1, 
    tb tb2 
WHERE 
    tb1.Name = tb2.Name AND 
    tb1.Id <= tb2.Id 
GROUP BY 
    tb2.Name, 
    tb2.Id 
4

Sólo actualizar la sub consulta directamente:

update d 
set Name = Name+'('+cast(r as varchar(10))+')' 
from ( select Name, 
        row_number() over (partition by Name order by Name) as r 
      from [table] 
     ) d 
where r > 1 
+0

¡Eso es aún mejor! – SQLMason

+0

este método también funciona para eliminar los duplicados. –

0

es una instrucción UPDATE aún más simple aquí:

UPDATE 
    tb 
SET 
    [Name] = [Name] + ' (' + CONVERT(VARCHAR, ROW_NUMBER() OVER (PARTITION BY [Name] ORDER BY Id)) + ')' 
WHERE 
    ROW_NUMBER() OVER (PARTITION BY [Name] ORDER BY Id) > 1 
+0

Gracias. Pero lo intenté, pero no puedo hacer que funcione. El mensaje de error dice "Las funciones con ventana solo pueden aparecer en las cláusulas SELECT u ORDER BY". –

Cuestiones relacionadas