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

いくつかのクエリを試してみましたが、目的の結果が得られないため、行IDを持っていることに依存していると思います。例えば:

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

5
これは最初のリンクのだましではありません。この質問には行IDがなく、リンクされた質問には行IDがあります。非常に異なります。
Alien Technology

'SELECT id FROM table GROUP BY id HAVING'を変更して、MAX / MINなどの集約関数を設定すると、機能するはずです。
混乱した

回答:


785

私はCTEが好きでROW_NUMBER、2つを組み合わせると、どの行が削除(または更新)されたかを確認できるため、単にをに変更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

2
@ omachu23:この場合は問題ありませんが、CTEではoutside(AND COl1='John')よりも効率的だと思います。通常、CTEでフィルターを適用する必要があります。
Tim Schmelter、2015

1
@ omachu23:CTEで任意のSQLを使用できます(順序付けは除く)。したがって、Johnsでフィルター処理する場合は、次のようにします...FROM dbo.Table1 WHERE Col1='John'。フィドルは次のとおり
Tim Schmelter、2015

1
最も簡単な解決策はただかもしれset rowcount 1 delete from t1 where col1=1 and col2=1見られるように、ここで
Zorgarath

15
この回答は、col1に重複がある行のみを削除します。たとえば、回答の選択を使用して、「select」の列を「partition by」に追加します。RN = ROW_NUMBER()OVER(PARTITION BY col1、col2、col3、col4、col5、col6、col7 ORDER BY col1)
rlee

158

SQL Serverテーブルから重複する行を削除するにはCTEを使用します

この記事に従うことを強くお勧めします:: 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
ウィンドウ関数は優れたソリューションです。
Robert Casey

2
私は少し混乱しています。元のテーブルではなく、CTEから削除しました。それはどのように機能するのでしょうか?
Bigeyes

8
@BigeyesがCTEからレコードを削除すると、対応するレコードが実際の物理テーブルから削除されます(CTEには実際のレコードへの参照が含まれているため)。
Shamseer K

この投稿まで、私はこれが事実であることを
知り

1
オリジナルとその複製の両方を削除したいのはなぜですか?なぜ重複を削除して他を保持したくないのか理解できません。
リッチ

52

使用せずに、CTEそしてROW_NUMBER()あなただけだけで、グループを使用してレコードを削除することができMAX、ここで関数であり、例えば、

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

4
このクエリは、重複していないレコードを削除します。
Derek Smalls 2017年

8
これは正常に機能します。ありがとうございます。@DerekSmallsは、重複していないレコードを削除しません。
モンテイロブレナ2017

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(select max(id)... having count(*)> 1)?
ブレント

1
持つか結合を使用する必要があるとは思わない、これで十分です:IDが存在しない場所から削除(URLで検索グループからmin(id)を選択)
クリストファーヤン

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それほど重要ではありません。

質問は3年前に出されたもので、私の答えはTimが投稿したものの別のバージョンです。


9

外部キーなどの参照がない場合は、これを行うことができます。プルーフオブコンセプトをテストすると、テストデータが重複するため、多くのことを行います。

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

INTO [newTable]

オブジェクトエクスプローラーに移動し、古いテーブルを削除します。

新しいテーブルの名前を古いテーブルの名前に変更します。


これは、紹介資料で学んだ最も簡単な方法であり、私が使用する方法です。
エリック

7

Microsoftには、重複を削除する方法についてのきちんとしたガイドがあります。http://support.microsoft.com/kb/139444を確認して ください

簡単に言えば、削除する行が数行しかない場合に重複を削除する最も簡単な方法は次のとおりです。

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

myprimarykeyは行の識別子です。

重複する行が2つしかないため、rowcountを1 に設定しました。重複する3つの行があった場合、rowcountを2に設定して、最初の2つを削除し、テーブルt1に1つだけ残すようにします。

それが誰にも役立つことを願って


1
1万行ある場合、複製した行数を知るにはどうすればよいですか?
Fearghal 2014

@Fearghalは「primaryKeyによってmyTableグループからprimaryKey、count(*)を選択する」を試みます。
oabarca 2014年

1
しかし、さまざまな数の重複行がある場合はどうでしょうか。つまり、行aには2つのレコードがあり、行bには5つのレコードがあり、行cには重複レコードがない
テルミット、

1
@ user2070775すべての行のサブセットのみに重複があり、それらの重複のうちのいくつかが2回、一部が3または4回重複している場合はどうなりますか?
テルミット2014年

@ user2070775「数行削除するだけ」と言っていた箇所を逃しました。また、ページにset rowcountに関する警告があり、SQLの将来のバージョンでは、更新または削除ステートメントに影響を与えません
テルミット

6

使用してみてください:

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

ここに画像の説明を入力してください


4

上記の提案されたソリューションを試した後、それは小さな中規模のテーブルで機能します。非常に大きなテーブルの場合、その解決策を提案できます。繰り返し実行されるため。

  1. のすべての依存関係ビューを削除します LargeSourceTable
  2. sql managment studioを使用して依存関係を見つけることができます。テーブルを右クリックして、[依存関係の表示]をクリックします
  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_INSERT1つの列に自動インクリメンタルIDが含まれているため、オンとオフを設定していることに注意してください。これもコピーしています。

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

には2つのソリューションがありますmysql

A)DELETE JOINステートメントを使用して重複行を削除する

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

このクエリは連絡先テーブルを2回参照するため、テーブルのエイリアスt1とを使用しますt2

出力は次のとおりです。

1つのクエリはOK、4つの行が影響を受ける(0.10秒)

重複する行を削除してを保持したい場合lowest idは、次のステートメントを使用できます。

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

   

B)中間テーブルを使用して重複行を削除する

次に、中間テーブルを使用して重複行を削除する手順を示します。

    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

フィールドに従って重複するレコードでグループ化し、レコードの1つを保持して残りを削除する必要があります。例えば:

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を参照して

重複を削除するアイデアには、

  • a)重複していない行を保護する
  • b)重複とみなされた多数の行の1つを保持します。

ステップバイステップ

  • 1)最初に重複の定義を満たす行を特定し、それらを一時テーブルに挿入します(例:#tableAll)。
  • 2)重複しない(単一行)または一時テーブルに異なる行を選択して、#tableUniqueと言います。
  • 3)#tableAllを結合するソーステーブルから削除して、重複を削除します。
  • 4)#tableUniqueのすべての行をソーステーブルに挿入します。
  • 5)#tableAllと#tableUniqueをドロップします

1

一時的にテーブルに列を追加する機能がある場合、これは私のために働いた解決策でした:

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

次に、MINとGROUP BYの組み合わせを使用してDELETEを実行します

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より大きい行がないはずです。最後に、rowid列を削除します。

ALTER TABLE dbo.DUPPEDTABLE DROP COLUMN RowID;

0

1ステップで情報を失うことなく重複行を削除する別の方法は、次のようなものです。

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を使用してID列を集約することだけでした。

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

注:重複を削除するには、一度に1セットの重複行しか削除されないため、複数回実行する必要がある場合があります。


オリジナルを残さずにすべての重複を削除するため、これは機能しません。OPは元のレコードを保存するように求めています。
0xdd '17

2
trueではありません。maxは、条件を満たしている最大IDを提供します。それが真実でない場合は、反対票を投じることを証明してください。
めちゃめちゃアップ

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;

巨大な(数百万のレコード)テーブルから重複を削除すると、時間がかかる場合があります。削除するのではなく、選択した行の一時テーブルに一括挿入することをお勧めします。'--CTE AS(SELECT NAME、ROW_NUMBER()OVER(PARTITION BY NAME ORDER ORDER NAME)ID from @TB)を使用してコードを書き直す(3行目をメモする)SELECT * INTO #unique_records FROM CTE WHERE ID = 1; 」
Emmanuel Bull

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))
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.