多连接和多子查询的性能比较

Posted

技术标签:

【中文标题】多连接和多子查询的性能比较【英文标题】:Performance Comparison of Multiple Join and Multiple Subqueries 【发布时间】:2020-06-24 13:37:10 【问题描述】:

我知道以前有人问过这类问题,但我找不到与我的确切问题相关的问题。 我会试着举一个夸张的例子。

假设我们想要找到至少一名员工年龄超过 40 岁且至少一名客户年龄低于 20 岁的公司。

我的同事为这个问题写的查询是这样的:

SELECT DISTINCT(c.NAME) FROM COMPANY c 
LEFT JOIN EMPLOYEE e ON c.COMPANY_ID = e.COMPANY_ID 
LEFT JOIN CUSTOMER u ON c.COMPANY_ID = u.COMPANY_ID
WHERE e.AGE > 40 and u.AGE < 20

我是数据库新手。但是看看这个查询(就像一个时间复杂度问题),它会创建一个不必要的巨大临时表。每个公司都有employeeAmount x customerAmount 行。

所以,我重新编写了查询:

SELECT c.NAME FROM COMPANY c 
WHERE EXISTS (SELECT * FROM EMPLOYEE e WHERE e.AGE > 40 AND c.COMPANY_ID = e.COMPANY_ID  )
  OR EXISTS (SELECT * FROM CUSTOMER u WHERE u.AGE < 20 AND c.COMPANY_ID = u.COMPANY_ID )

我不知道这个查询是否会更糟,因为它将为每个公司运行 2 个子查询。

我知道有更好的方法来写这个。例如,为 2 个年龄条件编写 2 个不同的子查询,然后将它们联合起来可能会更好。但我真的很想知道两个查询之一/两个是否有问题。

注意:您可以增加连接/子查询的数量。例如,“我们希望找到至少一名员工年龄超过 40 岁、至少一名客户年龄低于 20 岁的公司并且至少有一份订单超过 1000 美元

谢谢。

【问题讨论】:

您不应该使用AND 结合两个EXISTS 来正确回答您的问题吗? 【参考方案1】:

exists 版本通常应该有更好的性能,特别是如果您在每个子表中都有company_id 上的索引。

为什么? JOIN 版本创建了一个包含所有 40 岁以上的客户和所有 20 岁以下的员工的中间结果。如果这些组对于特定公司来说很大,那么这可能会非常大。然后,查询会执行额外的工作来删除重复项。

可能存在一些边缘情况,其中第一个版本具有良好的性能。例如,如果其中任何一个组是空的——没有 20 岁以下的员工或没有超过 40 岁的客户,我会预料到这一点。那么中间结果集是空的,并且不需要删除重复项。不过,对于一般情况,我推荐exists

【讨论】:

【参考方案2】:

要了解你的当前环境中真正发生的情况,你的数据库设置和你的数据你需要比较真实的 执行计划(不仅仅是 EXPLAIN PLAN,它只给出估计的计划)。除了 Oracle 使用的详细步骤(全表扫描、连接等)之外,只有真正的执行计划才能给出查询使用的 CPU 和 IO 等详细资源。

试试:

ALTER SESSION STATISTICS_LEVEL=ALL;

<your query>

SELECT * FROM TABLE(dbms_xplan.display(NULL, NULL, format=>'allstats last'));

不要假设,只是测试。

【讨论】:

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

MySQL查询和静态缓存的性能比较

性能问题:比较多线程和多处理的案例研究

这两个查询的性能比较

网站性能优化

比较日期查询的性能

mongodb更新比较频繁,性能下降的厉害怎么办