1346

かなり大きなSQL Serverテーブル (つまり 300,000 行以上) から重複行を削除する最良の方法は何ですか?

もちろん、RowIDidentity フィールドが存在するため、行は完全な複製にはなりません。

マイテーブル

RowID int not null identity(1,1) primary key,
Col1 varchar(20) not null,
Col2 varchar(2048) not null,
Col3 tinyint not null
4

41 に答える 41

1178

null がないと仮定するとGROUP BY、一意の列がありSELECTMIN (or MAX)保持する行として RowId があります。次に、行 ID を持たないものをすべて削除します。

DELETE FROM MyTable
LEFT OUTER JOIN (
   SELECT MIN(RowId) as RowId, Col1, Col2, Col3 
   FROM MyTable 
   GROUP BY Col1, Col2, Col3
) as KeepRows ON
   MyTable.RowId = KeepRows.RowId
WHERE
   KeepRows.RowId IS NULL

整数の代わりに GUID がある場合は、置き換えることができます

MIN(RowId)

CONVERT(uniqueidentifier, MIN(CONVERT(char(36), MyGuidColumn)))
于 2008-08-20T22:00:00.667 に答える
784

これを行う別の可能な方法は

; 

--Ensure that any immediately preceding statement is terminated with a semicolon above
WITH cte
     AS (SELECT ROW_NUMBER() OVER (PARTITION BY Col1, Col2, Col3 
                                       ORDER BY ( SELECT 0)) RN
         FROM   #MyTable)
DELETE FROM cte
WHERE  RN > 1;

ORDER BY (SELECT 0)同点の場合に保持する行は任意であるため、上記を使用しています。

たとえば、最新のものを保存するには、RowID次を使用できますORDER BY RowID DESC

実行計画

この実行計画は、自己結合を必要としないため、多くの場合、受け入れられた回答よりも単純で効率的です。

実行計画

ただし、常にそうであるとは限りません。GROUP BYこのソリューションが好まれる場所の 1 つは、ストリーム集約よりも優先してハッシュ集約が選択される状況です。

ROW_NUMBERソリューションは常にほとんど同じ計画を提供しますが、戦略GROUP BYはより柔軟です.

実行計画

ハッシュ集約アプローチを支持する可能性のある要因は次のとおりです。

  • パーティショニング列に有用なインデックスがありません
  • 比較的少数のグループで、各グループの重複が比較的多い

この 2 番目のケースの極端なバージョン (それぞれに多くの重複があるグループがほとんどない場合) では、単純に行を挿入して新しいテーブルに保持しTRUNCATE、元のテーブルをコピーして元に戻して、ログを削除するのと比較してログを最小限に抑えることも検討できます。行の割合が非常に高い。

于 2010-09-29T14:52:32.317 に答える
154

Microsoft サポート サイトには、重複の削除に関する優れた記事があります。それはかなり保守的です - 彼らはあなたに別々のステップですべてをやらせます - しかし、それは大きなテーブルに対してうまくいくはずです.

過去にこれを行うために自己結合を使用しましたが、おそらく HAVING 句できれいにすることができます:

DELETE dupes
FROM MyTable dupes, MyTable fullTable
WHERE dupes.dupField = fullTable.dupField 
AND dupes.secondDupField = fullTable.secondDupField 
AND dupes.uniqueField > fullTable.uniqueField
于 2008-08-20T21:53:14.940 に答える
100

次のクエリは、重複する行を削除するのに役立ちます。この例のテーブルにはIDID列があり、データが重複している列は、、Column1です。Column2Column3

DELETE FROM TableName
WHERE  ID NOT IN (SELECT MAX(ID)
                  FROM   TableName
                  GROUP  BY Column1,
                            Column2,
                            Column3
                  /*Even if ID is not null-able SQL Server treats MAX(ID) as potentially
                    nullable. Because of semantics of NOT IN (NULL) including the clause
                    below can simplify the plan*/
                  HAVING MAX(ID) IS NOT NULL) 

次のスクリプトは、1つのクエリでの、の使用法を示し、GROUP BY重複する列とその数を含む結果を返します。HAVINGORDER BY

SELECT YourColumnName,
       COUNT(*) TotalCount
FROM   YourTableName
GROUP  BY YourColumnName
HAVING COUNT(*) > 1
ORDER  BY COUNT(*) DESC 
于 2011-11-23T15:32:19.247 に答える
69
delete t1
from table t1, table t2
where t1.columnA = t2.columnA
and t1.rowid>t2.rowid

ポストグル:

delete
from table t1
using table t2
where t1.columnA = t2.columnA
and t1.rowid > t2.rowid
于 2010-09-30T02:35:06.353 に答える
46
DELETE LU 
FROM   (SELECT *, 
               Row_number() 
                 OVER ( 
                   partition BY col1, col1, col3 
                   ORDER BY rowid DESC) [Row] 
        FROM   mytable) LU 
WHERE  [row] > 1 
于 2014-05-21T07:54:47.127 に答える
37

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)
于 2015-05-19T14:35:26.173 に答える
30

重複行をフェッチするには:

SELECT
name, email, COUNT(*)
FROM 
users
GROUP BY
name, email
HAVING COUNT(*) > 1

重複行を削除するには:

DELETE users 
WHERE rowid NOT IN 
(SELECT MIN(rowid)
FROM users
GROUP BY name, email);      
于 2016-12-29T10:31:23.043 に答える
24

正確に重複した行を削除するクイックアンドダーティ(小さなテーブルの場合):

select  distinct * into t2 from t1;
delete from t1;
insert into t1 select *  from t2;
drop table t2;
于 2013-02-05T21:44:52.483 に答える
21

内部結合よりも subquery\having count(*) > 1 ソリューションの方が読みやすく、SELECT ステートメントに変換して実行前に何が削除されるかを確認するのが非常に簡単だったので、私はそれを好みます。

--DELETE FROM table1 
--WHERE id IN ( 
     SELECT MIN(id) FROM table1 
     GROUP BY col1, col2, col3 
     -- could add a WHERE clause here to further filter
     HAVING count(*) > 1
--)
于 2014-03-01T07:40:18.360 に答える
17
SELECT  DISTINCT *
      INTO tempdb.dbo.tmpTable
FROM myTable

TRUNCATE TABLE myTable
INSERT INTO myTable SELECT * FROM tempdb.dbo.tmpTable
DROP TABLE tempdb.dbo.tmpTable
于 2012-10-10T11:17:38.013 に答える
14

CTE の使用。アイデアは、重複レコードを形成する 1 つ以上の列を結合してから、好きな方を削除することです。

;with cte as (
    select 
        min(PrimaryKey) as PrimaryKey
        UniqueColumn1,
        UniqueColumn2
    from dbo.DuplicatesTable 
    group by
        UniqueColumn1, UniqueColumn1
    having count(*) > 1
)
delete d
from dbo.DuplicatesTable d 
inner join cte on 
    d.PrimaryKey > cte.PrimaryKey and
    d.UniqueColumn1 = cte.UniqueColumn1 and 
    d.UniqueColumn2 = cte.UniqueColumn2;
于 2014-11-13T16:20:35.920 に答える
13

さらに別の簡単な解決策は、ここに貼り付けられたリンクにあります。これは把握しやすく、同様の問題のほとんどに有効であるようです。ただし、これは SQL Server 用ですが、使用されている概念は許容範囲を超えています。

リンクされたページの関連部分は次のとおりです。

次のデータを検討してください。

EMPLOYEE_ID ATTENDANCE_DATE
A001    2011-01-01
A001    2011-01-01
A002    2011-01-01
A002    2011-01-01
A002    2011-01-01
A003    2011-01-01

では、これらの重複データを削除するにはどうすればよいでしょうか?

まず、次のコードを使用して、そのテーブルに ID 列を挿入します。

ALTER TABLE dbo.ATTENDANCE ADD AUTOID INT IDENTITY(1,1)  

次のコードを使用して解決します。

DELETE FROM dbo.ATTENDANCE WHERE AUTOID NOT IN (SELECT MIN(AUTOID) _
    FROM dbo.ATTENDANCE GROUP BY EMPLOYEE_ID,ATTENDANCE_DATE) 
于 2013-08-06T17:14:34.843 に答える
11

はい。一時テーブルを使用します。「機能する」単一の、あまりパフォーマンスの低いステートメントが必要な場合は、次のように使用できます。

DELETE FROM MyTable WHERE NOT RowID IN
    (SELECT 
        (SELECT TOP 1 RowID FROM MyTable mt2 
        WHERE mt2.Col1 = mt.Col1 
        AND mt2.Col2 = mt.Col2 
        AND mt2.Col3 = mt.Col3) 
    FROM MyTable mt)

基本的に、テーブルの各行について、副選択は、検討中の行とまったく同じであるすべての行の最上位のRowIDを検索します。したがって、「元の」重複していない行を表すRowIDのリストができあがります。

于 2008-08-20T22:27:53.843 に答える
11

重複の削除に関する別の優れた記事を次に示します。

それが難しい理由について説明しています

一時テーブル ソリューション、および 2 つの mysql の例。

将来的には、データベース レベルで、またはアプリケーションの観点から、それを防ぐつもりですか。データベースは参照整合性を維持する責任があるため、データベースレベルをお勧めします。開発者は問題を引き起こすだけです;)

于 2008-08-20T21:58:00.983 に答える
11

重複しない行を保持する必要があるテーブルがありました。速度や効率についてはわかりません。

DELETE FROM myTable WHERE RowID IN (
  SELECT MIN(RowID) AS IDNo FROM myTable
  GROUP BY Col1, Col2, Col3
  HAVING COUNT(*) = 2 )
于 2009-12-11T13:47:21.353 に答える
10

もう 1 つの方法は、同じフィールドとUnique Indexを使用して新しいテーブルを作成することです。次に、すべてのデータを古いテーブルから新しいテーブルに移動します。自動的に SQL SERVER が無視します (重複する値がある場合にどうするかについてのオプションもあります: 無視、割り込み、または sth) 重複する値。したがって、重複する行のない同じテーブルがあります。Unique Index が不要な場合は、データの転送後にドロップできます

特に大きなテーブルの場合、すべてのデータを新しい一意にインデックス付けされたテーブルに迅速に転送するために、DTS (データをインポート/エクスポートするための SSIS パッケージ) を使用できます。700 万行の場合、数分しかかかりません。

于 2013-09-18T06:36:12.373 に答える
9

以下のクエリを使用すると、単一の列または複数の列に基づいて重複レコードを削除できます。以下のクエリは、2 つの列に基づいて削除しています。テーブル名:testingおよび列名empno,empname

DELETE FROM testing WHERE empno not IN (SELECT empno FROM (SELECT empno, ROW_NUMBER() OVER (PARTITION BY empno ORDER BY empno) 
AS [ItemNumber] FROM testing) a WHERE ItemNumber > 1)
or empname not in
(select empname from (select empname,row_number() over(PARTITION BY empno ORDER BY empno) 
AS [ItemNumber] FROM testing) a WHERE ItemNumber > 1)
于 2012-02-08T12:06:52.687 に答える
9
  1. 同じ構造の新しい空のテーブルを作成する

  2. このようにクエリを実行します

    INSERT INTO tc_category1
    SELECT *
    FROM tc_category
    GROUP BY category_id, application_id
    HAVING count(*) > 1
    
  3. 次に、このクエリを実行します

    INSERT INTO tc_category1
    SELECT *
    FROM tc_category
    GROUP BY category_id, application_id
    HAVING count(*) = 1
    
于 2009-05-08T13:06:42.697 に答える
7

私はこのアプローチについて言及するだけでなく、それが役立つ可能性があり、すべての SQL サーバーで機能します。ほとんどの場合、重複は 1 つまたは 2 つしかなく、ID と重複の数はわかっています。この場合:

SET ROWCOUNT 1 -- or set to number of rows to be deleted
delete from myTable where RowId = DuplicatedID
SET ROWCOUNT 0
于 2013-01-30T19:45:37.097 に答える
7

アプリケーションレベルから(残念ながら)。重複を防ぐ適切な方法は一意のインデックスを使用してデータベース レベルで行うことに同意しますが、SQL Server 2005 では、インデックスは 900 バイトまでしか許可されておらず、varchar(2048) フィールドはそれを吹き飛ばします。

それがどれほどうまく機能するかはわかりませんが、インデックスで直接実行できなくても、これを強制するトリガーを作成できると思います。何かのようなもの:

-- given a table stories(story_id int not null primary key, story varchar(max) not null)
CREATE TRIGGER prevent_plagiarism 
ON stories 
after INSERT, UPDATE 
AS 
    DECLARE @cnt AS INT 

    SELECT @cnt = Count(*) 
    FROM   stories 
           INNER JOIN inserted 
                   ON ( stories.story = inserted.story 
                        AND stories.story_id != inserted.story_id ) 

    IF @cnt > 0 
      BEGIN 
          RAISERROR('plagiarism detected',16,1) 

          ROLLBACK TRANSACTION 
      END 

また、varchar(2048) は私には怪しいように聞こえます (人生には 2048 バイトのものもありますが、それはかなり珍しいことです)。本当に varchar(max) であってはいけませんか?

于 2008-08-20T22:53:12.630 に答える
7
DELETE
FROM
    table_name T1
WHERE
    rowid > (
        SELECT
            min(rowid)
        FROM
            table_name T2
        WHERE
            T1.column_name = T2.column_name
    );
于 2013-10-03T06:18:06.117 に答える
1

削除日を示すために日付が記録されるソフト削除メカニズムが使用される場合があります。この場合、UPDATEステートメントを使用して、重複するエントリに基づいてこのフィールドを更新できます。

UPDATE MY_TABLE
   SET DELETED = getDate()
 WHERE TABLE_ID IN (
    SELECT x.TABLE_ID
      FROM MY_TABLE x
      JOIN (SELECT min(TABLE_ID) id, COL_1, COL_2, COL_3
              FROM MY_TABLE d
             GROUP BY d.COL_1, d.COL_2, d.COL_3
            HAVING count(*) > 1) AS d ON d.COL_1 = x.COL_1
                                     AND d.COL_2 = x.COL_2
                                     AND d.COL_3 = x.COL_3
                                     AND d.TABLE_ID <> x.TABLE_ID
             /*WHERE x.COL_4 <> 'D' -- Additional filter*/)

この方法は、重複の量が多い場合と少ない場合で、約 3,000 万行を含むかなり中程度のテーブルに役立ちました。

于 2016-06-07T01:29:07.227 に答える