質問

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

もちろん、行は、 RowID アイデンティティフィールド。

マイテーブル

RowID int not null identity(1,1) primary key,
Col1 varchar(20) not null,
Col2 varchar(2048) not null,
Col3 tinyint not null
役に立ちましたか?

解決

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

他のヒント

これを行う別の可能な方法は次のとおりです

; 

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

実行計画

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

Execution Plans

ただし、常にそうとは限りません。ある場所では、 GROUP BY 解決策が好まれるのは、次のような状況です。 ハッシュ集合体 ストリーム集合体よりも優先して選択されます。

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

Execution Plans

ハッシュ集約アプローチが有利になる可能性がある要因は次のとおりです。

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

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

良い記事があります 重複の削除 Microsoft サポート サイトで。これはかなり保守的で、すべてを別々の手順で実行する必要がありますが、大きなテーブルに対してはうまく機能するはずです。

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

DELETE dupes
FROM MyTable dupes, MyTable fullTable
WHERE dupes.dupField = fullTable.dupField 
AND dupes.secondDupField = fullTable.secondDupField 
AND dupes.uniqueField > fullTable.uniqueField

次のクエリは、重複した行を削除するのに役立ちます。この例のテーブルには、 ID ID 列として使用され、重複データを持つ列は Column1, Column2 そして Column3.

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) 

次のスクリプトは、 GROUP BY, HAVING, ORDER BY 1 つのクエリで、重複した列とその数を含む結果を返します。

SELECT YourColumnName,
       COUNT(*) TotalCount
FROM   YourTableName
GROUP  BY YourColumnName
HAVING COUNT(*) > 1
ORDER  BY COUNT(*) DESC 
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
DELETE LU 
FROM   (SELECT *, 
               Row_number() 
                 OVER ( 
                   partition BY col1, col1, col3 
                   ORDER BY rowid DESC) [Row] 
        FROM   mytable) LU 
WHERE  [row] > 1 

これにより、最初の行を除く重複した行が削除されます。

DELETE
FROM
    Mytable
WHERE
    RowID NOT IN (
        SELECT
            MIN(RowID)
        FROM
            Mytable
        GROUP BY
            Col1,
            Col2,
            Col3
    )

参照する (http://www.codeproject.com/Articles/157977/Remove-Duplicate-Rows-from-a-Table-in-SQL-Server)

SQLサーバーテーブルから重複行を削除するには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)

正確に重複した行を削除するには、Quick and Dirty (小さなテーブルの場合):

select  distinct * into t2 from t1;
delete from t1;
insert into t1 select *  from t2;
drop table t2;

私は内部結合よりも 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
--)

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

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);      
SELECT  DISTINCT *
      INTO tempdb.dbo.tmpTable
FROM myTable

TRUNCATE TABLE myTable
INSERT INTO myTable SELECT * FROM tempdb.dbo.tmpTable
DROP TABLE tempdb.dbo.tmpTable

特別な状況下で機能するため、私の解決策を共有したいと思いました。私の場合、重複した値を持つテーブルには外部キーがありませんでした(値が別のデータベースから複製されたため)。

begin transaction
-- create temp table with identical structure as source table
Select * Into #temp From tableName Where 1 = 2

-- insert distinct values into temp
insert into #temp 
select distinct * 
from  tableName

-- delete from source
delete from tableName 

-- insert into source from temp
insert into tableName 
select * 
from #temp

rollback transaction
-- if this works, change rollback to commit and execute again to keep you changes!!

追伸:このようなことに取り組むとき、私は常にトランザクションを使用します。これにより、すべてが全体として確実に実行されるだけでなく、何もリスクを負わずにテストすることができます。もちろん、念のためにバックアップをとっておく必要があります...

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;

さらに別の簡単な解決策は、貼り付けられたリンクで見つけることができます ここ. 。これは理解しやすく、同様の問題のほとんどに効果的であると思われます。これは 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) 

このクエリは、私にとって非常に優れたパフォーマンスを示しました。

DELETE tbl
FROM
    MyTable tbl
WHERE
    EXISTS (
        SELECT
            *
        FROM
            MyTable tbl2
        WHERE
            tbl2.SameValue = tbl.SameValue
        AND tbl.IdUniqueValue < tbl2.IdUniqueValue
    )

2M のテーブルから 30 秒強で 100 万行を削除しました (50% 重複)

ここに別の良い記事があります 重複の削除.

なぜそれが難しいのかについて説明します。」SQL はリレーショナル代数に基づいており、セット内で重複が許可されていないため、リレーショナル代数では重複は発生しません。"

一時テーブルのソリューションと 2 つの mysql の例。

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

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

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 のリストが得られます。

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

DELETE FROM myTable WHERE RowID IN (
  SELECT MIN(RowID) AS IDNo FROM myTable
  GROUP BY Col1, Col2, Col3
  HAVING COUNT(*) = 2 )

他の方法は 新しいを作成します 同じフィールドを持つテーブルと 一意のインデックス付き. 。それから すべてのデータを古いテーブルから新しいテーブルに移動します. 。SQL SERVER は自動的に無視されます (値が重複する場合にどうするかについてのオプションもあります:無視、割り込み、または sth) 重複値。したがって、重複行のない同じテーブルが存在します。 一意のインデックスが不要な場合は、データの転送後に削除できます。.

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

これを使って

WITH tblTemp as
(
SELECT ROW_NUMBER() Over(PARTITION BY Name,Department ORDER BY Name)
   As RowNumber,* FROM <table_name>
)
DELETE FROM tblTemp where RowNumber >1

以下のクエリを使用すると、単一列または複数列に基づいて重複レコードを削除できます。以下のクエリは 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)
  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
    

これは重複レコードを削除する最も簡単な方法です

 DELETE FROM tblemp WHERE id IN 
 (
  SELECT MIN(id) FROM tblemp
   GROUP BY  title HAVING COUNT(id)>1
 )

http://askme.indianyouth.info/details/how-to-dumplicate-record-from-table-in-using-sql-105

このアプローチは便利であり、すべての 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

アプリケーションレベルからです(残念ながら)。重複を防ぐための適切な方法は一意のインデックスを使用してデータベース レベルで行うことであることに私は同意しますが、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) ではないでしょうか?

DELETE
FROM
    table_name T1
WHERE
    rowid > (
        SELECT
            min(rowid)
        FROM
            table_name T2
        WHERE
            T1.column_name = T2.column_name
    );
CREATE TABLE car(Id int identity(1,1), PersonId int, CarId int)

INSERT INTO car(PersonId,CarId)
VALUES(1,2),(1,3),(1,2),(2,4)

--SELECT * FROM car

;WITH CTE as(
SELECT ROW_NUMBER() over (PARTITION BY personid,carid order by personid,carid) as rn,Id,PersonID,CarId from car)

DELETE FROM car where Id in(SELECT Id FROM CTE WHERE rn>1)

削除しようとしている行をプレビューし、どの重複行を保持するかを制御したいと考えています。見る http://developer.azurewebsites.net/2014/09/better-sql-group-by-find-duplicate-data/

with MYCTE as (
  SELECT ROW_NUMBER() OVER (
    PARTITION BY DuplicateKey1
                ,DuplicateKey2 -- optional
    ORDER BY CreatedAt -- the first row among duplicates will be kept, other rows will be removed
  ) RN
  FROM MyTable
)
DELETE FROM MYCTE
WHERE RN > 1
DELETE 
FROM MyTable
WHERE NOT EXISTS (
              SELECT min(RowID)
              FROM Mytable
              WHERE (SELECT RowID 
                     FROM Mytable
                     GROUP BY Col1, Col2, Col3
                     ))
               );
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top