避免在截断表时阻止其他查询

时间:2018-08-27 19:43:57

标签: sql-server

这是我的问题。假设我有一个长期运行的SELECT查询(#1):

select * from table1 -- assume this runs for a long time

它正在运行时,我从同一张表中运行另一个SELECT(#2)。它并行运行并在一秒钟内完成:

select top 1 * from table1

因此#2未被#1阻止。

现在,假设我要运行#3,即截断并重新加载table1:

begin tran
   truncate table table1

   insert into table1
   select * from table2
commit

#3被#1阻止,必须等待,这是可以理解的。但是,它也会将TABLOCK放在table1上,并且#2都不能运行。基本上#2被#1阻止了。

问题:是否有一种方法可以不阻止其他查询的方式运行#3? 我希望看到#3等待#1,但是#2仍然可以运行。

我试图在运行#3之前检查锁,但是在sys.dm_tran_locks视图中看不到任何锁。我还能在其他地方看到它们吗?

3 个答案:

答案 0 :(得分:0)

我已经有一段时间没有这样做了,但是您可以这样尝试:

begin tran
   CREATE TABLE table1_new <(identical to table1, incl. indexes, keys and triggers)>

   insert into table1_new
   select * from table2

   drop table table1

   RENAME N'table1_new', N'table1', N'OBJECT'
commit

您可能需要尝试更高的隔离级别,以确保您的用户不会遇到任何“找不到对象”错误。

答案 1 :(得分:0)

好的,我找到了解决方案。不知道为什么以前对我不起作用,但是可以使用sys.dm_tran_locks视图。

基本上,我一直等到表上没有锁,然后开始截断/重新加载。

begin tran

--wait until there are no locks on the table
while exists(
    select 1 from sys.dm_tran_locks
    where resource_database_id = DB_ID()
    and resource_associated_entity_id = OBJECT_ID(N'dbo.table1')
)
begin
    waitfor delay '00:00:05'
end

truncate table table1

insert into table1
select * from table2

commit

答案 2 :(得分:0)

我的一位同事建议了另一个选项,我最终使用了该选项:在运行TRUNCATE之前设置LOCK_TIMEOUT。

如果它不能锁定表,它只会失败。 (当然,应该有一些机制可以稍后再运行)

SET LOCK_TIMEOUT 0
TRUNCATE TABLE table1

希望它可以帮助某人。