如何提高大型左外连接的性能?

Posted

技术标签:

【中文标题】如何提高大型左外连接的性能?【英文标题】:How to improve performance on large left outer join? 【发布时间】:2016-12-19 15:06:31 【问题描述】:

这些是我的桌子:

Source_Artikelen - 列:文章 - 描述(1.438.171 条记录)

Source_LevArt - 列:文章 - 制造商部件号(1.751.801 记录)

...这是我正在执行的查询

SELECT a.Artikel,a.Omschrijving, l.Artikel_Leverancier
  FROM Source_Artikelen AS a
       LEFT OUTER JOIN Source_LevArt AS l
    ON a.Artikel Like l.Artikel

在我手动取消之前,这个查询今晚运行了 20 多个小时。

那我想做什么?

我想列出我的表 Source_Artikelen 中的所有文章。然后我想看看 Source_LevArt 中是否有制造商零件号。

并非所有来自 Source_Artikelen 的文章都出现在 Source_LevArt 中 有时在一篇文章的 Source_LevArt 中有多个制造商部件号

这就是为什么我需要使用 LEFT OUTER JOIN

我已经尝试了一些关于索引的方法,但它并没有真正的帮助。可能我做错了什么。

我真的需要一些帮助,因为这只是我正在编写的查询的开始。 稍后我将不得不添加 2 个其他(大)标签作为左外连接...


2016 年 19 月 12 日更新 16:24: 嗨 piet.t

SELECT TOP(20) a.Artikel,a.Omschrijving, l.Artikel_Leverancier 
  FROM Source_Artikelen AS a 
       LEFT JOIN Source_LevArt AS l 
    ON a.Artikel LIKE l.Artikel 

这需要 9 秒

SELECT TOP(20) a.Artikel,a.Omschrijving, l.Artikel_Leverancier 
  FROM Source_Artikelen AS a 
       LEFT JOIN Source_LevArt AS l 
    ON a.Artikel = l.Artikel 

这需要 1 秒!

我真的不知道有什么区别,因为我没有使用通配符。

【问题讨论】:

Source_Artikelen .Artikel 是主键,Source_LevArt.Artikel 是外键吗?您还可以在“ON”(无 where 子句)后用“AND”表示“AND”“制造商零件编号”不为空。用包含零件号的字段替换报价中的内容。 (假设 source_levArt 中的一些记录可能为空。那么应该发生的是在连接发生之前将过滤器应用于第二个表,从而减少查询必须连接的记录数。 我要做的第一件事是去掉连接条件中的like,因为你似乎只想要完全匹配,所以试试ON a.Artikel = l.Artikel,看看这是否加快了速度. @vlatro “我真的不知道有什么不同,因为我没有使用通配符。”你知道,但 SQL 处理器不知道,因为 l.Artikel 的内容中可能隐藏了 %_ 谢谢@piet.t 【参考方案1】:

这里由 Paul White 介绍:Dynamic Seeks and Hidden Implicit Conversions

即使在完全匹配的情况下使用 like 也会进行动态搜索。这意味着在执行时知道要搜索的列,而不是在编译时。

下面是如何为我的下面示例中的表格派生列..

[Expr1005] = 标量运算符(CONVERT_IMPLICIT(varchar(12),[Aegon_X].[Sales].[Orders].[custid] as [o].[custid],0)), [Expr1006] = 标量运算符(LikeRangeStart(CONVERT_IMPLICIT(varchar(12),[Aegon_X].[Sales].[Orders].[custid] as [o].[custid],0)) ), [Expr1007] = 标量运算符(LikeRangeEnd(CONVERT_IMPLICIT(varchar(12),[Aegon_X].[Sales].[Orders].[custid] as [o].[custid],0)) ), [Expr1008] = 标量运算符(LikeRangeInfo(CONVERT_IMPLICIT(varchar(12),[Aegon_X].[Sales].[Orders].[custid] as [o].[custid],0)) )

以下是保罗所描述的,这些是如何推导出来的

上方的工具提示显示计算标量使用三个内部函数,LikeRangeStart、LikeRangeEnd 和 LikeRangeInfo。

前两个函数将范围描述为开区间。第三个函数返回一组以整数编码的标志,这些标志在内部用于定义存储引擎的某些搜索属性。下面的工具提示显示了在由 LikeRangeStart 和 LikeRangeEnd 的结果描述的开区间上的搜索,以及残差谓词“LIKE @Like”的应用。

总而言之,使用like SQL 使用动态搜索在编译时派生搜索属性..

以下示例显示不同的计划

使用类似: 我真的不知道有什么区别,因为我没有使用通配符。

select top 10* from sales.orders o
join
sales.customers c
on c.custid like o.custid

计划:

现在使用完全匹配时..

 select top 10* from sales.orders o
    join
    sales.customers c
    on c.custid =o.custid   

你可以看到合并加入计划

【讨论】:

【参考方案2】:

使用 = 而不是喜欢。

这 2 个索引应该为您提供最佳的 Select 性能。

CREATE INDEX idx ON Source_Artikelen(Artikel) INCLUDE(Omschrijving);

CREATE INDEX idx ON Source_LevArt(Artikel) INCLUDE(Artikel_Leverancier);

如果您实施它们并再次尝试您的 SELECT,您能上传一份您的执行计划吗?

【讨论】:

以上是关于如何提高大型左外连接的性能?的主要内容,如果未能解决你的问题,请参考以下文章

左外连接 SQL 服务器中的性能问题

性能改进:左外连接、排序依据、子查询:主查询未拾取索引

如何强制 Hibernate 在组件上使用左外连接进行命名查询?

如何使用 Dynamic Linq 进行左外连接?

如何在 Linq 中执行左外连接? [复制]

如何按照核心数据模式实现左外连接?