是什么阻止“从TableName中选择前1 *与(nolock)”返回结果?

时间:2012-06-08 20:38:13

标签: sql sql-server-2008 locking

我目前正在运行以下声明

select * into adhoc..san_savedi from dps_san..savedi_record

这需要花费很长时间,而且我想看看它走了多远,所以我跑了这个:

select count(*) from adhoc..san_savedi with (nolock)

这没有及时返回任何内容,所以为此我做到了这一点:

select top 1 * from adhoc..san_savedi with (nolock)

即使这似乎无限期地运行。我可以理解,如果有数百万条记录计数(*)可能需要很长时间,但我不明白为什么选择前1条记录不会立即回来考虑我指定nolock。

在完全公开的名称中,dps_san是一个通过链接服务器从odbc连接中获取的视图。我不认为这会影响为什么我不能退回到第一排但只是把它扔到那里以防我错了。

所以我想知道是什么让该语句无法运行?

编辑:

正如我上面提到的,是的dps_san..savedi_record是一个视图。这是它的作用:

select * from DPS_SAN..root.SAVEDI_RECORD

它只不过是一个别名而且没有分组/排序等等所以我认为问题不在这里,但如果我错了,请赐教。

4 个答案:

答案 0 :(得分:11)

带有SELECT

NOLOCK查询实际上没有锁定,他们仍需要在表(and as it is a heap it will also take a hobt lock)上进行SCH-S(架构稳定性)锁定。

此外,在SELECT甚至可以开始之前,SQL Server必须编译该语句的计划,这也要求它在表上进行SCH-S锁定。

当您长时间运行的事务通过SELECT ... INTO创建表时,它会在语句完成之前保持一个不兼容的SCH-M锁。

您可以在阻止期间通过查看sys.dm_os_waiting_tasks 同时 来验证这一点。

当我在一个连接中尝试以下内容时

BEGIN TRAN

SELECT *
INTO NewT
FROM master..spt_values

/*Remember to rollback/commit this later*/

然后执行(或只是尝试查看估计的执行计划)

SELECT *
FROM NewT
WITH (NOLOCK)

在一秒钟内,阅读查询被阻止。

SELECT wait_type,
       resource_description
FROM sys.dm_os_waiting_tasks
WHERE session_id = <spid_of_waiting_task>

显示等待类型确实为SCH_S,阻止资源为SCH-M

wait_type        resource_description
---------------- -------------------------------------------------------------------------------------------------------------------------------
LCK_M_SCH_S      objectlock lockPartition=0 objid=461960722 subresource=FULL dbid=1 id=lock4a8a540 mode=Sch-M associatedObjectId=461960722

答案 1 :(得分:2)

很可能没有锁...如果dps_san..savedi_record是一个视图,那么它可能需要很长时间才能执行,因为它可能在不使用索引的情况下访问表,或者它可能排序数百万条记录,或任何原因。然后,您的查询,即使是简单的顶部或计数,也只能与该视图的执行速度一样快。

答案 2 :(得分:2)

这里要考虑几个问题。 dps_san..savedi_record是一个视图吗?如果是这样,可能需要很长时间才能获得您的数据。我能想到的另一件事是你试图使用select into语法创建一个临时表,这是一个坏主意。 select * into ...语法将在选择的持续时间内锁定tempdb。

如果您使用该语法创建表,则有一种解决方法。首先,通过在初始语句的末尾抛出where 1=0来创建表:

select * into ... from ... where 1=0

这将首先创建表(这是快速的),它允许您insert into,因为该表现在存在(在查询期间没有锁定tempdb的惩罚)。

答案 3 :(得分:2)

找到正在执行session_id的{​​{1}}:

select into

这应该让你知道另一个会话是否阻止了select into,或者它是否有一个导致问题的等待类型。

您可以在另一个窗口中为尝试执行选择的会话重复此过程。我怀疑马丁是对的,我之前关于架构锁定的评论是相关的。