EXISTS、IN与JOIN性能分析
EXISTS、IN与JOIN,都可以用来实现形如“查询A表中在(或不在)B表中的记录”的查询逻辑。
在论坛上看到很多人对此有所误解(如关于in的疑惑、用 外连接 和 Is Null 代替 not in两帖),特做一简单测试。
测试结果:
测试代码较长,附于本帖最后。
图表中百分数表示同一组3个查询的执行时间比例。红色表示3个语句中最慢,绿色表示3个语句中最快的,并列则没加颜色。
其中索引只测试了聚集索引,当表中字段较多且查询字段是非聚集索引时,选择执行计划的条件比较复杂,没有测试。并且当表中数量变化后,执行计划可能也有差异。图表反映了3种查询方式的解析机制的不同,基本结论是类似的,但具体情况还要视执行计划而定。
分析结论:
通常情况下,3种查询方式的执行时间:
EXISTS <= IN <= JOIN
NOT EXISTS <= NOT IN <= LEFT JOIN
只有当表中字段允许NULL时,NOT IN的方式最慢:
NOT EXISTS <= LEFT JOIN <= NOT IN
综上:
IN的好处是逻辑直观简单(通常是独立子查询);缺点是只能判断单字段,并且当NOT IN时效率较低,而且NULL会导致不想要的结果。
EXISTS的好处是效率高,可以判断单字段和组合字段,并不受NULL的影响;缺点是逻辑稍微复杂(通常是相关子查询)。
JOIN用在这种场合,往往是吃力不讨好。JOIN的用途是联接两个表,而不是判断一个表的记录是否在另一个表。
编程建议:
(以下三条建议中EXISTS和IN同时代指肯定式逻辑和加NOT后的否定式逻辑)
如果查询条件是单字段主键(有索引且不允许NULL),则EXISTS和IN的性能基本一样,IN的查询通常写法简单、逻辑直观。
如果查询条件涉及多个字段,则最好选择EXISTS,千万不要用字段拼接再IN的方式(索引会失效)。
如果条件不确定,选用EXISTS是最保险的办法,性能最好,不受三值逻辑影响(EXISTS只会返回True/False不会返回Unknown),但代码逻辑稍稍复杂,思路要理清楚,而且相关字段最好采用“表(别)名.字段名”的形式。
附一:IN/NOT IN容易出现的两个问题
参看如下代码:
SELECT
EmployeeID = n,
EmployeeName = 'E' + RIGHT('000' + CAST(n AS varchar(10)),3)
INTO #Employees
FROM dbo.Nums WHERE n <= 10;
SELECT EmployeeID
INTO #Badboys
FROM (SELECT TOP(4) EmployeeID = n FROM dbo.Nums WHERE n <= 10 ORDER BY NEWID()) tmp
UNION
SELECT NULL;
--问题1:
SELECT * FROM #Employees WHERE EmployeeID IN (SELECT EmployeeID FROM #Badboys);
SELECT * FROM #Employees WHERE EmployeeID NOT IN (SELECT EmployeeID FROM #Badboys);
--问题2:
SELECT * FROM #Employees WHERE EmployeeName IN (SELECT EmployeeName FROM #Badboys);
SELECT * FROM #Employees WHERE EmployeeName NOT IN (SELECT EmployeeName FROM #Badboys);
其中:
问题1是三值逻辑的问题,说明了在NOT IN遇到NULL时要特别小心(参看关于 not in的疑问一帖)。这也是为什么建议“如果可能,尽量让所有字段都声明为NOT NULL”的原因之一。
问题2是SQL Server子查询处理时命名空间解析的漏洞,说明了在多表查询中采用“表(别)名.字段名”的形式的好处,否则就要对字段名的拼写非常小心。
附二:EXISTS、IN与JOIN性能分析测试代码:
[code=sql]
--表中字段不允许NULL
--TestCase1: 无重复数据,无索引
CREATE TABLE T1(n int NOT NULL);
CREATE TABLE T2(n int NOT NULL);
INSERT INTO T1
SELECT n FROM dbo.Nums WHERE n <= 100;
INSERT INTO T2
SELECT n FROM dbo.Nums WHERE n <= 100 AND n % 2 = 0;
--TestCase2: 无重复数据,有索引
CREATE UNIQUE CLUSTERED INDEX IX_T1 ON T1(n);
CREATE UNIQUE CLUSTERED INDEX IX_T2 ON T2(n);
--TestCase3: 有重复数据,无索引
DROP TABLE T1;
DROP TABLE T2;
CREATE TABLE T1(n int NOT NULL);
CREATE TABLE T2(n int NOT NULL);
INSERT INTO T1
SELECT n FROM dbo.Nums WHERE n <= 100;
INSERT INTO T2
SELECT n FROM dbo.Nums WHERE n <= 100 AND n % 2 = 0
UNION ALL
SELECT n FROM dbo.Nums WHERE n <= 100 AND n % 3 = 0;
--TestCase4: 有重复数据,有索引
CREATE CLUSTERED INDEX IX_T1 ON T1(n);
CREATE CLUSTERED INDEX IX_T2 ON T2(n);
--表中字段允许NULL
--TestCase5: 无重复数据,无索引
DROP TABLE T1;
DROP TABLE T2;
CREATE TABLE T1(n int NULL);
CREATE TABLE T2(n int NULL);
INSERT INTO T1
SELECT n FROM dbo.Nums WHERE n <= 100;
INSERT INTO T2
SELECT n FROM dbo.Nums WHERE n <= 100 AND n % 2 = 0;
--TestCase6: 无重复数据,有索引
CREATE UNIQUE CLUSTERED INDEX IX_T1 ON T1(n);
CREATE UNIQUE CLUSTERED INDEX IX_T2 ON T2(n);
--TestCase7: 有重复数据,无索引
DROP TABLE T1;
DROP TABLE T2;
CREATE TABLE T1(n int NULL);
CREATE TABLE T2(n int NULL);
INSERT INTO T1
SELECT n FROM dbo.Nums WHERE n <= 100;
INSERT INTO T2
SELECT n FROM dbo.Nums WHERE n <= 100 AND n % 2 = 0
UNION ALL
SELECT n FROM dbo.Nums WHERE n <= 100 AND n % 3 = 0;
--TestCase8: 有重复数据,有索引
CREATE CLUSTERED INDEX IX_T1 ON T1(n);
CREATE CLUSTERED INDEX IX_T2 ON T2(n);
--Foreach TestCase above,分别执行以下两组语句并观察执行计划:
--肯定式逻辑
SELECT T1.*
FROM T1
WHERE EXISTS (SELECT * FROM T2 WHERE T2.n = T1.n);
SELECT T1.*
FROM T1
WHERE T1.n IN (SELECT T2.n FROM T2);
SELECT DISTINCT T1.* --不加DISTINCT可能会引起重复
FROM T1
INNER JOIN T2
ON T1.n = T2.n;
--否定式逻辑
SELECT T1.*
FROM T1
WHERE NOT EXISTS (SELECT * FROM T2 WHERE T2.n = T1.n);
SELECT T1.*
FROM T1
WHERE T1.n NOT IN (SELECT T2.n FROM T2);
SELECT T1.*
FROM T1
LEFT JOIN T2
ON T1.n = T2.n
WHERE T2.n IS NULL;
--End Foreach
--清场
DROP TABLE T1;
DROP TABLE T2;
- 大小: 17.3 KB
分享到:
相关推荐
SQL语句优化——in,not in,exists,not exists, left join...on博客所需SQL语句.txt欢迎下载!
PostgreSQL作为一种强大的开源关系数据库系统,它支持多种SQL操作,其中包括IN、EXISTS、ANY/ALL和JOIN等操作符。这些操作符在不同的业务场景下有着不同的表现和性能影响。在实际的数据库操作中,选择合适的操作符是...
IN 适合内外表都很大的情况,因为 IN 是把外表和那表作 hash join。 在某些情况下,EXISTS 可以比 IN 快很多。这是因为 Oracle 在执行 IN 子查询时,首先执行子查询,并将获得的结果列表存放在一个加了索引的临时表...
### 经典SQL查询总结关于Exists, not Exists, IN, not IN 效率的说明 在数据库查询操作中,存在着多种方法来实现相似的功能,但不同的实现方式在性能上可能会有显著差异。本文将深入探讨 SQL 中 `EXISTS`, `NOT ...
相比之下,`NOT IN` 和 `NOT EXISTS` 在两个集合比较时就完成了筛选,减少了额外的开销。 关于日志文件过大的问题,这在大量删除操作时很常见。在MySQL中,删除操作会记录在事务日志中,如果恢复模型设置为完整模式...
- **连接查询**:当查询涉及到两个或更多表的数据时,`IN`和`EXISTS`都不是最佳选择,应优先考虑使用JOIN语句,如LEFT JOIN,以实现更高效的连接查询。 ### 总结 尽管`IN`和`EXISTS`在功能上看似相似,但在执行...
SQL语句优化——in,not in,exists,not exists, left join...on博客所需SQL语句2.txt,欢迎下载!
在SQL查询中,`IN`、`INNER JOIN`、`OUTER JOIN` 和 `EXISTS` 是四个重要的关键字,它们用于处理数据表之间的关联和筛选。这些概念在数据库设计和数据检索中至关重要,理解并熟练运用它们能显著提高查询效率。 1. *...
如果你想要查询在主表中不存在于子查询结果中的记录,使用 `NOT EXISTS` 可能比使用 `LEFT JOIN` 或 `NOT IN` 更直观。 - 使用场景:`EXISTS` 常用于检查关联或匹配的记录,而 `NOT EXISTS` 用于排除这些匹配的记录...
三、EXISTS与IN和JOIN的比较 1. EXISTS vs IN: - EXISTS更关注于是否存在匹配的行,而IN则关心具体匹配的值。在处理大量数据时,如果子查询返回的值较少,IN可能更快;反之,如果子查询返回很多行,EXISTS通常...
这是因为`IN`和`NOT IN`需要将主查询中的每一行都与子查询结果进行比较,而`EXISTS`和`NOT EXISTS`则只需判断子查询是否返回至少一行数据即可,无需获取所有数据行。 **性能对比:** - **IN 和 NOT IN**:需要将主...
且看接下来的具体分析:in其实是将外表和内表进行hash join,exists是先对外表进行loop操作,然后每次loop后再对内表进行查询。 如果两张表大小差不多,那么exists和in的效率差不多。 例如: 一张大表为A,一张小表B...
在理解`EXISTS`的使用时,我们需要深入探讨其工作原理、优缺点以及与其他查询操作(如`IN`、`JOIN`)的对比。 一、`EXISTS`的基本用法 `EXISTS`子句通常与子查询一起使用,形式如下: ```sql SELECT column1, ...
然而,当子查询返回的数据量非常大时,`IN` 可能不是最佳选择,因为它可能会使用哈希连接(Hash Join),这可能导致性能下降。 2. `EXISTS` 操作符: `EXISTS` 用于检查子查询是否返回任何行。它的语法结构稍显复杂...
- 如果使用 `EXISTS` 的目的是为了过滤结果集,考虑使用 `IN` 或 `JOIN` 作为替代方案。 - 在涉及多层嵌套的情况下,仔细分析查询逻辑,尝试简化结构以提高可读性和执行效率。 - 使用 `EXISTS` 代替 `IN` 可能会在...
- 如果子查询的结果集非常大,考虑使用 `JOIN` 语句代替 `EXISTS` 或 `IN`,这可能会更有效。 总结来说,`EXISTS` 和 `IN` 在SQL Server中都有各自的适用场景,选择哪种取决于具体的需求和数据特性。在编写高性能...
然而,它的执行效率通常不如使用NOT EXISTS或NOT IN,特别是在右表记录较少的情况下。 #### 总结 从以上分析可以看出,每种查询方式都有其适用场景,并不存在绝对的“好”或“坏”。选择哪种方式取决于具体的应用...
理解`EXISTS`和`NOT EXISTS`的用法对于优化SQL查询非常重要,因为它们通常比使用`IN`或`JOIN`操作符更有效率,特别是在处理大量数据时。`EXISTS`主要关注记录是否存在,而不需要返回具体的值,这使得它在处理复杂...