Sql中in和exists详解
Posted 皮卡丘的情绪
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Sql中in和exists详解相关的知识,希望对你有一定的参考价值。
in和exists的转换
1 结论
- in()适合子查询结果集比外表查询结果集小的情况(子表查询结果集的记录数决定了数据库的交互次数)
- exists()适合子查询结果集比外表查询结果集大的情况(外表查询结果集的记录数决定了数据库的交互次数)
- 当外表查询结果集与子查询结果集数据一样大时,in与exists效率差不多,可任选一个使用
- 小表驱动大表(更准确的说是查询结果集小的驱动查询结果集大的)
- IN查询在内部表和外部表上都可以使用到索引。
- Exists查询仅在内部表上可以使用到索引。
- 表的规模不是看内部表和外部表记录数的,而是外部表和子查询结果集中记录数的大小
2 in和exists的区别
2.1 in的性能分析
select * from A
where id in(select id from B)
上述sql会先执行括号内的子查询,再执行主查询,因此相当于以下过程:
for select id from B
for select * from A where A.id = B.id
以上查询使用了in语句,in()只执行一次,它查出B表中的所有id字段并缓存到内存中之后,检查A表的id是否与B表中的id相等,如果相等则将A表的记录加入结果集中,直到遍历完A表的所有记录.
它的查询过程类似于以下过程
List resultSet=[];
Array A=(select * from A);
Array B=(select id from B);
for(int i=0;i<A.length;i++)
for(int j=0;j<B.length;j++)
if(A[i].id==B[j].id)
resultSet.add(A[i]);
break;
return resultSet;
分析:
- 当前的in子查询是B表驱动A表
- mysql先将B表的数据一次性查出来存放于内存中,B表的记录数决定了数据库的交互次数
- 遍历B表的数据,再去查A表(每次遍历都是一次连接交互,这里会耗资源)
- 假设B有100000条记录,A有10条记录,会交互100000次数据库;再假设B有10条记录,A有100000记录,只会发生10次交互。
结论:
in()适合B表比A表数据小的情况
2.2 Exists的性能分析
select a.* from A a
where exists(select 1 from B b where a.id=b.id)
类似于以下过程:
for select * from A
for select 1 from B where B.id = A.id
它的查询过程类似于以下过程
List resultSet=[];
Array A=(select * from A)
for(int i=0;i<A.length;i++)
if(exists(A[i].id) //执行select 1 from B b where b.id=a.id是否有记录返回
resultSet.add(A[i]);
return resultSet;
分析:
- 当前exists查询是A表驱动B表
- 与in不同,exists将A的纪录查询到内存,因此A表的记录数决定了数据库的交互次数
- 假设A有10000条记录,B有10条记录,数据库交互次数为10000;假设A有10条,B有10000条,数据库交互次数为10。
2.3 实例
1. 建表sql
#–1.学生表
#-Student(s_id,s_name,s_birth,s_sex) –学生编号,学生姓名, 出生年月,学生性别
CREATE TABLE `Student` (
`s_id` VARCHAR(20),
s_name VARCHAR(20) NOT NULL DEFAULT '',
s_brith VARCHAR(20) NOT NULL DEFAULT '',
s_sex VARCHAR(10) NOT NULL DEFAULT '',
PRIMARY KEY(s_id)
);
#–2.成绩表
#Score(s_id,c_id,s_score) –学生编号,课程编号,分数
Create table Score(
s_id VARCHAR(20),
c_id VARCHAR(20) not null default '',
s_score INT(3),
primary key(`s_id`,`c_id`)
);
#-3.插入学生表数据
insert into Student values('01' , '赵雷' , '1990-01-01' , '男');
insert into Student values('02' , '钱电' , '1990-12-21' , '男');
insert into Student values('03' , '孙风' , '1990-05-20' , '男');
insert into Student values('04' , '李云' , '1990-08-06' , '男');
insert into Student values('05' , '周梅' , '1991-12-01' , '女');
insert into Student values('06' , '吴兰' , '1992-03-01' , '女');
insert into Student values('07' , '郑竹' , '1989-07-01' , '女');
insert into Student values('08' , '王菊' , '1990-01-20' , '女');
#-4.成绩表数据
insert into Score values('01' , '01' , 80);
insert into Score values('01' , '02' , 90);
insert into Score values('01' , '03' , 99);
insert into Score values('02' , '01' , 70);
insert into Score values('02' , '02' , 60);
insert into Score values('02' , '03' , 80);
insert into Score values('03' , '01' , 80);
insert into Score values('03' , '02' , 80);
insert into Score values('03' , '03' , 80);
insert into Score values('04' , '01' , 50);
insert into Score values('04' , '02' , 30);
insert into Score values('04' , '03' , 20);
insert into Score values('05' , '01' , 76);
insert into Score values('05' , '02' , 87);
insert into Score values('06' , '01' , 31);
insert into Score values('06' , '03' , 34);
insert into Score values('07' , '02' , 89);
insert into Score values('07' , '03' , 98);
数据展示:
2. in方法
SELECT
a.*
FROM
Student a
WHERE
a.s_id IN (SELECT b.s_id FROM Score b WHERE b.c_id = '01')
3. exists方法
SELECT
a.*
FROM
Student a
WHERE
EXISTS(SELECT * FROM Score b WHERE a.s_id = b.s_id AND b.c_id = '01')
4. 结果
3 not in 和not exists
如果查询语句使用了not in,那么内外表都进行全表扫描,没有用到索引;但not extsts 的子查询依然能用到表上的索引。所以无论哪个表大,用not exists都比not in要快。
SQL中EXISTS和IN的区别?
【中文标题】SQL中EXISTS和IN的区别?【英文标题】:Difference between EXISTS and IN in SQL? 【发布时间】:2010-09-06 16:45:46 【问题描述】:SQL中EXISTS
和IN
子句有什么区别?
什么时候应该使用EXISTS
,什么时候应该使用IN
?
【问题讨论】:
【参考方案1】:如果您可以使用where in
而不是where exists
,那么where in
可能更快。
使用where in
或where exists
将遍历您父级结果的所有结果。这里的不同之处在于where exists
会引起很多dependet 子查询。如果可以防止依赖子查询,那么where in
将是更好的选择。
例子
假设我们有 10,000 家公司,每个公司有 10 个用户(因此我们的 users 表有 100,000 个条目)。现在假设您想通过用户名或公司名来查找用户。
以下使用were exists
的查询的执行时间为 141 毫秒:
select * from `users`
where `first_name` ='gates'
or exists
(
select * from `companies`
where `users`.`company_id` = `companies`.`id`
and `name` = 'gates'
)
发生这种情况是因为每个用户都会执行一个相关的子查询:
但是,如果我们避免存在查询并使用以下方式编写它:
select * from `users`
where `first_name` ='gates'
or users.company_id in
(
select id from `companies`
where `name` = 'gates'
)
然后避免依赖子查询,查询将在 0,012 毫秒内运行
【讨论】:
【参考方案2】:IN 仅支持等式关系(或以 NOT 开头的不等式)。 它是 =any / =some 的同义词,例如
select *
from t1
where x in (select x from t2)
;
EXISTS 支持关系的变体类型,不能使用 IN 表示,例如-
select *
from t1
where exists (select null
from t2
where t2.x=t1.x
and t2.y>t1.y
and t2.z like '℅' || t1.z || '℅'
)
;
另外一种说法 -
EXISTS 和 IN 之间所谓的性能和技术差异可能是由特定供应商的实施/限制/错误造成的,但很多时候它们只不过是由于缺乏而造成的神话了解数据库内部结构。
表的定义、统计的准确性、数据库配置和优化器的版本都会影响执行计划,从而影响性能指标。
【讨论】:
赞成您对性能的评论:在不关注特定 DBMS 的情况下,我们应该假设优化器可以找出最有效的方法。【参考方案3】:EXISTS
将告诉您查询是否返回任何结果。例如:
SELECT *
FROM Orders o
WHERE EXISTS (
SELECT *
FROM Products p
WHERE p.ProductNumber = o.ProductNumber)
IN
用于将一个值与多个值进行比较,并且可以使用字面量值,如下所示:
SELECT *
FROM Orders
WHERE ProductNumber IN (1, 10, 100)
您还可以将查询结果与IN
子句一起使用,如下所示:
SELECT *
FROM Orders
WHERE ProductNumber IN (
SELECT ProductNumber
FROM Products
WHERE ProductInventoryQuantity > 0)
【讨论】:
最后一个查询很危险,因为在子查询不返回任何结果的情况下它可能会失败。 'in' 子句至少需要 1 个参数... @user2054927 如果子查询没有返回任何行,最后一个查询将正确地返回没有行 - 这没有什么危险!【参考方案4】:In certain circumstances, it is better to use IN rather than EXISTS. In general, if the selective predicate is in the subquery, then use IN. If the selective predicate is in the parent query, then use EXISTS.
https://docs.oracle.com/cd/B19306_01/server.102/b14211/sql_1016.htm#i28403
【讨论】:
应该注意的是,即使您在 2017 年发布此答案时,您指的是 12 年前发布并且已经远远超过生命的终结【参考方案5】:我发现使用 EXISTS 关键字通常很慢(这在 Microsoft Access 中是非常正确的)。 我改为以这种方式使用连接运算符: should-i-use-the-keyword-exists-in-sql
【讨论】:
【参考方案6】:我相信这有一个直截了当的答案。您为什么不从在他们的系统中开发该功能的人那里检查一下?
如果您是 MS SQL 开发人员,这里是直接来自 Microsoft 的答案。
IN
:
确定指定值是否与子查询或列表中的任何值匹配。
EXISTS
:
指定一个子查询来测试行是否存在。
【讨论】:
【参考方案7】:我的理解是,只要我们不处理 NULL 值,两者都应该是相同的。
查询不返回= NULL vs 的值的相同原因是NULL。 http://sqlinthewild.co.za/index.php/2010/02/18/not-exists-vs-not-in/
至于布尔值与比较器参数,要生成布尔值,需要比较两个值,这就是任何 if 条件的工作原理。所以我无法理解 IN 和 EXISTS 的行为有何不同 .
【讨论】:
【参考方案8】:我假设您知道它们的作用,因此使用方式不同,所以我将您的问题理解为:何时重写 SQL 以使用 IN 而不是 EXISTS 是个好主意,或者反之反之亦然。
这是一个合理的假设吗?
编辑:我问的原因是,在许多情况下,您可以基于 IN 重写 SQL 以使用 EXISTS,反之亦然,对于某些数据库引擎,查询优化器会区别对待两者。
例如:
SELECT *
FROM Customers
WHERE EXISTS (
SELECT *
FROM Orders
WHERE Orders.CustomerID = Customers.ID
)
可以改写为:
SELECT *
FROM Customers
WHERE ID IN (
SELECT CustomerID
FROM Orders
)
或加入:
SELECT Customers.*
FROM Customers
INNER JOIN Orders ON Customers.ID = Orders.CustomerID
所以我的问题仍然存在,原始发帖人是否想知道 IN 和 EXISTS 做了什么,以及如何使用它,或者他是否询问是否使用 IN 重写 SQL 以使用 EXISTS,反之亦然,将是好主意?
【讨论】:
我不知道 OP,但我想要这个问题的答案!我什么时候应该在返回 ID 的子查询中使用 EXISTS 而不是 IN? 在JOIN
中,你需要一个DISTINCT
很棒的演示,但几乎没有回答这个问题
@RoyTinker 答案是“使用 X 当它使查询比 Y 对于维护开发人员更容易理解时使用”和“使用 X 当它可以更快/更少资源密集型时使用”比 Y,这会导致性能问题”。工程是一种妥协
@CaiusJard 对,同意。我在 2010 年构建的系统是一个内部 JSON SQL ORM,因此性能是生成查询的“可读性”的主要问题。【参考方案9】:
原因是 EXISTS 运算符基于“至少找到”原则工作。一旦找到至少一个匹配的行,它就会返回 true 并停止扫描表。
另一方面,当 IN 运算符与子查询组合时,MySQL 必须先处理子查询,然后使用子查询的结果来处理整个查询。
一般的经验法则是,如果子查询包含一个大 数据量大时,EXISTS 运算符提供了更好的性能。
但是,使用 IN 运算符的查询将执行得更快,如果 子查询返回的结果集很小。
【讨论】:
【参考方案10】:基于规则优化器:
当子查询结果很大时,EXISTS
比IN
快得多。
IN
比EXISTS
快,当子查询结果非常小时。
基于成本优化器:
没有区别。【讨论】:
证明你的论点?我认为 IN 不会比 EXISTS 更快! @Nawaz 如何证明 IN 总是比 EXISTS 慢? 查询优化器实施不当?我似乎在某些 RDBM 中发生了这样的事情(尽管不完全是这种情况)...... EXISTS 返回纯布尔值,这总是比比较字符串或大于 BIT/布尔类型的值要快。 IN 可能是也可能不是布尔比较。由于编程更喜欢 EXPLICIT 用于稳定性(ACID 的一部分),因此通常首选 EXISTS。 为什么这个被赞了这么多次?这种基于假设的陈述完全没有理由应该是普遍正确的。【参考方案11】:exists
关键字可以以这种方式使用,但实际上它是为了避免计数:
--this statement needs to check the entire table
select count(*) from [table] where ...
--this statement is true as soon as one match is found
exists ( select * from [table] where ... )
这在您有if
条件语句的情况下最有用,因为exists
可能比count
快很多。
in
最适合用于需要传递静态列表的地方:
select * from [table]
where [field] in (1, 2, 3)
当您在in
语句中有一个表时,使用join
更有意义,但大多数情况下它应该无关紧要。无论哪种方式,查询优化器都应该返回相同的计划。在某些实现中(大多数是较旧的,例如 Microsoft SQL Server 2000)in
查询将始终获得nested join 计划,而join
查询将酌情使用嵌套的merge 或hash。更现代的实现更智能,即使使用in
也可以调整计划。
【讨论】:
您能否详细说明“当您在 in 语句中有一个表时,使用连接更有意义,但这并不重要。查询优化器将以任何方式返回相同的计划。 “?不是查询优化器部分,您可以使用JOIN
代替IN
的部分。
select * from [table] where [field] in (select [field] from [table2])
返回与select * from [table] join [table2] on [table2].[field] = [table].[field]
相同的结果(和查询计划)。
@Sander 它没有:第一个查询返回来自table
的所有列,而第二个查询返回来自table
和table2
的所有内容。在一些(大部分是较旧的)SQL 数据库中,in
查询将实现为嵌套连接,而join
查询可以嵌套、合并、散列等 - 最快的方法。
好的,我应该在 select 子句中指定列,但你应该更新你的答案,因为它明确指出查询“将返回相同的计划”。
exists
可以在 case 语句中使用,因此它们也可以很方便,即select case when exists (select 1 from emp where salary > 1000) then 1 else 0 end as sal_over_1000
【参考方案12】:
区别就在这里:
select *
from abcTable
where exists (select null)
上面的查询将返回所有记录,而下面的查询将返回空。
select *
from abcTable
where abcTable_ID in (select null)
试一试并观察输出。
【讨论】:
嗯...错误:[SQL0104] 令牌)无效。在这两种情况下。您是否假设使用特定的 RDBMS?【参考方案13】:哪个更快取决于内部查询获取的查询数量:
当您的内部查询获取数千行时,EXIST 会是更好的选择 当您的内部查询获取几行时,IN 会更快EXIST 评估 true 或 false 但 IN 比较多个值。当您不知道该记录是否存在时,您应该选择 EXIST
【讨论】:
【参考方案14】:如果您使用IN
运算符,SQL 引擎将扫描从内部查询获取的所有记录。另一方面,如果我们使用EXISTS
,SQL 引擎将在找到匹配项后立即停止扫描过程。
【讨论】:
【参考方案15】:EXISTS 的性能比 IN 快。 如果大多数过滤条件都在子查询中,那么最好使用 IN,如果大多数过滤条件在主查询中,那么最好使用 EXISTS。
【讨论】:
这种说法真的没有任何证据支持,是吗?【参考方案16】:如果子查询返回多个值,您可能需要执行外部查询 - 如果条件中指定的列中的值与子查询结果集中的任何值匹配。要执行此任务,您需要使用 in
关键字。
您可以使用子查询来检查一组记录是否存在。为此,您需要使用带有子查询的exists
子句。 exists
关键字总是返回真或假值。
【讨论】:
【参考方案17】:如果您使用 IN 运算符,SQL 引擎将扫描从内部查询获取的所有记录。另一方面,如果我们使用 EXISTS,SQL 引擎将在找到匹配项后立即停止扫描过程。
【讨论】:
@ziggy 解释一下?这几乎就是公认的答案所说的。在必须检查每条记录时,exists 可以在找到一条记录后立即停止。 不,不正确。IN
和 EXISTS
可以等价并相互转换。【参考方案18】:
Exists
关键字评估真或假,但IN
关键字比较相应子查询列中的所有值。
另一个Select 1
可以与Exists
命令一起使用。示例:
SELECT * FROM Temp1 where exists(select 1 from Temp2 where conditions...)
但是IN
效率较低,所以Exists
更快。
【讨论】:
【参考方案19】:当子查询结果很大时EXISTS
比IN
快得多。当子查询结果很小时IN
比EXISTS
快。
CREATE TABLE t1 (id INT, title VARCHAR(20), someIntCol INT)
GO
CREATE TABLE t2 (id INT, t1Id INT, someData VARCHAR(20))
GO
INSERT INTO t1
SELECT 1, 'title 1', 5 UNION ALL
SELECT 2, 'title 2', 5 UNION ALL
SELECT 3, 'title 3', 5 UNION ALL
SELECT 4, 'title 4', 5 UNION ALL
SELECT null, 'title 5', 5 UNION ALL
SELECT null, 'title 6', 5
INSERT INTO t2
SELECT 1, 1, 'data 1' UNION ALL
SELECT 2, 1, 'data 2' UNION ALL
SELECT 3, 2, 'data 3' UNION ALL
SELECT 4, 3, 'data 4' UNION ALL
SELECT 5, 3, 'data 5' UNION ALL
SELECT 6, 3, 'data 6' UNION ALL
SELECT 7, 4, 'data 7' UNION ALL
SELECT 8, null, 'data 8' UNION ALL
SELECT 9, 6, 'data 9' UNION ALL
SELECT 10, 6, 'data 10' UNION ALL
SELECT 11, 8, 'data 11'
查询 1
SELECT
FROM t1
WHERE not EXISTS (SELECT * FROM t2 WHERE t1.id = t2.t1id)
查询 2
SELECT t1.*
FROM t1
WHERE t1.id not in (SELECT t2.t1id FROM t2 )
如果在t1
中你的 id 有 null 值,那么查询 1 会找到它们,但查询 2 找不到 null 参数。
我的意思是IN
不能与null比较任何东西,所以它没有null的结果,但EXISTS
可以将所有东西都与null比较。
【讨论】:
这个答案是对汤姆·凯特的情绪的合理概括(asktom.oracle.com/pls/asktom/…) 我认为这个答案是基于直觉的,这很公平。但这不可能是普遍正确的。例如,Ingres 几乎肯定不是这样,它将两个等效的 SQL 查询解析为相同的 QUEL 查询,当涉及到以多种方式编写相同的东西时,它缺乏 SQL 的 - 咳咳 - “丰富性”。 当且仅当 t2.id 被定义为“NOT NULL”时,这两个查询在逻辑上是等价的。要在表定义中授予不依赖项的等效性,第二个查询应该是“SELECT t1.* FROM t1 WHERE t1.id not in (SELECT t2.id FROM t2 其中 t2.id 不为空 )"【参考方案20】:据我所知,当子查询返回 NULL
值时,整个语句将变为 NULL
。在这种情况下,我们使用EXITS
关键字。如果我们想比较子查询中的特定值,那么我们使用IN
关键字。
【讨论】:
【参考方案21】:我认为,
EXISTS
是当您需要将查询结果与另一个子查询匹配时。
Query#1 结果需要在 SubQuery 结果匹配的地方检索。一种加入..
例如。选择客户表#1 也已下订单表#2
IN 是检索特定列的值是否位于 IN
列表 (1,2,3,4,5)
例如。选择位于以下邮政编码中的客户,即 zip_code 值位于 (....) 列表中。
何时使用一个而不是另一个...当你觉得它读得恰当时(更好地传达意图)。
【讨论】:
以上是关于Sql中in和exists详解的主要内容,如果未能解决你的问题,请参考以下文章