13

日時列に基づいてSQLServerテーブルの行をアーカイブするプロセスを作成しています。Xより前の日付のすべての行を移動したいのですが、問題は、各日付に数百万の行があるため、各日付に対してBEGIN TRANSACTION ... INSERT ... DELETE...COMMITを実行するのに時間がかかりすぎることです。 、および他のユーザーのためにデータベースをロックします。

小さなチャンクでそれを行う方法はありますか?多分ROWCOUNTまたはそのようなものを使用していますか?

私はもともとこのようなものを考えていました:

SET ROWCOUNT 1000

DECLARE @RowsLeft DATETIME
DECLARE @ArchiveDate DATETIME

SET @ROWSLEFT = (SELECT TOP 1 dtcol FROM Events WHERE dtcol <= @ArchiveDate)

WHILE @ROWSLEFT IS NOT NULL
BEGIN

    INSERT INTO EventsBackups
    SELECT top 1000 * FROM Events

    DELETE Events

    SET @ROWSLEFT = (SELECT TOP 1 dtcol FROM Events WHERE dtcol <= @ArchiveDate)

END

しかし、その後、削除する行がバックアップしたばかりの行であることを保証できないことに気付きました。または私は...できますか?

更新: 私が検討した別のオプションは、ステップを追加することでした:

  1. 日付基準を満たす上位1000行を一時テーブルに選択します
  2. トランザクションを開始します
  3. 一時テーブルからアーカイブテーブルに挿入します
  4. ソーステーブルから削除し、すべての列で一時テーブルに結合します
  5. トランザクションをコミットする
  6. 日付基準を満たす行がなくなるまで、1〜5を繰り返します。

このシリーズの費用が、以下で説明する他のオプションのいくつかとどのように比較されるかについて、誰かが考えていますか?

詳細:誰かが尋ねたので、私はSQL2005を使用しています。

4

8 に答える 8

25

DELETEの結果を挿入するだけです。

WHILE 1=1
BEGIN

    WITH EventsTop1000 AS (
    SELECT TOP 1000 * 
        FROM Events
      WHERE <yourconditionofchoice>)
    DELETE EventsTop1000
        OUTPUT DELETED.* 
        INTO EventsBackup;

    IF (@@ROWCOUNT = 0)
        BREAK;
END

これはアトミックで一貫性があります。

于 2009-05-14T21:18:12.650 に答える
4

INSERTとOUTPUTINTO句を使用して、挿入された行のIDを格納してから、この一時テーブルにDELETE結合して、それらのIDのみを削除します。

DECLARE @TempTable (YourKeyValue KeyDatatype not null)

INSERT INTO EventsBackups
    (columns1,column2, column3)
    OUTPUT INSERTED.primaryKeyValue
    INTO @TempTable
    SELECT
        top 1000
        columns1,column2, column3
        FROM Events

DELETE Events
    FROM Events
        INNER JOIN @TempTable  t ON Events.PrimaryKey=t.YourKeyValue 
于 2009-05-14T18:16:26.237 に答える
0

日付フィールドにインデックスがありますか?まだSQLを使用していない場合は、アーカイブステートメントの実行中にすべてのユーザーをロックアウトするテーブルロックにアップグレードする必要があります。

この操作をうまく実行するには、インデックスが必要になると思います。日付フィールドにインデックスを付けて、操作を再試行してください。

于 2009-05-14T16:59:15.923 に答える
0

どうですか:

INSERT INTO EventsBackups
SELECT TOP 1000 * FROM Events ORDER BY YourKeyField

DELETE Events
WHERE YourKeyField IN (SELECT TOP 1000 YourKeyField FROM Events ORDER BY YourKeyField)
于 2009-05-14T16:27:02.520 に答える
0

一度に全部やってみませんか?

INSERT INTO EventsBackups
SELECT * FROM Events WHERE date criteria

じゃあ後で、

DELETE FROM Events
SELECT * FROM Events INNER JOIN EventsBackup on Events.ID = EventsBackup.ID

または同等のもの。

これまでのところ、取引が必要であることを示唆するものはありません。

于 2009-05-14T16:27:55.483 に答える
0

イベントのコピーを作成し、日付> = xのすべての行をその行に移動し、イベントを削除して、コピーの名前をイベントに変更できますか?または、コピーして切り捨ててからコピーし直しますか?少しのダウンタイムを許容できる場合は、これがおそらく最も迅速なアプローチです。

于 2009-05-14T17:50:33.710 に答える
0

これが私がやったことです:

SET @CleanseFilter = @startdate
WHILE @CleanseFilter IS NOT NULL
BEGIN
    BEGIN TRANSACTION

        INSERT INTO ArchiveDatabase.dbo.MyTable
        SELECT *
          FROM dbo.MyTable
         WHERE startTime BETWEEN @startdate AND @CleanseFilter

        DELETE dbo.MyTable
         WHERE startTime BETWEEN @startdate AND @CleanseFilter

    COMMIT TRANSACTION

    SET @CleanseFilter = (SELECT MAX(starttime)
                FROM (SELECT TOP 1000
                             starttime
                    FROM dbo.MyTable
                       WHERE startTime BETWEEN @startdate AND @enddate
                    ORDER BY starttime) a)
END

私は正確に1000を引いているのではなく、1000っぽいだけなので、時間列の繰り返しを適切に処理します(ROWCOUNTの使用を検討したときに心配したこと)。時間列には繰り返しが頻繁にあるため、1002または1004行/反復で定期的に移動しているのがわかります。したがって、すべてが取得されていることがわかります。

私はこれを答えとして提出しているので、人々が提供した他の解決策と比較して判断することができます。この方法に明らかに問題があるかどうか教えてください。皆さん、ご協力いただきありがとうございます。数日以内に投票数が最も多い回答を受け入れます。

于 2009-05-14T19:27:45.113 に答える
0

もう1つのオプションは、EventsBackupテーブルに同じレコードを追加するだけのトリガープロシージャをEventsテーブルに追加することです。

そうすれば、EventsBackupは常に最新の状態になり、Eventsテーブルからレコードを定期的に削除するだけです。

于 2009-05-14T19:38:53.183 に答える