如果我停止长时间运行的查询,它会回滚吗?

时间:2008-10-02 12:13:50

标签: sql sql-server duplicate-data

用于循环 17百万条记录以删除重复项的查询现已运行约 16小时,我想知道查询是否已停止正确现在,如果它将完成删除语句或在运行此查询时是否已删除?实际上,如果我确实停止它,它是否最终确定删除或回滚?

我发现当我做

 select count(*) from myTable

它返回的行(在执行此查询时)比起始行计数少约5。显然服务器资源非常差,所以这意味着这个过程需要16个小时才能找到5个重复项(实际上有数千个),这可能会运行几天?

这个查询在2000行测试数据上耗时6秒,并且在这组数据上效果很好,所以我认为整套需要15个小时。

有什么想法吗?

以下是查询:

--Declare the looping variable
DECLARE @LoopVar char(10)


    DECLARE
     --Set private variables that will be used throughout
      @long DECIMAL,
      @lat DECIMAL,
      @phoneNumber char(10),
      @businessname varchar(64),
      @winner char(10)

    SET @LoopVar = (SELECT MIN(RecordID) FROM MyTable)

    WHILE @LoopVar is not null
    BEGIN

      --initialize the private variables (essentially this is a .ctor)
      SELECT 
        @long = null,
        @lat = null,
        @businessname = null,
        @phoneNumber = null,
        @winner = null

      -- load data from the row declared when setting @LoopVar  
      SELECT
        @long = longitude,
        @lat = latitude,
        @businessname = BusinessName,
        @phoneNumber = Phone
      FROM MyTable
      WHERE RecordID = @LoopVar

      --find the winning row with that data. The winning row means 
      SELECT top 1 @Winner = RecordID
      FROM MyTable
      WHERE @long = longitude
        AND @lat = latitude
        AND @businessname = BusinessName
        AND @phoneNumber = Phone
      ORDER BY
        CASE WHEN webAddress is not null THEN 1 ELSE 2 END,
        CASE WHEN caption1 is not null THEN 1 ELSE 2 END,
        CASE WHEN caption2 is not null THEN 1 ELSE 2 END,
        RecordID

      --delete any losers.
      DELETE FROM MyTable
      WHERE @long = longitude
        AND @lat = latitude
        AND @businessname = BusinessName
        AND @phoneNumber = Phone
        AND @winner != RecordID

      -- prep the next loop value to go ahead and perform the next duplicate query.
      SET @LoopVar = (SELECT MIN(RecordID) 
    FROM MyTable
    WHERE @LoopVar < RecordID)
    END

12 个答案:

答案 0 :(得分:28)

不,如果停止查询执行,sql server将不会回滚它已经执行的删除。 oracle需要显式提交操作查询或者数据被回滚,但不是mssql。

使用sql server它将不会回滚,除非您在事务的上下文中专门运行并回滚该事务,或者在没有提交事务的情况下关闭连接。但我在上面的查询中没有看到交易上下文。

您还可以尝试重新构建查询以使删除效率更高一些,但基本上如果您的框的规格不符合要求,那么您可能会被迫等待它。

今后,您应该在桌面上创建一个唯一索引,以避免再次经历这个问题。

答案 1 :(得分:8)

您的查询未包含在事务中,因此它不会回滚各个删除语句已经发生的更改。

我使用以下查询在我自己的SQL Server上自行测试了这个,并且即使我取消了查询,ApplicationLog表也是空的:

declare @count int
select @count = 5
WHILE @count > 0
BEGIN
  print @count
  delete from applicationlog;
  waitfor time '20:00';
  select @count = @count -1
END

但是,您的查询可能需要数天或数周,比15小时更长。您估计每6秒处理2000条记录的错误是错误的,因为while循环中的每次迭代将花费1700万行显着更长的时间,而2000条行则需要更长的时间。因此,除非您的查询在2000行中花费的时间少于一秒,否则所有1700万行都需要几天。

您应该问一个关于如何有效删除重复行的新问题。

答案 2 :(得分:2)

如果您没有对事务做任何明确的事情,那么连接将处于autocommit transactions模式。在此模式下,每个SQL语句都被视为事务。

问题在于这是否意味着单个SQL语句是事务,因此在您执行时提交,或者外部WHILE循环是否算作事务。

MSDN上的WHILE构造的描述中似乎没有对此进行任何讨论。但是,由于WHILE语句不能直接修改数据库,因此启动自动提交事务似乎是合乎逻辑的。

答案 3 :(得分:2)

隐含交易

如果未设置“隐式事务”,则循环中的每次迭代都会提交更改。

可以使用“隐式事务”设置任何SQL Server。这是一个数据库设置(默认为OFF)。您还可以在Management Studio内的特定查询的属性中进行隐式事务(在查询窗格中单击右键&gt;选项),在客户端中使用默认设置或SET语句。

SET IMPLICIT_TRANSACTIONS ON;

无论哪种方式,如果是这种情况,无论查询执行中断如何,您仍然需要执行显式的COMMIT / ROLLBACK。


隐式交易参考:

http://msdn.microsoft.com/en-us/library/ms188317.aspx

http://msdn.microsoft.com/en-us/library/ms190230.aspx

答案 4 :(得分:1)

我继承了一个系统,它的逻辑与你在SQL中实现的一样。在我们的例子中,我们试图使用具有相似名称/地址等的模糊匹配将行链接在一起,并且该逻辑完全在SQL中完成。在我继承它的时候,我们在表格中有大约300,000行,根据时间计算,我们计算出它需要A YEAR来匹配它们。

作为一个实验,看看我能在SQL之外做多快,我写了一个程序将db表转储成平面文件,将平面文件读入C ++程序,构建我自己的索引,并做模糊那里的逻辑,然后将平面文件重新导入数据库。在C ++应用程序中花了大约30秒的时间在SQL中花了大约30秒。

所以,我的建议是,甚至不要尝试在SQL中做什么。导出,处理,重新导入。

答案 5 :(得分:1)

到目前为止执行的DELETES将不会回滚。


作为code in question的原作者,并且已发出警告表明性能取决于索引,我建议以下项目来加快速度。

RecordId最好是PRIMARY KEY。我不是指IDENTITY,我的意思是PRIMARY KEY。使用sp_help

确认

在评估此查询时应使用某些索引。弄清楚这四列中的哪一列具有最少的重复和索引......

SELECT *
FROM MyTable
WHERE @long = longitude
  AND @lat = latitude
  AND @businessname = BusinessName
  AND @phoneNumber = Phone

添加此索引之前和之后,请检查查询计划以查看是否已添加索引扫描。

答案 6 :(得分:0)

作为一个循环,即使使用适当的索引,您的查询也难以很好地扩展。根据{{​​3}}中的建议,应该将查询重写为单个语句。

如果您没有在事务中显式运行它,它将只回滚执行语句。

答案 7 :(得分:0)

我认为如果使用游标使用单遍算法重写它,这个查询会更有效率。您可以按经度,纬度,BusinessName和@phoneNumber命令光标表。你可以逐步浏览一行。如果一行具有与上一行相同的经度,纬度,商家名和电话号码,则将其删除。

答案 8 :(得分:0)

我认为你需要认真考虑你的方法论。  你需要开始思考集合(虽然为了性能你可能需要批处理,但不是逐行对1700万记录表。)

首先,你的所有记录都有重复吗?我怀疑没有,所以你要做的第一件事就是将处理限制在只有那些有重复的记录上。由于这是一个大表,您可能需要随着时间的推移批量删除,具体取决于正在进行的其他处理,您首先将要处理的记录拉到自己的索引表中。你也可以使用一个临时表,如果你能够同时完成这一切而不停止它,那么在数据库中创建一个表并在最后删除。

类似的东西(注意我没有写下创建索引的声明,我想你可以自己看一下):

SELECT min(m.RecordID), m.longitude, m.latitude, m.businessname, m.phone  
     into  #RecordsToKeep    
FROM MyTable   m
join 
(select longitude, latitude, businessname, phone
from MyTable
group by longitude, latitude, businessname, phone
having count(*) >1) a 
on a.longitude = m.longitude and a.latitude = m.latitude and
a.businessname = b.businessname and a.phone = b.phone 
group by  m.longitude, m.latitude, m.businessname, m.phone   
ORDER BY CASE WHEN m.webAddress is not null THEN 1 ELSE 2 END,        
    CASE WHEN m.caption1 is not null THEN 1 ELSE 2 END,        
    CASE WHEN m.caption2 is not null THEN 1 ELSE 2 END



while (select count(*) from #RecordsToKeep) > 0
begin
select top 1000 * 
into #Batch
from #RecordsToKeep

Delete m
from mytable m
join #Batch b 
        on b.longitude = m.longitude and b.latitude = m.latitude and
        b.businessname = b.businessname and b.phone = b.phone 
where r.recordid <> b.recordID

Delete r
from  #RecordsToKeep r
join #Batch b on r.recordid = b.recordid

end

Delete m
from mytable m
join #RecordsToKeep r 
        on r.longitude = m.longitude and r.latitude = m.latitude and
        r.businessname = b.businessname and r.phone = b.phone 
where r.recordid <> m.recordID

答案 9 :(得分:0)

还尝试考虑另一种删除重复行的方法:

delete t1 from table1 as t1 where exists (
    select * from table1 as t2 where
        t1.column1=t2.column1 and
        t1.column2=t2.column2 and
        t1.column3=t2.column3 and
        --add other colums if any
        t1.id>t2.id
)

我想你的表中有一个整数id列。

答案 10 :(得分:0)

如果您的计算机没有非常高级的硬件,则可能需要很长时间才能完成该命令。我不确定这项操作是如何在幕后执行的,但根据我的经验,这可以通过将记录从数据库中带入内存以获得更高效的程序,使用具有删除重复规则的树结构的程序用于插入。尝试使用ODBC将chuncks中的整个表(一次说10000行)读入C ++程序。一旦进入C ++程序,使用和std :: map其中key是唯一键,struct是一个结构,用于将其余数据保存在变量中。循环遍历所有记录并执行插入到地图中。地图插入功能将处理删除重复项。由于在地图内搜索是lg(n)时间,因此找到重复项的时间远远少于使用while循环。然后,您可以删除整个表,并通过形成插入查询并通过odbc执行它们或构建文本文件脚本并在管理工作室中运行它,从地图中将元组添加回数据库。

答案 11 :(得分:-1)

我很确定这是一种否定的。否则交易点会是什么?