web-dev-qa-db-ja.com

重複する行を削除する最も速い方法は何ですか?

大きなテーブルから重複する行を削除する必要があります。それを達成するための最良の方法は何ですか?

現在私はこのアルゴリズムを使用しています:

declare @t table ([key] int  )

insert into @t select 1
insert into @t select 1
insert into @t select 1
insert into @t select 2
insert into @t select 2
insert into @t select 3
insert into @t select 4
insert into @t select 4
insert into @t select 4
insert into @t select 4
insert into @t select 4
insert into @t select 5
insert into @t select 5
insert into @t select 5
insert into @t select 5
insert into @t select 5
insert into @t select 6
insert into @t select 6
insert into @t select 6
insert into @t select 7
insert into @t select 7
insert into @t select 8
insert into @t select 8
insert into @t select 9
insert into @t select 9
insert into @t select 9
insert into @t select 9
insert into @t select 9


select * from @t

; with cte as (
    select *
        , row_number() over (partition by [Key] order by [Key]) as Picker
    from @t
    )
delete cte 
where Picker > 1

select * from @t

私のシステムで実行すると:

;WITH Customer AS
    (
    SELECT *, ROW_NUMBER() OVER (PARTITION BY AccountCode ORDER BY AccountCode ) AS [Version]
    FROM Stage.Customer
    )
    DELETE
    FROM    Customer
    WHERE   [Version] <> 1

enter image description here

<> 1は> 1より優れていることがわかりました。

私はこのインデックスを作成できましたが、現在は存在しません:

USE [BodenDWH]
GO
CREATE NONCLUSTERED INDEX [<Name of Missing Index, sysname,>]
ON [Stage].[Customer] ([AccountCode])
INCLUDE ([ID])
GO

enter image description here

これを行う他の方法はありますか?

この場合、このテーブルは大きくありません-ライブシステムで約500,000レコード。

削除はSSISパッケージの一部であり、毎日実行され、1日あたり約10〜15個のレコードが削除されます。

データの構造に問題があります。顧客ごとに1つのAccountCodeが必要ですが、重複があり、それらが削除されない場合、後の段階でパッケージが破損します。

パッケージを開発したのは私ではなく、私の設計範囲は何も再設計しないことです。

私は、インデックスの作成や何かを参照する必要なく、T-SQLコードだけで、重複を取り除くための最善の方法の直後にいます。

4

テーブルが小さく、削除する行数が少ない場合は、

_;WITH Customer AS
    (
    SELECT *, ROW_NUMBER() OVER (PARTITION BY AccountCode ORDER BY (select null) ) AS [Version]
    FROM dbo.Customer
    )
    DELETE
    FROM    Customer
    WHERE   [Version] > 1;
_

注:上記のクエリでは、ウィンドウ順序句ORDER BY (select null)で任意の順序を使用していますから学びました)Itzik Ben-GanのT-SQLクエリbook と@AaronBertrandは上記も引用しています)

テーブルが大きい場合(例:5Mレコード)、 少数の行またはチャンク を削除すると、トランザクションログが膨張しないようになり、 ロックのエスカレーションが防止されます

ロックのエスカレーションは、Transact-SQLステートメントがテーブルの単一の参照で少なくとも5000のロックを取得した場合にのみ発生します。

_while 1=1
begin
WITH Customer AS
    (
    SELECT *, ROW_NUMBER() OVER (PARTITION BY AccountCode ORDER BY (select null) ) AS [Version]
    FROM dbo.Customer
    )
    DELETE top(4000) -- choose a lower batch size than 5000 to prevent lock escalation 
    FROM    Customer
    WHERE   [Version] > 1

    if @@ROWCOUNT < 4000
    BREAK ;

end
_
5
Kin Shah