多连接和多子查询的性能比较
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'));
不要假设,只是测试。
【讨论】:
以上是关于多连接和多子查询的性能比较的主要内容,如果未能解决你的问题,请参考以下文章