我的查询基本上是这样的:
Select *
From UserSearches us
left outer join Quotes q on q.UserSearchId = us.Id and q.QuoteNumber is not null
left outer join ContainerDetails cd on cd.QuoteId = q.Id
left outer join Surcharges s on s.ContainerDetailId = cd.Id
where us.SearchDate between @beginDate and @endDate
给定@beginDate和@endDate的某些值,我有一个搜索需要30秒才能返回大约100K行。
最终目标是填充一些具有父子孩子关系的对象。经过一些实验,我发现我可以通过以下方式大大加快查询速度:
Select *
From UserSearches us
left outer join Quotes q on q.UserSearchId = us.Id and q.QuoteNumber is not null
left outer join ContainerDetails cd on cd.QuoteId = q.Id
where us.SearchDate between @beginDate and @endDate
Select cd.Id into #cdIds
From UserSearches us
left outer join Quotes q on q.UserSearchId = us.Id and q.QuoteNumber is not null
left outer join ContainerDetails cd on cd.QuoteId = q.Id
where us.SearchDate between @beginDate and @endDate
Select * From Surcharges s
inner join #cdIds on s.ContainerDetailId = #cdIds.Id
DROP TABLE #cdIds
这在10秒内运行,这对我没有意义。当然,首先加入附加费应该更快。
附加费表格包含以下索引:
PK:
ALTER TABLE [dbo].[Surcharges] ADD CONSTRAINT [PK_dbo.Surcharges] PRIMARY KEY CLUSTERED
(
[Id] ASC
)
IX1:
CREATE NONCLUSTERED INDEX [IX_Surcharge_ContainerDetailId] ON [dbo].[Surcharges]
(
[ContainerDetailId] ASC
)
INCLUDE ( [Id],
[Every],
[Single],
[Column],
[About],
[Twelve],
[Of],
[Them],
)
IX2:
CREATE NONCLUSTERED INDEX [IX_ContainerDetailId] ON [dbo].[Surcharges]
(
[ContainerDetailId] ASC
)
总而言之,为什么对我的附加费进行单独查询比在第一时间加入它们更快?
编辑:以下是执行计划。这些是可以在Sql Studio中打开的.sqlplan文件:
答案 0 :(得分:8)
了解实际执行计划的内容。
你会看到你的第一个变体在100,276行中有Actual Data Size
= 11,272 MB。
在第二个变体中,填充临时表的查询在19,665行中仅返回173KB。最后一个查询在87,510行中返回1,685 MB。
11,272 MB
远远超过 1,685 MB
难怪第一个查询速度较慢。
这种差异是由两个因素造成的:
在第一个版本中,您可以选择UserSearches
,Quotes
,ContainerDetails
表中的所有列。在第二个版本中,您只从ID
中选择ContainerDetails
。除了从磁盘读取和通过网络额外字节传输之外,这种差异导致了截然不同的计划。第二个变体不做排序,不进行密钥查找并使用哈希联接而不是嵌套循环。它在Quotes
上使用不同的索引。第二种变体使用ContainerDetails
上的索引扫描而不是搜索。
查询会产生不同数量的行,因为第一个变体使用LEFT JOIN
和第二个INNER JOIN
。
所以,要使它们具有可比性:
*
列表显式列出您需要的那些列。INNER JOIN
(或LEFT JOIN
)Surcharges
。<强>更新强>
您的问题是“为什么SQL Server会更快地运行第二个查询”,答案是:因为查询不同并且它们产生不同的结果(不同的行集,不同的列集)。
现在你要问另一个问题:如何使它们变得一样快速。
您的两个变体中哪一个产生了您想要的正确结果?我假设它是临时表的第二个变种。
请注意,我在这里没有回答如何让它们快速。我在这里回答如何让它们变得一样。
以下单个查询应该与使用临时表的第二个变量生成完全相同的结果,但没有显式临时表。我希望它的性能与你的第二个临时表类似。我故意用CTE写它来复制你的变体的结构与临时表,虽然很容易重写它没有。无论如何,优化器都足够聪明。
WITH
CTE
AS
(
Select cd.Id
From
UserSearches us
left outer join Quotes q on q.UserSearchId = us.Id and q.QuoteNumber is not null
left outer join ContainerDetails cd on cd.QuoteId = q.Id
where
us.SearchDate between @beginDate and @endDate
)
Select *
From
Surcharges s
inner join CTE on s.ContainerDetailId = CTE.Id
;
答案 1 :(得分:3)
好吧,我在查询本身以及计划中看到的内容似乎有两个很大的差异。
首先,可能最有影响力的是,在您使用临时表的第二个版本中,对Surcharges表的最终查询是INNER JOINing而不是您在原始查询中使用的LEFT JOIN运算符。我不确定哪个版本是准确的,但根据计划信息(第一个版本为1860万,第二个版本为510万),返回记录数量的差异似乎非常高。如果您将第一个版本更改为附加费表中的INNER JOIN,您是否在持续时间方面看到类似的结果?
其次,并且影响力可能较小,您的第二个版本会在批处理的select ... into部分中为您提供并行执行。没有看到更多的东西,我可能不敢评论为什么会这样,但它是一个潜在的差异化因素。
我从第一个撰稿人开始,看看你最终结束了什么,并从那里开始。
编辑:
为了帮助澄清这些评论,请尝试将您的第一个查询更改为此并附上查询计划/查看结果/持续时间与临时表/选择...到版本:
Select *
From UserSearches us
left outer join Quotes q on q.UserSearchId = us.Id and q.QuoteNumber is not null
left outer join ContainerDetails cd on cd.QuoteId = q.Id
INNER join Surcharges s on s.ContainerDetailId = cd.Id
where us.SearchDate between @beginDate and @endDate
这应该会给你一个与第二个版本差不多相似的持续时间 - 如果还没有,请附上该版本的查询计划。
答案 2 :(得分:1)
但你不是在比较苹果和苹果
第一个是左3
第二个是2个左边和1个内部连接
在第二个结果是分裂
试试这个 将@beginDate和@endDate之间的我们.SearchDate移动到连接中 我怀疑它正在进行大规模的连接并且过滤最后一次 让日期过滤器提前发生
color #FFF <- good
color ##FFF <- easy to miss, will cause very unhelpful 'eos' message at file end
快速搜索对我没有意义
那些左连接对此完全没有任何意义 左边做的就是返回cd.ID = null
Select *
From UserSearches us
left outer join Quotes q
on q.UserSearchId = us.Id
and q.QuoteNumber is not null
and us.SearchDate between @beginDate and @endDate
left outer join ContainerDetails cd
on cd.QuoteId = q.Id
left outer join Surcharges s
on s.ContainerDetailId = cd.Id
如果您只想要附加费
Select cd.Id into #cdIds
From UserSearches us
left outer join Quotes q on q.UserSearchId = us.Id and q.QuoteNumber is not null
left outer join ContainerDetails cd on cd.QuoteId = q.Id
where us.SearchDate between @beginDate and @endDate
答案 3 :(得分:0)
试试这个,
;With CTE as
(
Select us.Id
From UserSearches us
where us.SearchDate >= @beginDate1 and us.SearchDate <= @endDate1
)
Select us.id,q.col1,cd.col2
From CTE us
left outer join Quotes q on q.UserSearchId = us.Id and q.QuoteNumber is not null
left outer join ContainerDetails cd on cd.QuoteId = q.Id
left outer join Surcharges s on s.ContainerDetailId = cd.Id