草庐IT

mysql中exists的用法详解

有梦想的攻城狮 2023-04-24 原文

前言

在日常开发中,用mysql进行查询的时候,有一个比较少见的关键词exists,我们今天来学习了解一下这个
exists这个sql关键词的用法,这样在工作中遇到一些特定的业务场景就可以有更加多样化的解决方案

语法解释

语法

SELECT column1 FROM t1 WHERE [conditions] and EXISTS (SELECT * FROM t2 );

说明

  • 括号中的子查询并不会返回具体的查询到的数据,只是会返回true或者false,如果外层sql的字段在子查询中存在则返回true,不存在则返回false
  • 即使子查询的查询结果是null,只要是对应的字段是存在的,子查询中则返回true,下面有具体的例子

执行过程

1、首先进行外层查询,在表t1中查询满足条件的column1
2、接下来进行内层查询,将满足条件的column1带入内层的表t2中进行查询,
3、如果内层的表t2满足查询条件,则返回true,该条数据保留
4、如果内层的表t2不满足查询条件,则返回false,则删除该条数据
5、最终将外层的所有满足条件的数据进行返回


贴个链接,mysql官方对于这个命令的说明: https://dev.mysql.com/doc/refman/8.0/en/exists-and-not-exists-subqueries.html;喜欢看英文原版说明的可以来这里看一下

使用案例

环境准备

👉 mysql版本: 8.0.28

👉 数据库表设计:

  • 学生表: t_student
CREATE TABLE `t_student` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '学生姓名',
  `age` int NOT NULL COMMENT '年龄',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=37 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='学生表';

导入部分数据


INSERT INTO `t_student` (`id`, `name`, `age`)
VALUES
   (1, '小张', 10),
   (2, 'chenille', 13),
   (3, '小王', 15),
   (4, '小米', 11),
   (5, 'dong', 13),
   (6, 'xi', 12),
   (7, 'chenille', 13),
   (8, '小王地方', 15),
   (9, '米来', 11),
   (10, 'dong', 13),
   (11, '呵呵', 12),
   (12, 'chenille', 13),
   (13, '小赵', 15),
   (14, '小米-0', 11),
   (15, 'bei', 13),
   (16, 'xi-xx', 12),
   (17, 'chenille', 13),
   (18, '小王-hehe', 15),
   (19, '小米-qian', 11),
   (20, 'dong', 13),
   (21, 'xi', 12),
   (22, 'chenille', 13),
   (23, '小王-1', 15),
   (24, '小米-2', 11),
   (25, 'dong-3', 13),
   (26, 'xi-0', 12),
   (27, 'chenille-4', 13),
   (28, '小王-4', 15),
   (29, '小米-7', 11),
   (30, 'dong-1', 13),
   (31, 'xi-5', 12),
   (32, '貔貅', 10),
   (33, '耄耋', 12),
   (34, '饕餮', 9),
   (35, '龙', 13),
   (36, '青牛', 12);


  • 班级学生表:t_class_student
CREATE TABLE `t_class_student` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `student_id` int NOT NULL COMMENT '学生ID',
  `class_id` int NOT NULL COMMENT '班号',
  `class_name` varchar(100)  DEFAULT '' COMMENT '班级名称',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='班级学生表';

导入部分数据

 INSERT INTO `t_class_student` (`id`, `student_id`, `class_id`, `class_name`)
VALUES
	(1, 1, 1, '一年级1班'),
	(2, 2, 1, '一年级1班'),
	(3, 3, 1, '一年级1班'),
	(4, 4, 1, '一年级1班'),
	(5, 5, 1, '一年级1班'),
	(6, 6, 1, '一年级1班'),
	(7, 7, 1, '一年级1班'),
	(8, 8, 1, '一年级1班'),
	(9, 9, 1, '一年级1班'),
	(10, 10, 1, '一年级1班'),
	(11, 11, 2, '一年级2班'),
	(12, 12, 2, '一年级2班'),
	(13, 13, 2, '一年级2班'),
	(14, 14, 2, '一年级2班'),
	(15, 15, 2, '一年级2班'),
	(16, 16, 2, '一年级2班'),
	(17, 17, 2, '一年级2班'),
	(18, 18, 2, '一年级2班'),
	(19, 19, 2, '一年级2班'),
	(20, 20, 2, '一年级2班'),
	(21, 21, 3, '二年级2班'),
	(22, 22, 3, '二年级2班'),
	(23, 23, 3, '二年级2班'),
	(24, 24, 3, '二年级2班'),
	(25, 25, 3, '二年级2班'),
	(26, 26, 3, '二年级2班'),
	(27, 27, 3, '二年级2班'),
	(28, 28, 3, '二年级2班'),
	(29, 29, 3, '二年级2班'),
	(30, 30, 3, '二年级2班'),
	(31, 31, 4, '三年级1班');
    (32, 32, 4, null);
 

常用查询

已分配班级的学生名单 👇

select * from t_student as s where exists (select student_id from t_class_student where student_id = s.id);

未分配班级的学生名单 👇

select * from t_student as s where not exists (select student_id from t_class_student where student_id = s.id);

已分配 三年级1班 的学生名单 👇

select * from t_student as s where exists (select student_id from t_class_student where student_id = s.id and class_id = 4);

已分配 并且班级是 一年级1班 和 一年级2班 的学生名单 👇

select * from t_student as s where exists (select student_id from t_class_student where student_id = s.id and class_id in (1, 2) );

查询到的字段为null,但是子查询返回的结果为true 👇

select * from t_student as s where exists (select class_name from t_class_student where student_id = s.id and class_id = 4);

查询全部学生名单 👇

select * from t_student as s where exists (select student_id from t_class_student where 1=1);

已分配 三年级1班 的并且年龄大于10岁的学生名单 👇

select * from t_student as s where age > 10 and exists (select student_id from t_class_student where student_id = s.id and class_id = 4);

exists与in的效率比较

上面的这些查询其实也可以通过 in 关键字来实现,下面我们写一下 in 关键字对应的查询语句,

通过 in 实现已分配班级的学生名单 👇

select * from t_student as s where id in (select student_id from t_class_student where student_id = s.id);

通过 in 实现未分配班级的学生名单 👇

select * from t_student as s where id not in (select student_id from t_class_student where student_id = s.id);

下面我们来分析一下这两个关键字使用效率到底那个更高呢?

循环嵌套查询执行原理

👉 循环由外向内,外层循环执行一次,内层循环则需要完整的执行一次,内层执行执行完后返回执行结果,外层循环继续执行,直到外层循环完全执行完成

循环优化策略

👉 有了上面的执行原理的说明,我们明白了一个道理:内层循环次数的多少不会影响到外层的次数,但是外层循环的次数直接会影响到内层循环的次数,外层循环每多一次,内层循环就需要多完整的一次循环,所以我们优化的目标其实就是使外层的循环次数尽量少,总结来说:小表驱动大表。小表就是外层循环,大表就是内层循环,也就是尽量减少外层循环的次数

exists和in查询原理的区别

👀 exists : 外表先进行循环查询,将查询结果放入exists的子查询中进行条件验证,确定外层查询数据是否保留

👀 in : 先查询内表,将内表的查询结果当做条件提供给外表查询语句进行比较

结论

通过上面的优化策略分析和exists和in的查询原理的分析,将这两块内容结合起来其实就得出了我们想要的一个结论:


  • 外层小表,内层大表(或者将sql从左到由来看:左面小表,右边大表): existsin 的效率高
  • 外层大表,内层小表(或者将sql从左到由来看:左面大表,右边小表): inexists 的效率高

  • 参考资料
    https://www.bilibili.com/video/BV1V64y1q7yi?spm_id_from=333.337.search-card.all.click

总结

上面我们简单介绍了一下平时我们比较少用到的一个exists的关键字,通过一些使用的实例,大家也可以基本上了解了它的使用方法,在以后的日常工作中,我们在碰到一些查询问题的时候,这个时候就有了更加多样化的选择方案啦,希望这篇文章对大家有帮助,谢谢

有关mysql中exists的用法详解的更多相关文章

  1. 使用canal同步MySQL数据到ES - 2

    文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co

  2. ruby - 你会如何在 Ruby 中表达成语 "with this object, if it exists, do this"? - 2

    在Ruby(尤其是Rails)中,您经常需要检查某物是否存在,然后对其执行操作,例如:if@objects.any?puts"Wehavetheseobjects:"@objects.each{|o|puts"hello:#{o}"end这是最短的,一切都很好,但是如果你有@objects.some_association.something.hit_database.process而不是@objects呢?我将不得不在if表达式中重复两次,如果我不知道实现细节并且方法调用很昂贵怎么办?显而易见的选择是创建一个变量,然后测试它,然后处理它,但是你必须想出一个变量名(呃),它也会在内存中

  3. ruby-on-rails - 无法安装 mysql2 0.3.14 gem - 2

    我看到其他人也遇到过类似的问题,但没有一个解决方案对我有用。0.3.14gem与其他gem文件一起存在。我已经完全按照此处指示完成了所有操作:https://github.com/brianmario/mysql2.我仍然得到以下信息。我不知道为什么安装程序指示它找不到include目录,因为我已经检查过它存在。thread.h文件存在,但不在ruby​​目录中。相反,它在这里:C:\RailsInstaller\DevKit\lib\perl5\5.8\msys\CORE\我正在运行Windows7并尝试在Aptana3中构建我的Rails项目。我的Ruby是1.9.3。$gemin

  4. ruby-on-rails - 工厂女孩/Rails : Generator to create a factory for existing model? - 2

    我在我的Rails项目中使用rspec_rails和factory_girl_railsgem。所有模型都已创建。是否有我可以运行的生成器来为现有模型创建工厂文件?例如:我已经有了一个Blog模型。RSpec允许我通过简单地运行以下命令在spec/models/blog_spec.rb生成一个模型规范文件:railsgeneraterspec:modelblog是否有我可以在命令行中运行的生成器,它会为这个现有模型生成工厂文件,位于:spec/factories/blogs.rb?我在factory_girl_rails中没有看到任何关于发电机的提及文档。

  5. ruby - 如何使用 ruby​​ mysql2 执行事务 - 2

    我已经开始使用mysql2gem。我试图弄清楚一些基本的事情——其中之一是如何明确地执行事务(对于批处理操作,比如多个INSERT/UPDATE查询)。在旧的ruby-mysql中,这是我的方法:client=Mysql.real_connect(...)inserts=["INSERTINTO...","UPDATE..WHEREid=..",#etc]client.autocommit(false)inserts.eachdo|ins|beginclient.query(ins)rescue#handleerrorsorabortentirelyendendclient.commi

  6. ruby - 有人可以解释一下在 Ruby 中注入(inject)的真实、通俗易懂的用法吗? - 2

    我正在学习Ruby,遇到了inject。我正处于理解它的风口浪尖,但当我是那种需要真实世界的例子来学习一些东西的人时。我遇到的最常见的例子是人们使用inject来添加一个(1..10)范围的总和,我不太关心这个。这是一个任意的例子。在实际程序中我会用它做什么?我正在学习,所以我可以继续使用Rails,但我不必有一个以Web为中心的示例。我只需要一些我可以全神贯注的目标。谢谢大家。 最佳答案 inject有时可以通过它的“其他”名称reduce更好地理解。它是一个对Enumerable进行操作(迭代一次)并返回单个值的函数。它有许多有

  7. ruby - 使用法拉第上传文件 - 2

    我在尝试使用Faraday将文件上传到网络服务时遇到问题。我的代码:conn=Faraday.new('http://myapi')do|f|f.request:multipartendpayload={:file=>Faraday::UploadIO.new('...','image/jpeg')}conn.post('/',payload)尝试发布后似乎没有任何反应。当我检查响应时this是我所看到的:#:post,:body=>#,#,@opts={}>,#],@index=0>>,#>],@ios=[#,#,@opts={}>,#],@index=0>,#],@index=0>

  8. ruby - rspec: raise_error 用法来匹配错误信息 - 2

    我使用raise(ConfigurationError.new(msg))引发错误我试着用rspec测试一下:expect{Base.configuration.username}.toraise_error(ConfigurationError,message)但这行不通。我该如何测试呢?目标是匹配message。 最佳答案 您可以使用正则表达式匹配错误消息:it{expect{Foo.bar}.toraise_error(NoMethodError,/private/)}这将检查NoMethodError是否由privateme

  9. 物联网MQTT协议详解 - 2

    一、什么是MQTT协议MessageQueuingTelemetryTransport:消息队列遥测传输协议。是一种基于客户端-服务端的发布/订阅模式。与HTTP一样,基于TCP/IP协议之上的通讯协议,提供有序、无损、双向连接,由IBM(蓝色巨人)发布。原理:(1)MQTT协议身份和消息格式有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。MQTT传输的消息分为:主题(Topic)和负载(payload)两部分Topic,可以理解为消息的类型,订阅者订阅(Su

  10. Tcl脚本入门笔记详解(一) - 2

    TCL脚本语言简介•TCL(ToolCommandLanguage)是一种解释执行的脚本语言(ScriptingLanguage),它提供了通用的编程能力:支持变量、过程和控制结构;同时TCL还拥有一个功能强大的固有的核心命令集。TCL经常被用于快速原型开发,脚本编程,GUI和测试等方面。•实际上包含了两个部分:一个语言和一个库。首先,Tcl是一种简单的脚本语言,主要使用于发布命令给一些互交程序如文本编辑器、调试器和shell。由于TCL的解释器是用C\C++语言的过程库实现的,因此在某种意义上我们又可以把TCL看作C库,这个库中有丰富的用于扩展TCL命令的C\C++过程和函数,所以,Tcl是

随机推荐