【问题标题】:weird execution plan of a SQL Server querySQL Server 查询的奇怪执行计划
【发布时间】:2011-08-23 18:50:53
【问题描述】:

上下文:SQL Server 2008。内部连接有 2 个表。 事实表有 4000 万行,包含患者键和所用药物以及其他事实。药物键和患者键按该顺序组合有一个唯一索引(非聚集)。 维度表是药物清单(70 行)。 加入是根据用药键(代理键)获取用药码(业务码)。 查询:

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

问题:为什么它会尝试使用索引并沿着低效路径走下去?

【问题讨论】:

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

标签: sql-server indexing sql-execution-plan


【解决方案1】:

您需要将SomeFact 添加为tblFact 索引上的INCLUDE 以使其成为covering

目前,该表将被访问两次:一次用于索引,然后再次进行查找以获取 SomeFact 作为 RID 或键查找(取决于是否存在聚集索引)

这不适用于tblDIM,因为我假设MKey 是聚集索引,它可以隐式覆盖

【讨论】:

  • +1 - 我敢肯定他仍然需要为聚集索引拉取所有页面
  • 与当前执行计划很好,索引被访问了70次(等于dim表中的行数)。有键查找(聚集索引)。我不愿让索引完全覆盖,因为事实列因查询而异。
  • @vuht2000: 三个选项: 1. 没有索引,每个查询将花费 1:40 2. 使 (Mkey, Pkey) 聚集索引 3. 使当前索引覆盖。您的选择...
  • @gbn:谢谢。我将 Mkey 移动到索引中的第二列,它按预期工作。但是,我仍然无法避免想知道为什么 SQL Server 通过一个相当简单的查询做出了如此糟糕的选择。同意查询是否有过滤器,比如针对特定患者,那么使用索引是有意义的。但 SQL Server 清楚地知道有或没有过滤器的预期行数。
  • @vuht2000:使用 MKey 2nd 我怀疑它没有使用索引,因为它没用:它与没有索引相同(比较计划),但只是占用磁盘空间
【解决方案2】:

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

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

【讨论】:

【解决方案3】:

您的统计数据是最新的吗?检查:

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;

【讨论】:

  • OP 说“索引是在运行之前重建的”:索引统计信息总是由索引重建更新(但不是列索引)。基本上,tblFact 索引没有覆盖
猜你喜欢
  • 1970-01-01
  • 2020-04-30
  • 2011-10-13
  • 1970-01-01
  • 2017-07-03
  • 1970-01-01
  • 1970-01-01
  • 2020-10-13
  • 1970-01-01
相关资源
最近更新 更多