かなり大きなSQL Server
テーブル (つまり 300,000 行以上) から重複行を削除する最良の方法は何ですか?
もちろん、RowID
identity フィールドが存在するため、行は完全な複製にはなりません。
マイテーブル
RowID int not null identity(1,1) primary key,
Col1 varchar(20) not null,
Col2 varchar(2048) not null,
Col3 tinyint not null
かなり大きなSQL Server
テーブル (つまり 300,000 行以上) から重複行を削除する最良の方法は何ですか?
もちろん、RowID
identity フィールドが存在するため、行は完全な複製にはなりません。
マイテーブル
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
、一意の列がありSELECT
、MIN (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
実行計画
この実行計画は、自己結合を必要としないため、多くの場合、受け入れられた回答よりも単純で効率的です。
ただし、常にそうであるとは限りません。GROUP BY
このソリューションが好まれる場所の 1 つは、ストリーム集約よりも優先してハッシュ集約が選択される状況です。
ROW_NUMBER
ソリューションは常にほとんど同じ計画を提供しますが、戦略GROUP BY
はより柔軟です.
ハッシュ集約アプローチを支持する可能性のある要因は次のとおりです。
この 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)
次のスクリプトは、1つのクエリでの、の使用法を示し、GROUP BY
重複する列とその数を含む結果を返します。HAVING
ORDER BY
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
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)
重複行をフェッチするには:
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 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 DISTINCT *
INTO tempdb.dbo.tmpTable
FROM myTable
TRUNCATE TABLE myTable
INSERT INTO myTable SELECT * FROM tempdb.dbo.tmpTable
DROP TABLE tempdb.dbo.tmpTable
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 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のリストができあがります。
重複の削除に関する別の優れた記事を次に示します。
それが難しい理由について説明しています。
一時テーブル ソリューション、および 2 つの mysql の例。
将来的には、データベース レベルで、またはアプリケーションの観点から、それを防ぐつもりですか。データベースは参照整合性を維持する責任があるため、データベースレベルをお勧めします。開発者は問題を引き起こすだけです;)
重複しない行を保持する必要があるテーブルがありました。速度や効率についてはわかりません。
DELETE FROM myTable WHERE RowID IN (
SELECT MIN(RowID) AS IDNo FROM myTable
GROUP BY Col1, Col2, Col3
HAVING COUNT(*) = 2 )
もう 1 つの方法は、同じフィールドとUnique Indexを使用して新しいテーブルを作成することです。次に、すべてのデータを古いテーブルから新しいテーブルに移動します。自動的に SQL SERVER が無視します (重複する値がある場合にどうするかについてのオプションもあります: 無視、割り込み、または sth) 重複する値。したがって、重複する行のない同じテーブルがあります。Unique Index が不要な場合は、データの転送後にドロップできます。
特に大きなテーブルの場合、すべてのデータを新しい一意にインデックス付けされたテーブルに迅速に転送するために、DTS (データをインポート/エクスポートするための SSIS パッケージ) を使用できます。700 万行の場合、数分しかかかりません。
以下のクエリを使用すると、単一の列または複数の列に基づいて重複レコードを削除できます。以下のクエリは、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)
同じ構造の新しい空のテーブルを作成する
このようにクエリを実行します
INSERT INTO tc_category1
SELECT *
FROM tc_category
GROUP BY category_id, application_id
HAVING count(*) > 1
次に、このクエリを実行します
INSERT INTO tc_category1
SELECT *
FROM tc_category
GROUP BY category_id, application_id
HAVING count(*) = 1
私はこのアプローチについて言及するだけでなく、それが役立つ可能性があり、すべての 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
);
削除日を示すために日付が記録されるソフト削除メカニズムが使用される場合があります。この場合、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 万行を含むかなり中程度のテーブルに役立ちました。