13

我正在编写一个基于日期时间列归档 SQL Server 表中的行的过程。我想移动日期在 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 直到没有满足日期条件的行

有没有人知道这个系列的费用与下面讨论的其他一些选项相比如何?

细节:我正在使用 SQL 2005,因为有人问过。

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

使用带有 OUTPUT INTO 子句的 INSERT 来存储插入行的 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

这就是我最终做的事情:

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,只是 1000ish,所以它适当地处理时间列中的重复(当我考虑使用 ROWCOUNT 时,我担心这一点)。由于时间列中经常有重复,我看到它定期移动 1002 或 1004 行/迭代,所以我知道它得到了一切。

我将此作为答案提交,以便可以根据人们提供的其他解决方案来判断它。让我知道这种方法是否有明显问题。谢谢大家的帮助,我会接受几天内得票最多的答案。

于 2009-05-14T19:27:45.113 回答
0

另一种选择是向 Events 表添加一个触发器过程,该过程只将相同的记录添加到 EventsBackup 表中。

这样,EventsBackup 始终是最新的,您所做的只是定期从您的 Events 表中清除记录。

于 2009-05-14T19:38:53.183 回答
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

怎么样:

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

你有日期字段的索引吗?如果您还没有 sql 可能会被迫升级到表锁,这将在您的归档语句运行时锁定您的所有用户。

我认为您将需要一个索引才能使此操作完全执行!在您的日期字段上放置一个索引并再次尝试您的操作!

于 2009-05-14T16:59:15.923 回答
0

您能否制作事件的副本,将所有日期>= x 的行移至该位置,删除事件并重命名副本事件?还是复制,截断然后复制回来?如果您能承受一点停机时间,这可能是最快的方法。

于 2009-05-14T17:50:33.710 回答