【问题标题】:Can I delete records from a sql server table in the middle of a while reader.read loop我可以在 while reader.read 循环的中间从 sql server 表中删除记录吗
【发布时间】:2012-02-22 18:23:17
【问题描述】:

我正在尝试使用 SQL 数据阅读器将记录从一个表移动到存档表中。我想逐个记录地进行传输(而不是批量插入和删除),这样如果发生错误,就会丢失最少的数据量(并且必须从备份中恢复)。

我的问题是,如果我在读取记录后删除记录,SQL 阅读器会起作用吗?这是迄今为止做我想做的最简单和直接的方法。它会打开意外错误吗?

While myReader.Read // select * from tableA where older than 1 year record
    insert the record into tableA_archive
    delete the record from tableA   
loop

这似乎有点类似于循环遍历您正在循环中修改的列表——我知道这会在 .net 中引起各种头痛

for aItem in listA
   if aItem fits criteria, send to list B and delete from listA
next

在读者阅读时是否有任何理由不修改基础表?例如,如果我在循环中间开始删除记录,读者是否会使用某些索引来访问可能会发生变化的记录?

【问题讨论】:

  • 为什么不只是DELETE FROM tableA OUTPUT deleted.* INTO tableA_archive WHERE Dt <= DATEADD(year,-1,getdate())
  • @MartinSmith 不错,不知道 DELETE INTO 语法。
  • 共识似乎是最好的方法是编写一个使用事务语句的 SQL 服务器存储过程。所以基本上我在事务中需要两条语句:将旧记录插入存档,从非存档中删除旧记录。事务语句将确保这两个语句都执行,或者两个语句都不执行。如果它在事务中遇到错误,则不会发生插入/删除。对吗?
  • 另外,您似乎需要在事务中捕获错误并指定回滚,否则它可能会继续运行sqlteam.com/article/introduction-to-transactions
  • @MartinSmith,这应该是一个答案。

标签: .net sql-server ado.net sqldatareader


【解决方案1】:

您应该避免将行一直传输到客户端,只是为了再次将它们重新上传回服务器。相反,做这样的事情(在同一个事务中):

INSERT INTO NEW_TABLE SELECT * FROM OLD_TABLE WHERE condititon;
DELETE FROM OLD_TABLE WHERE condititon;

甚至特定于 MS SQL Server:

DELETE FROM OLD_TABLE OUTPUT DELETED.* INTO NEW_TABLE WHERE condition;

(所有条件都一样)

如果您愿意,您甚至可以从您的 .NET 代码中执行这些语句,并且仍然保留下载所有行的好处。

【讨论】:

  • 感谢布兰科(和@MartinSmith)。沿着这些思路的查询在测试环境中完美运行。但是,我想安全地运行它。鉴于它只是一个语句,我应该在事务语句中运行它吗?如果遇到错误,SQL Server 会回滚它以使其永远不会运行吗?
  • @akh2103 自尊的 DBMS(包括 MS SQL Server)不会让您在事务之外执行任何操作。即使您没有显式启动事务,事务也会为您隐式启动(如果出现问题,甚至由您的 ADO.NET 提供程序自动回滚)。所以是的,即使你没有明确地将 DELETE FROM OLD_TABLE OUTPUT ... 包装到事务中,你也会得到“全有或全无”的行为。
【解决方案2】:

您应该花一些时间阅读transactional isolation,特别是可重复阅读。

在 SQL Server 中,选择语句(支持您的阅读器)在 READ COMMITTED 隔离中执行(默认情况下),这意味着您的删除命令将能够在读取行后删除它们,但 阅读器读取的行不能在 select 语句执行时被删除语句修改。

无论如何,将数据从一个表移动到另一个表通常应该在 SQL 中完成,而不是在 .NET 中。

【讨论】:

    【解决方案3】:

    这有几种方法,首先你要了解什么是事务隔离 在 SQL Server 中,什么时候需要使用它。

    在您的情况下,似乎您正在将历史数据移动到另一个表,因此在事务范围内使用 SQL 存储过程(带有回滚传输)是值得的。

    【讨论】:

      【解决方案4】:

      您应该在事务中执行此操作。即使您也可以在代码中执行此操作,这也是一个非常好的 SP 候选者。您将必须找出导致大量行的条件(不是 1,但可能是 100 或 1000 或 10000)。试验并找出在合理的时间内(可能是几秒钟)在一个语句中可以插入和删除多少个。

      SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
      
      BEGIN TRAN
          INSERT INTO tableA_archive (col1, col2)
              SELECT col1, col2 FROM tableA WHERE (SomeCondition)
          DELETE FROM tableA  WHERE (SomeCondition)
      COMMIT TRAN
      

      SomeCondition 可以是日期范围(例如一天、一小时等)。

      然后在更改 SomeCondition 的循环中运行此过程,直到没有要备份的内容。由于这是在事务中,因此插入和删除都会成功或失败。

      不管你想删除多少个,如果你担心一致性,你必须在一个事务中进行。

      【讨论】:

      • 您应该为此技术指定正确的隔离级别(可重复读取)。默认值 (READ COMMITTED) 可能导致 SELECT 语句检索与 DELETE 语句不同的行。
      猜你喜欢
      • 1970-01-01
      • 2021-12-30
      • 2018-12-01
      • 1970-01-01
      • 2018-08-07
      • 1970-01-01
      • 2017-04-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多