Ars*_*nko 2 performance sql-server execution-plan sql-server-2012 query-performance
我需要从A包含三个外键的表中找到一个值到另外两个表B和C.
为了实验,我测试了两种查询值的方法:
多个查询:
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的单个查询比第一种方法慢?
您的“单个查询”执行计划表明,预先计算 StartID、Category 等允许在 A 上有效使用索引,直接“寻找”您想要的记录(您有一个非聚集索引寻找在您的查询计划中),确定了要搜索的给定类别等。
另一方面,带有 JOIN 的“单一查询”需要在返回与您的条件匹配的“前 1”结果之前“即时”执行表 ABC 之间的连接。此连接将涉及那些与其他相关表匹配的那些表的所有记录(全聚集索引扫描)。
如果您经常像这样按 Day 和 Title 进行查询,则似乎缺少一个索引(或统计数据已过期),允许按这些条件搜索表 B 和 C。
顺便说一句,如果您按某些条件“排序”(或者如果您的搜索语句是唯一的),Top 1 只会给出确定性结果 - 否则您将获得查询的第一行,这可能会或可能不会一致运行它的场合。