Переименовать дублирующиеся строки - PullRequest
6 голосов
/ 03 марта 2011

Вот упрощенный пример моей проблемы. У меня есть таблица, где есть столбец «Имя» с повторяющимися записями:

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

Выполнение GROUP BY как SELECT Name, COUNT(*) AS [Count] FROM Table GROUP BY Name приводит к следующему:

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

Меня интересуют только дубликаты, поэтому я добавлю предложение HAVING, SELECT Name, COUNT(*) AS [Count] FROM Table GROUP BY Name HAVING COUNT(*) > 1:

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

Пока банально, но теперь все становится сложнее: мне нужен запрос, чтобы получить все дублирующиеся записи, но с хорошим индикатором увеличения, добавленным в столбец Имя. Результат должен выглядеть примерно так:

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)

Примечание: строка 4 с надписью «BBB» исключена, а первый дубликат сохраняет исходное имя.

Использование оператора EXISTS дает мне все записи, которые мне нужны, но как мне создать новое значение имени?

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

Мне нужно создать инструкцию UPDATE, которая исправит все дубликаты, т.е. изменит имя в соответствии с этим шаблоном.

Обновление: Разобрался сейчас. Это был пункт PARTITION BY , который я пропустил.

Ответы [ 5 ]

12 голосов
/ 03 марта 2011
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

Если вы хотите оператор обновления, вы можете использовать почти такую ​​же структуру:

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
5 голосов
/ 03 марта 2011

Просто обновите подзапрос напрямую:

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
1 голос
/ 03 марта 2011
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
)

Это даст вам то, что вы изначально просили. Для заявления об обновлении, вы хотите сделать некоторое время и обновить топ 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 голосов
/ 07 марта 2011

Вот еще более простая инструкция UPDATE:

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 голосов
/ 03 марта 2011

Нет необходимости делать UPDATE вообще. Следующее создаст таблицу для INSERT по желанию

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
...