2011-08-23 56 views
2

上下文:SQL Server 2008.内部连接有2个表。 拥有4000万行的事实表包含患者密钥和所服用的药物以及其他事实。药物密钥和患者密钥在该顺序中有一个独特的索引(非聚集)。 维度表是药物清单(70行)。 加入是基于药物密钥(代理键)获得药物代码(商业代码)。 查询:SQL Server查询的奇怪执行计划

SELECT a.PKey, a.SomeFact, b.MCode 
FROM tblFact a 
JOIN tblDIM b ON a.MKey = b.MKey 

所有返回的列是整数。 上述查询在7分钟内运行,其执行计划显示使用(MKey,PKey)上的索引。该指数在运行前重建。 当我禁用事实表上的索引(或将数据复制到具有相同结构但没有索引的新表)时,相同的查询仅需要1:40分钟。

IO统计数据也令人惊叹。

有索引:表'tblFACT'。扫描计数70,逻辑读取190296338,物理读取685138,预读读取98713

没有索引:表'tblFACT_copy'。扫描计数17,逻辑读取468891,物理读取0,预读读取419768

问题:为什么它会尝试使用索引并降低效率低下的路径?

+1

我们可以看到索引定义和执行计划吗?它是否必须对您的索引执行RID_LOOKUP? –

回答

0

在极少数情况下,数据库选择不正确的执行计划。在这种情况下,索引用于连接,但由于所有数据都是从两个表中提取的,因此只扫描整个表会更快。 如果将WHERE子句添加到查询中,索引版本会更快,因为如果没有索引,它仍然需要扫描整个表格,而不是只抓取它需要的一小部分记录。

可能有一些指令鼓励数据库不使用索引或使用不同的索引,但我不太了解SQL服务器。

+0

只是FYI,'FROM YourTable WITH(INDEX(0))'强制执行表扫描 – Andomar

+0

@Andomar:谢谢,好的技巧 – vuht2000

+0

而在SQL Server 2008 R2 SP1和Denali中,可以使用FORCESCAN强制扫描任何索引 - 尽管可能会有太多的非学术案例可能会有用。根据你的口音,在拥挤的演示厅里大声说出这个词也可能很幽默。 :-)请参阅http://sqlblog.com/blogs/aaron_bertrand/archive/2011/04/22/sql-server-2008-r2-sp1-ctp-is-now-available.aspx和http:// msdn。 microsoft.com/en-us/library/ms187373%28SQL.110%29.aspx –

0

你的统计信息是最新的?请与:

SELECT object_name = Object_Name(ind.object_id) 
,  IndexName = ind.name 
,  StatisticsDate = STATS_DATE(ind.object_id, ind.index_id) 
FROM SYS.INDEXES ind 
order by 
     STATS_DATE(ind.object_id, ind.index_id) desc 

的更新:

exec sp_updatestats; 
+0

OP表示“索引在运行之前就重建了”:索引统计信息总是通过索引重建进行更新(但不是列索引)。基本上,tblFact索引不包括 – gbn

1

您需要添加SomeFact为包括关于tblFact指数,使其covering

目前,该表将被访问两次:一次是为索引,然后再进行查找,以得到SomeFact无论是作为一个RID或键查找(取决于是否有一个聚集索引)

这没有按” t适用于tblDIM,因为我认为MKey是隐式涵盖的聚集索引

+0

+1 - 我相信他仍然需要将聚簇索引 – JNK

+0

的所有页面与当前执行计划完美结合,访问该索引70次(等于该行中的行数昏暗的桌子)。有关键查找(聚集索引)。我不愿意让索引完全覆盖,因为事实列因查询而异。 – vuht2000

+0

@ vuht2000:三个选项:1.没有索引,每个查询都会采用1:40 2.使(Mkey,Pkey)聚集索引3.使当前索引覆盖。您的选择... – gbn