多个内连接和子查询的查询优化

Posted

技术标签:

【中文标题】多个内连接和子查询的查询优化【英文标题】:Query optimization for multiple inner joins and sub-query 【发布时间】:2020-12-17 04:57:15 【问题描述】:

我需要有关以下查询的查询优化方面的帮助。

SELECT pr.todate , pr.descr, cmp.company_id 
FROM employee AS emp
INNER JOIN company AS cmp ON emp.emp_comp_id = cmp.company_id 
INNER JOIN profile AS pr ON emp.acca_id = pr.profile_id 
INNER JOIN acondition ON as_id = as_ac_id
WHERE as_closed = 0 
  AND (pr.ac_act_id = 20)
  AND (pr.todate = (SELECT MIN(todate) AS Expr1 
                    FROM profile pro 
                    INNER JOIN employee empl ON empl.acca_id = pro.profile_id 
                    JOIN acondition ON as_id = as_ac_id 
                    WHERE (pro.ac_act_id = 20 
                           AND empl.emp_comp_id = cmp.company_id) 
                      AND as_closed = 0)) 

由于主查询和子查询有重复的join,有什么办法可以去掉子查询中的join?

【问题讨论】:

我们需要更多信息来帮助您……例如执行计划、表定义和表上的索引。是什么让您认为它可以优化? 嗨,employeecompanyprofilenonclustered 索引,aconditionclustered 索引。有什么办法可以减少子查询中的内连接? edit 请进入问题并使用粘贴计划来发布您的执行计划。为什么要删除连接?您的查询似乎完全合理。 有点慢,所以我需要优化这个查询。出于安全原因,我在这里使用了虚拟表,所以我可能无法共享执行计划 @the_coder_guy ,老实说,*** 并不是寻求查询调优建议的最佳场所。这需要与您进行大量的来回交流,并且还涉及很多事情。您最好的选择是查看 SQL Server 社区 slack 频道:dbatools.io/slack 【参考方案1】:

因为,正如您所澄清的,您的子查询几乎与您的主查询相同,您可以使用窗口函数 RANK 作为过滤条件。 RANK 为关系分配相同的编号,这意味着如果每个公司的多个记录匹配,您将获得所有记录,例如

SELECT todate, descr, company_id
FROM (
    SELECT pr.todate, pr.descr, cmp.company_id
      , RANK() OVER (PARTITION BY cmp.company_id ORDER BY pr.todate ASC) RankNumber
    FROM employee AS emp
    INNER JOIN company AS cmp ON emp.emp_comp_id = cmp.company_id 
    INNER JOIN profile AS pr ON emp.acca_id = pr.profile_id 
    INNER JOIN acondition ON as_id = as_ac_id
    WHERE as_closed = 0 AND pr.ac_act_id = 20
) X
where RankNumber = 1;

【讨论】:

您好,感谢您的回复,这个查询只给了我一个结果,而我的原始查询给了我大约 35 个结果。 您好,我尝试调试,但无法弄清楚 where 子句中可能出现的问题。 以前的记录示例:2019-01-31 00:00:00.000 新员工 101 2020-11-27 00:00:00.000 老员工 014 2020-11-27 00:00:00.000 任期员工016 2020-12-11 00:00:00.000 新的初级员工 030 2020-11-27 00:00:00.000 董事 034 2020-12-14 00:00:00.000 初级经理 052 2019-02-18 00:00:00.000高级经理058 新记录输出:2019-01-31 00:00:00.000 新员工 101 它不应该只返回最旧的记录,它应该返回所有符合条件的记录。因此,如果我在没有 `AND (pr.todate = (SELECT MIN(todate) AS Expr1 FROM profile pro INNER JOIN employee empl ON empl.acca_id = pro.profile_id JOIN acondition ON as_id = as_ac_id WHERE (pro.ac_act_id = 20 AND empl.emp_comp_id = cmp.company_id) AND as_closed = 0)) `【参考方案2】:

这对你有用吗?

SELECT ca.todate , pr.descr, cmp.company_id 
FROM employee AS emp
INNER JOIN company AS cmp ON emp.emp_comp_id = cmp.company_id 
CROSS APPLY (
    SELECT TOP(1) pr.todate
    FROM profile pr 
    INNER JOIN acondition ON as_id = as_ac_id 
    WHERE emp.acca_id = pr.profile_id AND (pr.ac_act_id = 20) AND as_closed = 0 
    ORDER BY pr.todate ASC
) AS ca

【讨论】:

嗨,在INNER JOIN acondition ON as_id = as_ac_id 的内部连接中,我出错了,因为它是配置文件表的一部分。 嗨,它没有给我正确的结果,我想min (todate ) 标准没有得到应用。因此我得到了 49 个结果。 @Tole1010 你和我犯了同样的错误,你得到了一个分钟 todate 而不是每个公司一个。同样在您的情况下,您实际上并没有对您获得的日期应用任何过滤。 @Tole1010 有没有办法在这里添加日期的过滤条件? 您可以将结果插入临时表,然后在 todate 再次加入,但我不确定这是否会提高您的性能。如果您提供数据来填充表格,那么帮助您会容易得多。

以上是关于多个内连接和子查询的查询优化的主要内容,如果未能解决你的问题,请参考以下文章

带有连接和子查询的 mysql 查询优化

如何优化多表连接视图以获得更快的响应?

条件检查之间的Mysql中有多个条件?有没有办法优化这个查询?

使用 IN 子句和子查询进行极端查询优化

如何优化 MySQL 中的多个左连接?

如何优化这个 sql 查询(内连接)