web-dev-qa-db-ja.com

WHILEループを使用したSQL Serverの一括削除が機能しない

私は非常に大きなテーブルを持っているので、古いエントリを削除するために以下を使用しています:

WHILE (@@ROWCOUNT > 0)
BEGIN
    DELETE TOP (5000) FROM myTable
    WHERE date < 20130103
END

異なる日付を使用してこれを数回実行しました。正常に動作することもありますが(約20分かかります)、クエリがすぐに終了して何も削除されない場合もあります。その場合は、そのテーブルから単純なSELECTステートメントを実行してから、上記のWHILEステートメントを再試行すると機能します。これがなぜか誰でも知っていますか?このクエリを自動化して定期的に実行し、テーブルサイズを制御する必要がありますが、実行時に実際に正しく削除されることを確認したいと思います。ありがとうございました。

16
JCB

おそらく、その理由は@@ROWCOUNTは値0に初期化されます。

最初にこのクエリを実行して設定できます。

select count(*) from myTable where date < 20130103

これにより、クエリに少し時間が追加されますが、削除される行の数が表示されます。

次のようなこともできます:

select top 1     * from myTable 

はるかに速くなります。

10
Gordon Linoff

このコードブロックの前に何を実行していますか? @@ROWCOUNTは、その前のステートメントに設定されます。他のコマンドを事前に実行すると、0になる可能性があります。

代わりに、初期カウントを1に強制できます。

DECLARE @Rows INT
SET @Rows = 1

WHILE (@Rows > 0)
BEGIN
    DELETE TOP (5000) FROM myTable
    WHERE date < 20130103

    SET @Rows = @@ROWCOUNT
END
35

それは時々@@ROWCOUNTは最初はゼロです-したがって、whileループは、条件beforeをチェックするため、最初の実行を含むすべての実行を実行するため、実行されません。

これが自家製do-whileループ。SQLServerにはビルトインされていないため。

loop:
   DELETE TOP (5000) FROM myTable
   WHERE date < 20130103
if @@ROWCOUNT > 0 goto loop
6

バッチで削除する場合、WAITFOR DELAY(少なくとも1秒または2秒)を追加します。また、ループの外側で作成した最初のステートメント。ところで、区切り文字としてROWCOUNTを使用しないでください( https://docs.Microsoft.com/en-us/sql/t-sql/statements/set-rowcount-transact-sql?view=sql-server-2017 )。 2つのオプションがあります。

DECLARE @BatchSize BIGINT = 50000
SET ROWCOUNT @BatchSize

DELETE 
FROM myTable 
WHERE 
date < 20130103

WHILE (@@ROWCOUNT > 0)
BEGIN
WAITFOR DELAY '00:00:02'
DELETE 
FROM myTable 
WHERE 
date < 20130103
END

または

DECLARE @BatchSize BIGINT = 50000

WHILE 1=1
BEGIN

    WAITFOR DELAY '00:00:02'

    DELETE TOP(@BatchSize )
    FROM myTable 
    WHERE 
    date < 20130103

    IF @@ROWCOUNT < @BatchSize 
        Break
END
2
Eduardo Gelvis

基本的に、

SELECT 0 -- rowcount is 1 
WHILE (@@ROWCOUNT > 0)
BEGIN
    DELETE TOP (5000) FROM myTable
    WHERE date < 20130103
END

または

SET ROWCOUNT 5000 -- set row count to 5000
SELECT 0 -- rowcount is 1 
WHILE (@@ROWCOUNT > 0)
BEGIN
    DELETE FROM myTable
    WHERE date < 20130103
END
SET ROWCOUNT 0  -- set rowcount to unlimited
2
Ali Osman Yavuz

次のようにクエリを記述することもできます。

SET ROWCOUNT 5000; -- set batch size
WHILE EXISTS (SELECT 1 FROM myTable WHERE date < '2013-01-03')
BEGIN
    DELETE FROM myTable
    WHERE date < '2013-01-03'
END;
SET ROWCOUNT 0; -- set batch size back to "no limit"

どちらの方法でも、日付文字列を適切にフォーマットする必要があります。

削除条件とexists句のステートメントが同じであることを確認してください。そうしないと、無限ループが発生する可能性があります。

1
Jim Clouse