为什么sql server更喜欢索引扫描而不是查找?

Ing*_*ond 0 sql-server execution-plan

我的桌子是这样的:

    CREATE TABLE [dbo].[ClosedTaskCustomFields](
    [ClosedTaskId] [uniqueidentifier] NOT NULL,
    [CustomFieldId] [uniqueidentifier] NOT NULL,
    [Value] [nvarchar](450) NULL
    ... 
 CONSTRAINT [PK_ClosedTaskCustomFields] PRIMARY KEY CLUSTERED 
(
    [ClosedTaskId] ASC,
    [CustomFieldId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
Run Code Online (Sandbox Code Playgroud)

我也有这样的索引用于连接 ClosedTask 表:

CREATE NONCLUSTERED INDEX [IX_ClosedTaskCustomFields_ClosedTaskId] ON [dbo].[ClosedTaskCustomFields]
(
    [ClosedTaskId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
GO

Run Code Online (Sandbox Code Playgroud)

但我不明白为什么这样的查询会读取所有 ClosedTaskCustomFields 表以加入 ClosedTask(如果我有索引)。

      select count(1) from ClosedTaskCustomFields ctcf
      join ClosedTask c on c.id = ctcf.ClosedTaskId
      where c.State = 'Rejected'
-- Result count is 50k.
Run Code Online (Sandbox Code Playgroud)

计划: https: //www.brentozar.com/pastetheplan/? id=SJ9Y4xFEp 在此输入图像描述

Pau*_*ite 8

SQL Server 根据其估计选择看起来最便宜的计划。这意味着您想要的计划(在两个表上都进行索引查找)的成本会更高。您可以使用提示自行测试这一点FORCESEEK

\n

请注意,较高的估计成本并不一定意味着该计划的执行速度会比在特定硬件上选择的替代方案慢。

\n

该计划成本较低的原因有很多:

\n
    \n
  1. 优化器假设所有查询在缓存中没有数据的情况下开始。
  2. \n
  3. 顺序 I/O 的成本低于随机 I/O。
  4. \n
  5. 假设对 B 树的搜索很大程度上是随机的,重复搜索时会不断减少。这试图考虑触摸先前从持久存储带入内存以用于同一查询的页面的可能性。
  6. \n
  7. 使用并行性时,SQL Server 不会降低嵌套循环连接内侧的成本。这往往不利于并行嵌套循环计划。
  8. \n
  9. 您的计划具有位图。这用于拒绝在扫描期间无法连接的行。这就是扫描涉及 1,169,875 行但仅返回 51,307 行(所有执行的实际行数)的原因。
  10. \n
  11. 在可用连接类型中,哈希连接的每行处理成本最低。构建哈希表的额外成本被这个好处以及位图提供的好处所抵消(嵌套循环不可用)。
  12. \n
\n

您可能会发现优化器自然会选择带有提示的搜索计划,或者通常会选择MAXDOP 1更高的并行成本阈值。似乎您已将其设置为默认值 5,如今许多人认为该值太低了。

\n

否则,您将只能使用诸如 之类的提示来覆盖优化器选择\xe2\x80\x94 的正常方法FORCESEEK。除非查询至关重要并且节省几毫秒的时间很重要,否则我不会打扰。

\n

有关行模式位图的更多信息,请参阅我的文章Bitmap Magic(或\xe2\x80\xa6 SQL Server 如何使用位图过滤器)。

\n