mysql中关于exists的讲解

我认为exists语法是mysql中一个很强大的工具,可以简单地实现某些复杂的数据处理。

下面我谈谈与exists有关的三个方面。


all 与 any

首先,看到了exists,难免还会想到all和any,它们比exists容易理解一些。all 和 any都能让一行数据与多行数据进行比较,这是它们的主要功能。

create table T(X int);
insert into T(X) values(1),(2),(3),(4); # eg.1
select * from T where X > all( select * from T where X < 3 ); #输出3,4 # eg.2
select * from T where X > any( select * from T where X > 1 ); #输出3,4

先看eg.1, 显然select * from T where X < 3结果是1,2;而all要求存在X大于集合{1,2}内的任意元素,即3,4。

同理,对于eg.2,select * from T where X > 1结果是2,3,4;any的要求是存在X大于集合{2,3,4}内的某个元素即可,即3,4。


划分表

在说exists之前,再看看一个比较特别的语句,关于表(table)的“划分”用法。

eg.1

# fruitTable
Id Name Class Count Date
1 苹果 水果 10 2011-7-1
1 桔子 水果 20 2011-7-2
1 香蕉 水果 15 2011-7-3
2 白菜 蔬菜 12 2011-7-1
2 青菜 蔬菜 19 2011-7-2

现在要求进行筛选,条件是Id唯一,Date选最近的一次

这种筛选条件潜藏着对于表的划分要求。以fruitTable为例,需要划分为2个子表,Id为1的为一个子表、Id为2的为另一个子表,再从各自子表里面选出时间最大的那个元组。

先看看下面一个错误的解法

SELECT DISTINCT Id, Name, Class, Count, Date FROM fruitTable t1
WHERE (Date IN
(SELECT MAX(Date) FROM fruitTable t2 GROUP BY Id)); # 结果
1 桔子 水果 20 2011-7-2
1 香蕉 水果 15 2011-7-3
2 青菜 蔬菜 19 2011-7-2

这周解法在逻辑上有漏洞。它将不同Id的最大时间混在了一起,没有真正地划分表格。

再来看看正确的解法

划分表格的思路是正确的,但问题是怎么划分,如果另外创建2个新的table,那这样显然太麻烦了,于是有了下面这种写法。

SELECT DISTINCT Id, Name, Class, Count, Date FROM fruitTable t1
WHERE (Date =
(SELECT MAX(Date) FROM fruitTable t2 WHERE t2.Id=t1.Id));

注意WHERE t2.Id=t1.Id 很巧妙地 对表t2 基于t2.Id=t1.Id这个标准 进行了划分。可以推导一下,比如遍历表t1,先是第1个元组: 1 苹果 水果 10 2011-7-1, 可以知道t1.Id=1, 带入第2个select: (SELECT MAX(Date) FROM fruitTable t2 WHERE t2.Id=1) , 观察这个select语句的筛选条件WHERE t2.Id=1, 发现它的范围限定在了Id为1的元组内,聚集函数MAX(Date)返回Id为1的所有元组中Date最大的值(2011-7-3)。

因此对于表t1, 当t1.Id=1时,只有Date=2011-7-3的元组才会被选出来;而当tl.Id=2时,第2个select又变为SELECT MAX(Date) FROM fruitTable t2 WHERE t2.Id=2, 返会所有Id=2的元组中Date的最大值(2011-7-2)。

可以发现,表t2是受t1.Id控制的,根据t1.Id的不同而被划分为不同的子表,这就是表的划分,并且不需要另外创建新的表。


exists

先说说exists的基本用法

create table R(
X int, Y varchar(5), Z varchar(5)
); create table S(
Y varchar(5), Z varchar(5), Q int
); insert into R(X,Y,Z) values(
1,'a','A'
),(
1,'b','B'
),(
1,'a','B'
),(
1,'c','C'
),(
2,'a','B'
),(
2,'b','B'
),(
2,'c','A'
),(
3,'z','Z'
); insert into S(Y,Z,Q) values(
'b','B',1
),(
'a','B',2
); ----------------------------- select * from R where exists( select * from S where S.Y='b' and R.Y=S.Y );
# 结果
'1', 'b', 'B'
'2', 'b', 'B'

对于exists可以先简单地理解为if判断。

比如语句select * from R where exists( select * from S where S.Y='b' and R.Y=S.Y );就可以理解为 从表R中筛选出满足条件 S.Y='b' and R.Y=S.Y (select * from S where S.Y='b' and R.Y=S.Y) 的元组。

这个性质可以看出2个特性

  • 首先exists()括号内的表不会影响最终返回的结果。比如上面的例子,返回的结果始终是关于表R的元组,和表S没有任何关系
  • 对于exists()语句,关键的是括号内的where子句。对于exists( select * from S where S.Y='b' and R.Y=S.Y ) 这种语句,可以直接当作 if( S.Y== 'b' and R.Y ==S.Y )。当然也不是说select不重要,比如exists( select 1 from S where S.Y='b' and R.Y=S.Y )是永远为真的条件。

理清上面2点,我们就更能意识到exists非常像是一个关于条件判断的语句。

下面例子类似

# 选了张三老师课的学生
select distinct sc.sid from sc
where exists (
select * from course c,teacher t
where sc.cid = c.cid and c.tid = t.tid and t.tname = "张三");

但仅仅只有exists还不够,因为很多其它语句也能实现这个功能,真正强大的是not exists。

对于存在exists只是一个元组与某个局部作比较,因为只要存在即可。而对于不存在,却是一个元组和整体做比较,因为要确定不存在,就必须遍历所有。

在这方面来说,not exists比exists更强大。

找最值

SELECT DISTINCT Id, Name, Class, Count, Date FROM fruitTable t1
WHERE (Date =
(SELECT MAX(Date) FROM fruitTable t2 WHERE t2.Id=t1.Id));
#用not exists
SELECT DISTINCT Id, Name, Class, Count, Date FROM fruitTable t1
WHERE NOT EXISTS(
SELECT * FROM fruitTable t2 WHERE t2.Id=t1.Id and t2.Date > t1.Date );

这里not exists同样可以看作not if,关键是明白哪部分条件被否定(not)。根据之前的理论,这里条件明显是t2.Id=t1.Id and t2.Date > t1.Date , 而t2.Id=t1.Id不能作为否定的对象,因为这是必然存在的(自己想想,t1和t2内容一样),用来限定表t2的范围(即之前说的划分子表),再看t2.Date > t1.Date,这才是否定的部分,即对于t2中Id为t1.Id的所有元组的Date都不大于t1.Date,而此时的t1.Date也即最大值。

嵌套not exists

还有更复杂的情况,多层not exists嵌套使用。比如实现关系代数里的除法运算。

# 表R,S的定义上面已经给出  下面计算 R除以S
select distinct R1.x from R R1 where not exists (
select * from S where not exists (
select * from R R2 where R1.X=R2.X and R2.Y=S.Y and R2.Z=S.Z ));

一个not exists只表示不存在,需要遍历所有元组才能做出判断

2个not exists嵌套,表示每一个都存在,同样需要遍历所有元组才能确定,同时还是“肯定”

这里有3个select,2个not exists。

最里面的not exists是用来否定R2.Y=S.Y and R2.Z=S.Z (因为R1.X=R2.X一定成立,这个是用来划分子表的), 最外层的not exists就用来表示不存在这个意思,你会发现最后这个句子表达的意思就是关系代数里面除法的定义。


使用联合来解决exists问题

因为MySQL每次的操作都是基于行的,当涉及到表与表之间类似集合的关系时,处理起来比较麻烦。比如下面这个问题。

insert into R(X,Y) values(
1,'a'
),(
1,'b'
),(
1,'B'
),(
1,'C'
),(
2,'A'
),(
2,'c'
),(
3,'z'
); insert into S(Y,Q) values(
'b',1
),(
'B',2
); #问题:表R内,对于X值相同的行组成一组(或叫集合)。在这样的每组元素中,要求R(Y)中不能出现与S(Y)相同的值,求这样的组的X值有哪些。
#这种问题是关于集合之间的关系,不同于 一行与一个集合之间的关系。
#下面运用之前讲的not exists来求解
select distinct X from R R1 where not exists (
select * from R R2 where R2.X=R1.X and R2.Y in (select distinct Y from S));

下面来介绍另外一种方法,联合。

仔细观察可以发现R和S之间是有关系的,因此可以将它们进行自然连接,这样就直接得到了所有R(Y)=S(Y)的值。

select distinct X from R where X not in (select distinct X from R,S where R.Y=S.Y);

但是对于代码可读性来说,in和exists比派生表联合优雅

mysql中关于exists的深入讲解的更多相关文章

  1. mysql中not exists的简单理解

    http://www.cnblogs.com/glory-jzx/archive/2012/07/19/2599215.html http://sunxiaqw.blog.163.com/blog/s ...

  2. Mysql中EXISTS关键字用法、总结

    在做教务系统的时候,一个学生(alumni_info)有多个教育经历(alumni_education),使用的数据库是mysql,之前使用左链接查询的,发现数据量才只有几万条时,查询就很慢了,早上想 ...

  3. MySql中in和exists效率

    mysql中的in语句是把外表和内表作hash 连接,而exists语句是对外表作loop循环,每次loop循环再对内表进行查询.一直大家都认为exists比in语句的效率要高,这种说法其实是不准确的 ...

  4. 浅析MySQL中exists与in的使用

    exists对外表用loop逐条查询,每次查询都会查看exists的条件语句,当 exists里的条件语句能够返回记录行时(无论记录行是的多少,只要能返回),条件就为真,返回当前loop到的这条记录, ...

  5. 转!!MySQL中的存储引擎讲解(InnoDB,MyISAM,Memory等各存储引擎对比)

    MySQL中的存储引擎: 1.存储引擎的概念 2.查看MySQL所支持的存储引擎 3.MySQL中几种常用存储引擎的特点 4.存储引擎之间的相互转化 一.存储引擎: 1.存储引擎其实就是如何实现存储数 ...

  6. mysql中in和exists二者的区别和性能影响

    mysql查询语句in和exists二者的区别和性能影响 还记得一次面试中被人问到in 和 exists的区别,当然只是草草做答,现在来做下分析. mysql中的in语句是把外表和内表作hash 连接 ...

  7. 浅析MySQL中exists与in的使用 (写的非常好)

    转自http://sunxiaqw.blog.163.com/blog/static/990654382013430105130443/ exists对外表用loop逐条查询,每次查询都会查看exis ...

  8. Mysql数据库中的EXISTS和NOT EXISTS

    SQL语言中没有蕴含逻辑运算.但是,可以利用谓词演算将一个逻辑蕴含的谓词等价转换为:p->q ≡┐p∨q. 我们通过一个具体的题目来分析:(具体的表和数据详见文章:Mysql数据库中的EXIST ...

  9. 浅析mysql中exists 与 in 的使用

    一.exists的使用    exists对外表用loop逐条查询,每次查询都会查看exists的条件语句,当exists里的条件语句能够返回记录行时(无论记录行是的多少,只要能返回),条件就为真,返 ...

随机推荐

  1. Nginx之负载均衡配置(一)

    前文我们聊了下nginx作为反向代理服务器,代理后端动态应用服务器的配置,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/12430543.html:今天我们来聊 ...

  2. java算法--循环队列

    循环队列 我们再用队列得时候不知道发没发现这样一个问题. 这是一个只有三个位置得队列,在进行三次加入(addqueue)操作和三次取出(get)操作之后再进行加入操作时候的样子.明显可以看到,队列已经 ...

  3. 基于GIS空间分析的多边形提取技术

    现有基于矢量图形的骨架线提取方法主要包括数据预处理.基于约束 Delauny 三角剖分的骨架线结点生成和骨架线的连接 3 个过程,上述过程都可利用现有 GIS 系统的数据处理.空间分析和建模功能实现. ...

  4. 在Deepin系统上装Python 3.8遇到的那些坑

    - 作为一天时间在Deepin上都没装好Python的代表,我感觉有必要记录一下我自己的解决方法 坑1-- SSL/TLS 字样错误 "pip is configured wih locat ...

  5. 配置XNA以适用VS2017进行开发

    Win10似乎已不再支持使用XNA进行游戏开发,安装XNA Game Studio经常会出现错误,显示不兼容,即使安装VS2010也不行.下面给出方法,可以使用VS2017配合XNA进行游戏开发. 1 ...

  6. can do / will do / should do 情态动词

    can do = be able to do will do = be going to do should do = ought to do 情态动词 都是表示建议 从这里发现 to do (不确定 ...

  7. 【Weiss】【第03章】链表例程的一些修改

    主要是,感觉原来的链表例程通过Node的分配形成了链表,但是没有自动消除Node的办法比较危险,一旦在clear()之前把链表赋了其它值就内存泄漏了. 所以改了析构函数,自动清理分配出来的内存.既然改 ...

  8. 再刷JVM-JVM运行时数据区域

    前言 Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域.这些区域有各自的用途,以及创建和销毁的时机,有的区域随着虚拟机进程的启动而一直存在,有些区域则是依赖用户线程 ...

  9. OneNote代码高亮

    向OneNote 2016安装NoteHighlight 下载.msi 文件,下载链接 下载之前查看自己的电脑上安装的OneNote版本以及位数(32-64) 查看方法:文件->选项->关 ...

  10. 【转】Kerberos简介

    Kerberos协议: Kerberos协议主要用于计算机网络的身份鉴别(Authentication), 其特点是用户只需输入一次身份验证信息就可以凭借此验证获得的票据(ticket-grantin ...