为什么用左连接做四个简单的选择而不是一个选择更快?

Ars*_*nko 2 performance sql-server execution-plan sql-server-2012 query-performance

我需要从A包含三个外键的表中找到一个值到另外两个表BC.

为了实验,我测试了两种查询值的方法:

多个查询:

declare @start int = (select top 1 [Id] from [B] where [Day] = '2015-01-01')
declare @end int = (select top 1 [Id] from [B] where [Day] = '2017-06-14')
declare @category int = (select top 1 [Id] from [C] where [Title] = 'Hello, World!')

select top 1 [Name]
from [A]
where [StartId] = @start
    and [EndId] = @end
    and [CategoryId] = @category
    and [Day] = '2016-05-27'
Run Code Online (Sandbox Code Playgroud)

单个查询:

select top 1 [Name]
from [A]
left join [B] as [BStart] on [BStart].[Id] = [A].[StartId]
left join [B] as [BEnd] on [BEnd].[Id] = [A].[EndId]
left join [C] on [C].[Id] = [A].[CategoryId]
where [BStart].[Day] = '2015-01-01'
    and [BEnd].[Day] = '2017-06-14'
    and [C].[Title] = 'Hello, World!'
    and [A].[Day] = '2016-05-27'
Run Code Online (Sandbox Code Playgroud)

我很惊讶执行计划表明单个查询比多个查询更昂贵。五个selects 一起做时,有left joins 的那个表示 53%。其他四个查询各占 12%。

这些是执行计划:

declare @start int = (select top 1 [Id] from [B] where [Day] = '2015-01-01')`
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

declare @end int = (select top 1 [Id] from [B] where [Day] = '2017-06-14')
Run Code Online (Sandbox Code Playgroud)

(下同)

declare @category int = (select top 1 [Id] from [C] where [Title] = 'Hello, World!')
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

select top 1 [Name]
from [A]
where [StartId] = @start
    and [EndId] = @end
    and [CategoryId] = @category
    and [Day] = '2016-05-27'
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

select top 1 [Name]
from [A]
left join [B] as [BStart] on [BStart].[Id] = [A].[StartId]
left join [B] as [BEnd] on [BEnd].[Id] = [A].[EndId]
left join [C] on [C].[Id] = [A].[CategoryId]
where [BStart].[Day] = '2015-01-01'
    and [BEnd].[Day] = '2017-06-14'
    and [C].[Title] = 'Hello, World!'
    and [A].[Day] = '2016-05-27'
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

为什么带有left joins的单个查询比第一种方法慢?

sev*_*ist 8

您的“单个查询”执行计划表明,预先计算 StartID、Category 等允许在 A 上有效使用索引,直接“寻找”您想要的记录(您有一个非聚集索引寻找在您的查询计划中),确定了要搜索的给定类别等。

另一方面,带有 JOIN 的“单一查询”需要在返回与您的条件匹配的“前 1”结果之前“即时”执行表 ABC 之间的连接。此连接将涉及那些与其他相关表匹配的那些表的所有记录(全聚集索引扫描)。

如果您经常像这样按 Day 和 Title 进行查询,则似乎缺少一个索引(或统计数据已过期),允许按这些条件搜索表 B 和 C。

顺便说一句,如果您按某些条件“排序”(或者如果您的搜索语句是唯一的),Top 1 只会给出确定性结果 - 否则您将获得查询的第一行,这可能会或可能不会一致运行它的场合。