Як видалити повторювані рядки в SQL Server?


415

Як я можу видалити повторювані рядки там, де їх немає unique row id?

Мій стіл є

col1  col2 col3 col4 col5 col6 col7
john  1    1    1    1    1    1 
john  1    1    1    1    1    1
sally 2    2    2    2    2    2
sally 2    2    2    2    2    2

Я хочу, щоб після видалення дубліката залишилися такі:

john  1    1    1    1    1    1
sally 2    2    2    2    2    2

Я спробував кілька запитів, але думаю, що вони залежать від наявності ідентифікатора рядка, оскільки я не отримую бажаного результату. Наприклад:

DELETE
FROM table
WHERE col1 IN (
    SELECT id
    FROM table
    GROUP BY id
    HAVING (COUNT(col1) > 1)
)

5
Це не дупа першої ланки. У цьому питанні немає ідентифікатора рядка, а в пов'язаному питанні - ідентифікатор рядка Дуже різні.
Alien Technology

змінити "ВИБРАТИ ідентифікатор ІЗ ТАБЛИЦІ ГРУПИ ІД ГОЛОВОМ", щоб мати агреговану функцію, наприклад MAX / MIN, і вона повинна працювати.
заплутався

Відповіді:


785

Мені подобаються CTE і ROW_NUMBERтому, що обидві комбінації дозволяють нам бачити, які рядки видаляються (або оновлюються), тому просто змініть DELETE FROM CTE...на SELECT * FROM CTE:

WITH CTE AS(
   SELECT [col1], [col2], [col3], [col4], [col5], [col6], [col7],
       RN = ROW_NUMBER()OVER(PARTITION BY col1 ORDER BY col1)
   FROM dbo.Table1
)
DELETE FROM CTE WHERE RN > 1

DEMO (результат різний; я припускаю, що це пов'язано з помилкою друку з вашого боку)

COL1    COL2    COL3    COL4    COL5    COL6    COL7
john    1        1       1       1       1       1
sally   2        2       2       2       2       2

Цей приклад визначає дублікати одним стовпцем col1через PARTITION BY col1. Якщо ви хочете включити кілька стовпців, просто додайте їх до PARTITION BY:

ROW_NUMBER()OVER(PARTITION BY Col1, Col2, ... ORDER BY OrderColumn)

2
Дякую за чудову відповідь. MSFT на відміну тут має дуже складну відповідь: stackoverflow.com/questions/18390574/…
Barka

2
@ omachu23: в цьому випадку це не має значення, хоча я вважаю, що це ефективніше в CTE, ніж за межами ( AND COl1='John'). Зазвичай слід застосувати фільтр у CTE.
Тім Шмелтер

1
@ omachu23: ви можете використовувати будь-який SQL в CTE (крім замовлення), тому якщо ви хочете фільтрувати за Johns : ...FROM dbo.Table1 WHERE Col1='John'. Ось скрипка: sqlfiddle.com/#!6/fae73/744/0
Тім

1
Найпростішим рішенням може бути саме так, set rowcount 1 delete from t1 where col1=1 and col2=1як це можна побачити тут
Зоргарат

15
Ця відповідь видалить лише рядки, що містять дублікати у col1. Додайте стовпці в "виділіть" до "розділу за", наприклад, використовуючи вибір у відповіді: RN = ROW_NUMBER () НАД (РОЗДІЛЕННЯ по col1, col2, col3, col4, col5, col6, col7 ЗАМОВЛЕННЯ col1)
rlee

158

Я віддаю перевагу CTE для видалення повторюваних рядків із таблиці серверів sql

настійно рекомендую дотримуватися цієї статті :: http://codaffection.com/sql-server-article/delete-duplicate-rows-in-sql-server/

зберігаючи оригінал

WITH CTE AS
(
SELECT *,ROW_NUMBER() OVER (PARTITION BY col1,col2,col3 ORDER BY col1,col2,col3) AS RN
FROM MyTable
)

DELETE FROM CTE WHERE RN<>1

без збереження оригіналу

WITH CTE AS
(SELECT *,R=RANK() OVER (ORDER BY col1,col2,col3)
FROM MyTable)
 
DELETE CTE
WHERE R IN (SELECT R FROM CTE GROUP BY R HAVING COUNT(*)>1)

2
Функція вікон - чудове рішення.
Роберт Кейсі

2
Я мало розгублений. Ви видалили його з CTE не з оригінальної таблиці. То як це працює?
Bigeyes

8
@Bigeyes видалення записів із CTE видалить відповідні записи з фактичної фізичної таблиці (оскільки CTE містить посилання на фактичні записи).
Shamseer K

Я не мав уявлення, що це було так до цього посту ... Дякую
Zakk Diaz

1
Чому ви хочете видалити і оригінал, і його дублікат? Я не розумію, чому ви не хочете просто видалити дублікат і зберегти інший.
Багатий

52

Без використання CTEі ROW_NUMBER()ви можете просто видалити записи, лише використовуючи групу за допомогою MAXфункції, тут є і приклад

DELETE
FROM MyDuplicateTable
WHERE ID NOT IN
(
SELECT MAX(ID)
FROM MyDuplicateTable
GROUP BY DuplicateColumn1, DuplicateColumn2, DuplicateColumn3)

4
Цей запит видалить не повторювані записи.
Дерек Смоллз

8
Це добре працює, дякую. @DerekSmalls це не видаляє мої не повторювані записи.
monteirobrena

1
Або ви можете зберігати оригінальні записи, використовуючиMIN(ID)
Savage

18
DELETE from search
where id not in (
   select min(id) from search
   group by url
   having count(*)=1

   union

   SELECT min(id) FROM search
   group by url
   having count(*) > 1
)

Чи не могли ви переписатись у: where id in (виберіть max (id) ... маючи count (*)> 1)?
Брент

1
Я не вірю, що потрібно використовувати або об'єднання, цього буде достатньо: видалити з пошуку, де не входить (виберіть хв (ідентифікатор) з пошукової групи за URL-адресою)
Крістофер Ян

9

Будь ласка, дивіться також спосіб видалення нижче.

Declare @table table
(col1 varchar(10),col2 int,col3 int, col4 int, col5 int, col6 int, col7 int)
Insert into @table values 
('john',1,1,1,1,1,1),
('john',1,1,1,1,1,1),
('sally',2,2,2,2,2,2),
('sally',2,2,2,2,2,2)

Створив зразок таблиці з назвою @tableта завантажив його заданими даними.

введіть тут опис зображення

Delete  aliasName from (
Select  *,
        ROW_NUMBER() over (Partition by col1,col2,col3,col4,col5,col6,col7 order by col1) as rowNumber
From    @table) aliasName 
Where   rowNumber > 1

Select * from @table

введіть тут опис зображення

Примітка: Якщо ви даєте всі стовпці в Partition byчастині, то order byце не має великого значення.

Я знаю, це запитання було задано три роки тому, і моя відповідь - це інша версія того, що Тим опублікував, але розміщення лише випадків корисно для всіх.


9

Якщо у вас немає посилань, як-от іноземні ключі, ви можете це зробити. Я роблю це багато, коли тестую докази концепції, і дані тестування дублюються.

SELECT DISTINCT [col1],[col2],[col3],[col4],[col5],[col6],[col7]

INTO [newTable]

Зайдіть у провідник об’єктів і видаліть стару таблицю.

Перейменуйте нову таблицю з назвою старої таблиці.


Це найпростіший спосіб, який я дізнався у своїх вступних матеріалах, і який я використовую.
eric

7

Microsoft має дуже чітке керівництво щодо видалення дублікатів. Перевірте http://support.microsoft.com/kb/139444

Коротше кажучи, ось найпростіший спосіб видалення дублікатів, якщо ви маєте лише кілька рядків для видалення:

SET rowcount 1;
DELETE FROM t1 WHERE myprimarykey=1;

myprimarykey - це ідентифікатор для рядка.

Я встановив кількість рядків на 1, оскільки у мене було лише два ряди, які були дублюються. Якби у мене було дублювання 3 рядків, я б встановив кількість рядків у 2, щоб він видалив перші два, які він бачив, і залишив лише один у таблиці t1.

Сподіваюся, це допоможе комусь


1
Як я можу знати, скільки рядків я дублював, якщо у мене є 10k рядків?
Фірґхал

@Fearghal спробуйте "вибрати PrimaryKey, count (*) з групи myTable від PrimaryKey;"
oabarca

1
Але що робити, якщо є різні кількості повторюваних рядків? тобто рядок a має 2 записи, а рядок b має 5 записів, а рядок c не має повторюваних записів
терміт

1
@ user2070775 Що робити, якщо лише у підмножини всіх рядків є дублікати, а з цих дублікатів деякі дублюються двічі, а деякі три чи чотири рази?
терміт

@ user2070775 Я пропустив частину, де ви сказали "лише кілька рядків видалити". Також на сторінці є попередження про встановлену кількість рядків, що в наступних версіях sql це не буде впливати на оновлення чи видалення операторів
thermite

6

Спробуйте використовувати:

SELECT linkorder
    ,Row_Number() OVER (
        PARTITION BY linkorder ORDER BY linkorder DESC
        ) AS RowNum
FROM u_links

введіть тут опис зображення


4

Після випробування запропонованого рішення вище, це працює для невеликих середніх таблиць. Я можу запропонувати таке рішення для дуже великих таблиць. оскільки він працює в ітераціях.

  1. Відкиньте всі види залежностей від LargeSourceTable
  2. Ви можете знайти залежності за допомогою студії управління sql, клацніть правою кнопкою миші на таблиці та натисніть "Переглянути залежності"
  3. Перейменуйте таблицю:
  4. sp_rename 'LargeSourceTable', 'LargeSourceTable_Temp'; GO
  5. Створіть LargeSourceTableще раз, але тепер додайте первинний ключ із усіма стовпцями, які визначають додавання дублюванняWITH (IGNORE_DUP_KEY = ON)
  6. Наприклад:

    CREATE TABLE [dbo].[LargeSourceTable] ( ID int IDENTITY(1,1), [CreateDate] DATETIME CONSTRAINT [DF_LargeSourceTable_CreateDate] DEFAULT (getdate()) NOT NULL, [Column1] CHAR (36) NOT NULL, [Column2] NVARCHAR (100) NOT NULL, [Column3] CHAR (36) NOT NULL, PRIMARY KEY (Column1, Column2) WITH (IGNORE_DUP_KEY = ON) ); GO

  7. Створіть знову перегляди, які ви скинули в першу чергу для нової створеної таблиці

  8. Тепер запустіть наступний скрипт sql, ви побачите результати в 1 000 000 рядків на сторінці, ви можете змінити номер рядка на сторінці, щоб частіше бачити результати.

  9. Зауважте, що я встановлював IDENTITY_INSERTі вимикав, оскільки один стовпчик містить автоматичний додатковий ідентифікатор, який я також копіюю

SET IDENTITY_INSERT LargeSourceTable ON DECLARE @PageNumber AS INT, @RowspPage AS INT DECLARE @TotalRows AS INT declare @dt varchar(19) SET @PageNumber = 0 SET @RowspPage = 1000000 select @TotalRows = count (*) from LargeSourceTable_TEMP

While ((@PageNumber - 1) * @RowspPage < @TotalRows )
Begin
    begin transaction tran_inner
        ; with cte as
        (
            SELECT * FROM LargeSourceTable_TEMP ORDER BY ID
            OFFSET ((@PageNumber) * @RowspPage) ROWS
            FETCH NEXT @RowspPage ROWS ONLY
        )

        INSERT INTO LargeSourceTable 
        (
             ID                     
            ,[CreateDate]       
            ,[Column1]   
            ,[Column2] 
            ,[Column3]       
        )       
        select 
             ID                     
            ,[CreateDate]       
            ,[Column1]   
            ,[Column2] 
            ,[Column3]       
        from cte

    commit transaction tran_inner

    PRINT 'Page: ' + convert(varchar(10), @PageNumber)
    PRINT 'Transfered: ' + convert(varchar(20), @PageNumber * @RowspPage)
    PRINT 'Of: ' + convert(varchar(20), @TotalRows)

    SELECT @dt = convert(varchar(19), getdate(), 121)
    RAISERROR('Inserted on: %s', 0, 1, @dt) WITH NOWAIT
    SET @PageNumber = @PageNumber + 1
End

SET IDENTITY_INSERT LargeSourceTable OFF


4

Є два рішення в mysql:

A) Видаліть повторювані рядки за допомогою DELETE JOINоператора

DELETE t1 FROM contacts t1
INNER JOIN contacts t2 
WHERE 
    t1.id < t2.id AND 
    t1.email = t2.email;

Цей запит двічі посилається на таблицю контактів, тому він використовує псевдоніми t1та t2.

Вихід:

1 запит ОК, 4 рядки зачеплені (0,10 сек)

У випадку, якщо ви хочете видалити повторювані рядки та зберегти lowest id, ви можете скористатися наступним висловом:

DELETE c1 FROM contacts c1
INNER JOIN contacts c2 
WHERE
    c1.id > c2.id AND 
    c1.email = c2.email;

   

В) Видаліть повторювані рядки за допомогою проміжної таблиці

Далі показані дії для видалення повторюваних рядків за допомогою проміжної таблиці:

    1. Створіть нову таблицю зі структурою такою ж, як і оригінальну таблицю, з якої потрібно видалити повторювані рядки.

    2. Вставте окремі рядки від початкової таблиці до безпосередньої таблиці.

    3. Вставте окремі рядки від початкової таблиці до безпосередньої таблиці.

 

Крок 1. Створіть нову таблицю, структура якої така ж, як і вихідну таблицю:

CREATE TABLE source_copy LIKE source;

Крок 2. Вставте окремі рядки з початкової таблиці в нову таблицю:

INSERT INTO source_copy
SELECT * FROM source
GROUP BY col; -- column that has duplicate values

Крок 3. опустіть оригінальну таблицю та перейменуйте безпосередню таблицю на початкову

DROP TABLE source;
ALTER TABLE source_copy RENAME TO source;

Джерело: http://www.mysqltutorial.org/mysql-delete-duplicate-rows/


2
-- this query will keep only one instance of a duplicate record.
;WITH cte
     AS (SELECT ROW_NUMBER() OVER (PARTITION BY col1, col2, col3-- based on what? --can be multiple columns
                                       ORDER BY ( SELECT 0)) RN
         FROM   Mytable)



delete  FROM cte
WHERE  RN > 1

2

Потрібно згрупувати за дублюючими записами згідно з полями, потім утримуйте один із записів та видаліть решту. Наприклад:

DELETE prg.Person WHERE Id IN (
SELECT dublicateRow.Id FROM
(
select MIN(Id) MinId, NationalCode
 from  prg.Person group by NationalCode  having count(NationalCode ) > 1
 ) GroupSelect
 JOIN  prg.Person dublicateRow ON dublicateRow.NationalCode = GroupSelect.NationalCode 
 WHERE dublicateRow.Id <> GroupSelect.MinId)

2

Видалення дублікатів з величезної (кілька мільйонів записів) таблиці може зайняти багато часу. Я пропоную вам зробити об'ємну вставку в таблицю темпів вибраних рядків, а не видаляти.

--REWRITING YOUR CODE(TAKE NOTE OF THE 3RD LINE) WITH CTE AS(SELECT NAME,ROW_NUMBER() 
OVER (PARTITION BY NAME ORDER BY NAME) ID FROM @TB) SELECT * INTO #unique_records FROM 
CTE WHERE ID =1;

2

Це можна зробити багатьма способами на сервері sql, найпростіший спосіб зробити це: вставити окремі рядки з таблиці дублікатів рядків у нову тимчасову таблицю. Потім видаліть усі дані з таблиці дублікатів рядків, потім вставте всі дані з тимчасової таблиці, в якій немає дублікатів, як показано нижче.

select distinct * into #tmp From table
   delete from table
   insert into table
   select * from #tmp drop table #tmp

   select * from table

Видаліть повторювані рядки за допомогою загального вираження таблиці (CTE)

With CTE_Duplicates as 
(select id,name , row_number() 
over(partition by id,name order by id,name ) rownumber  from table  ) 
delete from CTE_Duplicates where rownumber!=1

1
with myCTE
as

(
select productName,ROW_NUMBER() over(PARTITION BY productName order by slno) as Duplicate from productDetails
)
Delete from myCTE where Duplicate>1

1

З посиланням на https://support.microsoft.com/en-us/help/139444/how-to-remove-duplicate-rows-from-a-table-in-sql-server

Ідея видалення дубліката передбачає

  • а) Захист тих рядків, які не дублюються
  • b) Збережіть один із багатьох рядків, які кваліфікуються разом як дублікати.

Крок за кроком

  • 1) Спочатку визначте рядки, які задовольняють визначенню дубліката, і вставте їх у таблицю темпів, скажімо, # tableAll.
  • 2) Виберіть недубльовані (однорядкові) або окремі рядки в таблиці темп, наприклад, #tableUnique.
  • 3) Видалити з вихідної таблиці приєднання #tableВсі для видалення дублікатів.
  • 4) Вставте у вихідну таблицю всі рядки з #tableUnique.
  • 5) Відкиньте #tableAll та #tableUnique

1

Якщо у вас є можливість тимчасово додати стовпчик до таблиці, це рішення, яке працювало для мене:

ALTER TABLE dbo.DUPPEDTABLE ADD RowID INT NOT NULL IDENTITY(1,1)

Потім виконайте DELETE, використовуючи комбінацію MIN і GROUP BY

DELETE b
FROM dbo.DUPPEDTABLE b
WHERE b.RowID NOT IN (
                     SELECT MIN(RowID) AS RowID
                     FROM dbo.DUPPEDTABLE a WITH (NOLOCK)
                     GROUP BY a.ITEM_NUMBER,
                              a.CHARACTERISTIC,
                              a.INTVALUE,
                              a.FLOATVALUE,
                              a.STRINGVALUE
                 );

Переконайтесь, що DELETE виконано правильно:

SELECT a.ITEM_NUMBER,
    a.CHARACTERISTIC,
    a.INTVALUE,
    a.FLOATVALUE,
    a.STRINGVALUE, COUNT(*)--MIN(RowID) AS RowID
FROM dbo.DUPPEDTABLE a WITH (NOLOCK)
GROUP BY a.ITEM_NUMBER,
    a.CHARACTERISTIC,
    a.INTVALUE,
    a.FLOATVALUE,
    a.STRINGVALUE
ORDER BY COUNT(*) DESC 

В результаті не повинно бути рядків, кількість яких перевищує 1. Нарешті, видаліть стовпчик із рядами:

ALTER TABLE dbo.DUPPEDTABLE DROP COLUMN RowID;

0

Ще один спосіб видалення дублюваних рядків без втрати інформації за один крок такий:

delete from dublicated_table t1 (nolock)
join (
    select t2.dublicated_field
    , min(len(t2.field_kept)) as min_field_kept
    from dublicated_table t2 (nolock)
    group by t2.dublicated_field having COUNT(*)>1
) t3 
on t1.dublicated_field=t3.dublicated_field 
    and len(t1.field_kept)=t3.min_field_kept

0

О, вау, я відчуваю себе таким нерозумним, готові відповісти на всі ці відповіді, вони як відповідь експертів з усіма CTE та тимчасовою таблицею тощо.

І все, що я зробив, щоб це працювало, було просто агрегувати стовпчик ідентифікаторів за допомогою MAX.

DELETE FROM table WHERE col1 IN (
    SELECT MAX(id) FROM table GROUP BY id HAVING ( COUNT(col1) > 1 )
)

ПРИМІТКА. Вам може знадобитися запустити його кілька разів, щоб видалити дублікат, оскільки це видалить одночасно один набір дублікатів рядків.


Це не працюватиме, оскільки видалить усі дублікати, не залишаючи оригіналів. ОП просить зберегти оригінальні записи.
0xdd

2
Неправда, max надасть вам максимальний ідентифікатор, який відповідає умові. Якщо це не відповідає дійсності, доведіть свою заяву проголосування.
заплутався

0
DECLARE @TB TABLE(NAME VARCHAR(100));
INSERT INTO @TB VALUES ('Red'),('Red'),('Green'),('Blue'),('White'),('White')
--**Delete by Rank**
;WITH CTE AS(SELECT NAME,DENSE_RANK() OVER (PARTITION BY NAME ORDER BY NEWID()) ID FROM @TB)
DELETE FROM CTE WHERE ID>1
SELECT NAME FROM @TB;
--**Delete by Row Number** 
;WITH CTE AS(SELECT NAME,ROW_NUMBER() OVER (PARTITION BY NAME ORDER BY NAME) ID FROM @TB)
DELETE FROM CTE WHERE ID>1;
SELECT NAME FROM @TB;

Видалення дублікатів з величезної (кілька мільйонів записів) таблиці може зайняти багато часу. Я пропоную вам зробити об'ємну вставку до темп-таблиці вибраних рядків, а не видалити. '- ЗАПИСУЙТЕ СВОЙ КОД (ВІДПОМОЖУЙТЕ 3-ту лінію) С CTE AS (ВИБІРТЕ ІМЯ, ROW_NUMBER () НАДЕЖЕ (ДІЛЬНІСТЬ ЗА ІМЕЙНИМ ЗАМОВЛЕННЯМИ ІМЕНОМ) ІДІТЬ @TB) ВИБІРТЕ * В #unique_records ОТ CTE WHERE ID = 1; '
Еммануель Булл

0
DELETE FROM TBL1  WHERE ID  IN
(SELECT ID FROM TBL1  a WHERE ID!=
(select MAX(ID) from TBL1  where DUPVAL=a.DUPVAL 
group by DUPVAL
having count(DUPVAL)>1))
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.