SQL Server更新查询不使用索引

时间:2010-08-19 18:13:27

标签: sql-server performance indexing

我有一个运行缓慢的更新查询(请参阅下面的第一个查询)。我在PhoneStatus表和PhoneID列上创建了一个名为IX_PhoneStatus_PhoneID的索引。 Table PhoneStatus包含2000万条记录。当我运行以下查询时,不使用索引并使用聚簇索引扫描,反过来更新运行缓慢。

UPDATE PhoneStatus    
SET RecordEndDate = GETDATE()    
WHERE PhoneID = 126  

如果我执行以下查询(包括新的FROM),我仍然会遇到与未使用的索引相同的问题。

UPDATE PhoneStatus    
SET RecordEndDate = GETDATE()   
FROM Cust_Profile.PhoneStatus     
WHERE PhoneID = 126 

但是,如果我添加HINT以强制在FROM上使用索引,它就可以正常工作,并且会使用Index Seek。

UPDATE PhoneStatus     
SET RecordEndDate = GETDATE()   
FROM Cust_Profile.PhoneStatus WITH(INDEX(IX_PhoneStatus_PhoneID))   
WHERE PhoneID = 126    

有谁知道为什么第一个查询不会使用索引?

更新

在包含2000万条记录的表中,每个phoneID最多可以显示10次

BarDev

5 个答案:

答案 0 :(得分:5)

20M表中有多少个不同的PhoneID?如果条件where PhoneID=126不够有选择性,您可能会点击索引tipping point。如果此查询和访问条件非常频繁,则PhoneID是群集索引最左侧密钥的良好候选。

答案 1 :(得分:2)

看看Is an index seek always better or faster than an index scan? 有时搜索和扫描将完全相同。

索引可能会被忽略,因为您的统计信息可能过时或索引的selectivity太低而SQL Server认为扫描会更好

启用统计信息,查看有和没有搜索的查询之间是否存在任何差异

SET STATISTICS io ON
UPDATE PhoneStatus
SET RecordEndDate = GETDATE()
WHERE PhoneID = 126


UPDATE PhoneStatus
SET RecordEndDate = GETDATE()
FROM Cust_Profile.PhoneStatus WITH(INDEX(IX_PhoneStatus_PhoneID))
WHERE PhoneID = 126

现在看一下回来的读物

答案 2 :(得分:2)

Pablo是正确的,只有当SQL Server认为这将更有效地运行查询时,它才会使用索引。但是有2000万行应该知道使用索引。我想你只需要更新数据库的统计数据。

请填写更多信息,请参阅http://msdn.microsoft.com/en-us/library/aa260645(SQL.80).aspx

答案 3 :(得分:1)

SQLServer(或任何其他SQL Server产品),如果没有强制使用任何索引。它将使用它,如果它认为将有助于更有效地运行查询。

因此,在您的情况下,SQLServer认为它不需要使用IX_PhoneStatus_PhoneID,并且通过使用其聚簇索引可能会获得更好的结果。但这可能是错误的,这就是索引提示的含义:让服务器知道它可以通过使用其他索引做得更好。

如果最近创建并填充了您的表,则可能是统计数据有些过时的情况。所以你可能想要强制statistic update

答案 4 :(得分:0)

重申:

  • 你有桌子PhoneStatus
  • 使用聚集索引
  • 列PhoneStatus和PhoneId上的非聚集索引,按此顺序
  • 您正在发布“... WHERE PhoneId = 126”
  • 的更新
  • 表中有2000万行(即它很大,然后是一些)

SQL将接受您的查询并尝试找出如何在不进行操作的情况下完成工作 整个桌子。对于非聚集索引,数据可能如下所示:

PhoneStatus  PhoneID
  A            124
  A            125
  A            126
  B            127
  C            128
  C            129
  C            130
 etc.

问题是,SQL将首先检查第一列,之前检查值 第二栏由于更新中未指定第一列,因此SQL 不能通过索引搜索树“快捷”到相关条目,因此必须扫描整个表。 (不,SQL不够聪明,不能说“呃,我只是检查一下 第二列首先“,是的,他们这样做是对的。”

由于非聚集索引不会使查询更快,因此默认为表 扫描 - 并且由于存在聚簇索引,这意味着它将成为聚簇索引扫描。 (如果聚集索引在PhoneId上,那么您的查询会有最佳性能,但我猜这不是这种情况。)

使用提示时,会强制使用非聚集索引,这样会更快 而不是全表扫描如果该表的列数远多于索引(其中 基本上只有两个),因为要筛选的数据要少得多。