Sql 为什么外部联接比单独查询慢
我有一个查询,基本上如下所示:Sql 为什么外部联接比单独查询慢,sql,sql-server,query-optimization,sql-server-2014,Sql,Sql Server,Query Optimization,Sql Server 2014,我有一个查询,基本上如下所示: 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
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秒才能返回大约10万行的搜索
最终目标是填充一些具有父子关系的对象。因此,经过一些实验,我发现我可以通过以下方法大大加快查询速度:
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秒钟,这对我来说毫无意义。当然,首先加入附加费应该更快
附加费表具有以下索引:
主键:
IX1:
IX2:
总而言之,为什么单独查询我的附加费要比一开始就加入它们更快
编辑:以下是执行计划。以下是可以在Sql Studio中打开的.sqlplan文件:
我在查询本身和计划中看到的差异似乎有两大方面 首先,可能是最有影响的,在使用临时表的第二个版本中,对附加费表的最终查询是内部联接,而不是原始查询中使用的左联接运算符。我不确定哪个版本是准确的,但根据计划信息,返回记录的数量差异似乎很大(第一个版本为1860万条,第二个版本为510万条)。如果在“附加费”表中将第一个版本更改为内部联接,在持续时间方面是否会看到类似的结果 其次,可能影响较小的是,您的第二个版本在批处理的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
希望这会给您一个与第二个版本大致相似的持续时间-如果仍然没有,请附上该版本的查询计划。但您不是在对苹果进行比较
第一个是3左
第二个是2个左连接和1个内连接
在第二种情况下,结果是分裂的 试试这个
将us.SearchDate在@beginDate和@endDate之间向上移动到连接中
我怀疑它正在进行大规模的连接,而最后的过滤
让日期过滤器尽早出现
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
快速搜索对我来说毫无意义
那些左连接对此没有任何作用左侧所做的就是返回cd.ID=null
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 s.*
From UserSearches us
join Quotes q
on q.UserSearchId = us.Id
and q.QuoteNumber is not null
and us.SearchDate between @beginDate and @endDate
join ContainerDetails cd
on cd.QuoteId = q.Id
join Surcharges s on
on s.ContainerDetailId = cd.Id
要了解正在发生的事情,请查看实际执行计划 最好是在家里 您将看到第一个变量在100276行中的实际数据大小为11272MB 在第二个变量中,填充临时表的查询只返回19665行中的173KB。最后一个查询返回87510行中的1685MB
11272MB
远远超过1685MB
难怪第一个查询速度较慢
这种差异是由两个因素造成的:
UserSearches
、Quotes
、ContainerDetails
表中的所有列。而在第二个变体中,您仅从容器详细信息
中选择ID
。除了从磁盘读取和通过网络传输额外的字节外,这种差异导致了实质上不同的计划。第二个变体不进行排序,不进行键查找,并使用哈希连接而不是嵌套循环。它在引号上使用不同的索引。第二种变体在集装箱细节上使用索引扫描,而不是搜索
左连接
,第二个变量使用内部连接
*
而是只显式列出您需要的列内部联接
(或左侧联接
)附加费
更新 您的问题是“为什么SQL Server会更快地运行第二个查询”,答案是:因为查询不同,它们产生不同的结果(不同的行集、不同的列集) 现在你又在问另一个问题:如何使它们保持相同的速度 您的两个变体中的哪一个产生您想要的正确结果?我假设它是temp table的第二个变体 请注意,我在这里没有回答如何使它们快速。我在这里回答如何使它们相同 以下单个查询应生成与第二个变量完全相同的结果,该变量使用临时表,但不使用显式临时表。我希望它的性能与您的第二个变体类似,具有临时表。我特意用CTE编写了它来复制带有临时表的变体的结构,尽管不用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
;
;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
;以CTE为例
(
选择我们
来自我们
我们在哪里
Select s.*
From UserSearches us
join Quotes q
on q.UserSearchId = us.Id
and q.QuoteNumber is not null
and us.SearchDate between @beginDate and @endDate
join ContainerDetails cd
on cd.QuoteId = q.Id
join Surcharges s on
on s.ContainerDetailId = cd.Id
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
;
;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