2010-02-15 114 views
3

我目前正在从一个数据库提取表到另一个数据库的SSIS包。两个数据库中的表都使用与主键相同的列。我选择语句来提取数据是一个简单的选择语句。当我运行包时,我收到一个错误,那里有重复的主键值。SQL Server查询返回多行

我回顾了我的select语句并验证了我的select语句没有返回重复的行。因此,为了测试这一点,我从表格中删除了主键,并将数据插入到SSIS包中。它运行后,我看着桌子,看看哪些行被复制。我发现,在提取器在被复制的地方进行编辑时,在编辑之前有一行记录,在编辑之后记录了它。我可以很容易地告诉它,因为表中有一个最后修改的字段,每次更新记录时都会更新。

我在我的select语句中添加了一个NOLOCK提示,并且它停止返回重复的行。

所以我的问题是为什么?我会预期,带有NOLOCK表提示的select语句将返回重复行的机会更大,因为它没有使用锁定,并且没有NOLOCK提示的select语句应该使用锁定来确保它不返回重复行。

这是我用来选择数据的select语句。我没有验证连接并不导致其重复的行:

SELECT pe.enc_id, 
     pe.enc_nbr, 
     pe.billable_ind, 
     pe.clinical_ind AS clinical_ind, 
     pe.budget_ind, 
     pe.print_stmt_ind, 
     pe.send_coll_letter_ind, 
     pe.outsource_exempt_ind, 
     cb.First_name + ' ' + cb.last_name AS CreatedBy, 
     pe.create_timestamp AS create_timestamp, 
     mb.first_name + ' ' + mb.last_name AS ModifiedBy, 
     pe.modify_timestamp AS modify_timestamp 
FROM patient_encounter pe WITH(NOLOCK) 
     LEFT OUTER JOIN user_mstr cb WITH(NOLOCK) ON 
      pe.created_by = cb.user_id 
     LEFT OUTER JOIN user_mstr mb WITH(NOLOCK) ON 
      pe.modified_by = mb.user_id 

回答

0

的使用NOLOCK提示只是告诉数据库服务器忽略了锁,并从数据库中只选择当前值 - 因此它只是选择了所有当前行的值在它到达该行时。

请注意,您不会在正在更新的行的新表中获取更新。

没有看到你的SQL,我猜测它的构造方式,它抓住当前行,等待锁定清除,然后选择新行。

锁定整个表格将防止更改/重复,但您可能会在选择时锁定每个人不在表格中。

编辑: FYI备选方案:使用由其他进程锁定READPAST-行被跳过,在表级别当然会阻塞其他进程,并且可能不期望 TABLOCK锁。

注意:在事务写入期间UPDLOCK被转换为XLOCK。

提示有两个类别:粒度和隔离级别。粒度包括PAGLOCK,NOLOCK,ROWLOCK和TABLOCK。隔离级别提示包括HOLDLOCK,NOLOCK,READCOMMITTED,REPEATABLEREAD和SERIALIZABLE。每组最多可以使用一个。

EDIT2:只是为了完整性:READCOMMITTED-只从已提交的事务中读取数据。这是默认行为。编辑3:更多信息:NOLOCK将读取行,但是如果ROLLBACK发生在可能影响所选集的准确性的事务处理中,则会冒着读取“脏”数据的风险,这些数据将不会存在或具有不存在的数据。

其他重要信息是发现交易正在使用什么类型的锁,以便您可以相应地进行规划。

+0

感谢您的信息。我在帖子中添加了我正在使用的select语句。 为什么要抢两行?第一次读它之后,为什么更改记录会导致它再次读取它? –

+0

请记住,即使您不使用NOLOCK,服务器也可以覆盖“提示”,但它可以在内部使用,因此可以使用重复的行。 –

+0

伟大的观点,谢谢。无论如何要告诉内部使用什么? –

1

NOLOCK提示会导致脏读取异常,而其中一种异常是重复读取。这样的读取十分频繁,如果更新更改了行通过查询扫描索引的位置:

  • 说你在表2行,有一个ID键,行与关键值1和2
  • 一个请求(T1)运行UPDATE表SET key = 3 WHERE key = 1;
  • 第二个请求(T2)运行SELECT ... FROM WITH WITH(NOLOCK);
  • T1锁定与键值的行1
  • T2忽略锁定T1具有和与关键值1
  • T2继续读取的行和与关键值读取行2
  • T1更新该行,并且排在他新的键值位置3
  • T2继续扫描索引INT移动,并与键值3

所以选择了读取的行两次,一次同时有钥匙读取行值1,一次它有一个关键值3.这只是一个会发生什么的微不足道的例子。实际上,更复杂的查询可以运行复杂的计划并使用其他索引,所有索引都受到这种异常情况的影响。

总之:NOLOCK暗示是邪恶的。如果您想避免争用,请使用snapshot isolation

+0

感谢您的回复,以及NOLOCK的绝佳解释。令我困惑的不是当我使用NOLOCK时发生这种情况,而是当我不使用NOLOCK时发生这种情况。主键列没有被更改,记录有更新的非ID字段,并且当我不使用NOLOCK时它正在被读取两次。 –

+0

对不起,在对角线阅读您的问题,实际上完全忽略了这一点。 –

+0

您能否显示在提取过程中发生的编辑示例? –