SQL:如何使用 CASE 提高 INNER JOIN 的性能

Posted

技术标签:

【中文标题】SQL:如何使用 CASE 提高 INNER JOIN 的性能【英文标题】:SQL : How to improve performance for INNER JOIN with CASE 【发布时间】:2018-03-31 04:52:13 【问题描述】:

对于需要读取过滤器的过滤器表并读取与该过滤器相关的源数据表的查询,我有一个很大的性能问题。 所以我的过滤器表看起来像这样;

FILTER_ID CUSTOMER COUNTRY DEPARTMENT
   1                  UK      DEP1
   2       CUS1       US      DEP1
   3       CUS1  

这是我的源数据表;

ROW_NO CUSTOMER COUNTRY DEPARTMENT
  1      CUS1     UK      DEP1
  2      CUS2     UK      DEP1
  3      CUS3     UK      DEP1
  4      CUS1     US      DEP1
  5      CUS1     SG      DEP3
  6      CUS1     UK      DEP3

对于过滤表上的每个过滤器,我需要从源数据表中获取行。但是如果 FILTER 表上的列是 EMPTY,我们需要读取源数据表中存在的该列的所有成员。假设对于 FILTER_ID 1,我们需要从源表中读取 COUNTRY = UK 和 DEPARTMENT = DEP1 的所有客户。

结果表应该是这样的;

FILTER_ID   ROW_NO   CUSTOMER   COUNTRY   DEPARTMENT
   1           1       CUS1       UK        DEP1
   1           2       CUS2       UK        DEP1
   1           3       CUS3       UK        DEP1
   2           4       CUS1       US        DEP1
   3           1       CUS1       UK        DEP1
   3           4       CUS1       US        DEP1
   3           5       CUS1       SG        DEP3
   3           6       CUS1       UK        DEP3

我正在使用条件连接,它工作正常,但问题是,它非常慢!

select t1.FILTER_ID, t2.* from FILTER_TABLE as t1
inner join SOURCE_DATA as t2 on 
CASE WHEN t1.CUSTOMER    = '' THEN t2.CUSTOMER   ELSE t1.CUSTOMER END = t2.CUSTOMER and
CASE WHEN t1.DEPARTMENT  = '' THEN t2.DEPARTMENT ELSE t1.DEPARTMENT END = t2.DEPARTMENT and
CASE WHEN t1.COUNTRY     = '' THEN t2.COUNTRY    ELSE t1.COUNTRY END = t2.COUNTRY 

有没有办法优化这段代码?

【问题讨论】:

FILTER_TABLE 修复了吗?从某种意义上说,它总是只有 3 行吗? 不,它不是固定的,它有 12K 行。源表有 85K 行,具体取决于条件。 您可以将您的CASE 语句重写为OR 的组合(因为t2.CUSTOMER = t2.CUSTOMER 几乎每次都可以(除非t2.CUSTOMERNULL)),例如:@987654330 @。请检查并告诉我们结果。 你能把你的 FILTER_TABLE 分成 9 个只包含“非空”列的表吗?然后写 9 SELECT 与它们之间的联合,每个选择只连接固定列而没有任何 CASE? @AlbertoMartinez 尝试使用“动态”参数创建一个大型“智能”查询实际上是一个非常常见的错误。结果总是令人失望,因为那些“智能”查询会导致执行计划效率低下和/或无法使用索引 【参考方案1】:

这样试试,我想,很快:

select t1.FILTER_ID, t2.* from FILTER_TABLE as t1
inner join SOURCE_DATA as t2 on 
 t1.CUSTOMER    = t2.CUSTOMER and
 t1.DEPARTMENT  = t2.DEPARTMENT and
 t1.COUNTRY     = t2.COUNTRY 
where t1.CUSTOMER <> '' and t1.DEPARTMENT <>'' and t1.COUNTRY <> ''
union all
select t1.FILTER_ID, t2.* from FILTER_TABLE as t1
inner join SOURCE_DATA as t2 on 
CASE WHEN t2.CUSTOMER    = t2.CUSTOMER 
where t1.CUSTOMER = '' and t1.DEPARTMENT <>'' and t1.COUNTRY <> ''
union all
select t1.FILTER_ID, t2.* from FILTER_TABLE as t1
inner join SOURCE_DATA as t2 on 
CASE WHEN t2.DEPARTMENT    = t2.DEPARTMENT 
where t1.CUSTOMER <> '' and t1.DEPARTMENT = '' and t1.COUNTRY <> ''
union all
select t1.FILTER_ID, t2.* from FILTER_TABLE as t1
inner join SOURCE_DATA as t2 on 
CASE WHEN t2.COUNTRY    = t2.COUNTRY 
where t1.CUSTOMER <> '' and t1.DEPARTMENT <> '' and t1.COUNTRY = ''

【讨论】:

感谢您的回答,但您的解决方案也与上述类似。同时不同的列可以为空。假设 CUSTOMER = ' ' 和 DEPARTMENT = ' ' 我在产品服务器中有 12 列,如果我尝试使用所有组合,组合将会很疯狂。【参考方案2】:

试试这样:

select t1.FILTER_ID, t2.* from FILTER_TABLE as t1
inner join SOURCE_DATA as t2 on 
 IIF(t1.CUSTOMER = '',t2.CUSTOMER, t1.CUSTOMER)  = t2.CUSTOMER and
 IIF(t1.DEPARTMENT = '', t2.DEPARTMENT,t1.DEPARTMENT) = t2.DEPARTMENT  and
 IIF(t1.COUNTRY = '',t1.COUNTRY,t2.COUNTRY) = t2.COUNTRY 

【讨论】:

以上是关于SQL:如何使用 CASE 提高 INNER JOIN 的性能的主要内容,如果未能解决你的问题,请参考以下文章

sql left join 和 inner join 效率

SQL优化--使用 EXISTS 代替 IN 和 inner join来选择正确的执行计划

在以下情况下如何提高 sql 性能[关闭]

如何在 SQL Server 中使用 INNER JOIN 从多个表中删除

INNER JOIN与LEFT JOIN在SQL Server的性能

SQL 如何通过 INNER JOIN 更新 -