开心一刻html
我要飞的更高,飞的更高,啊!ide
SQL 中的谓词指的是:返回值是逻辑值的函数。咱们知道函数的返回值有多是数字、字符串或者日期等等,但谓词的返回值所有是逻辑值(TRUE/FALSE/UNKNOW),谓词是一种特殊的函数。关于逻辑值,能够查看:神奇的 SQL 之温柔的陷阱 → 三值逻辑 与 NULL !函数
SQL 中的谓词有不少,如 =、>、<、<> 等,咱们来看看 SQL 具体有哪些经常使用的谓词spa
建立表与初始化数据code
-- 一、表建立并初始化数据 DROP TABLE IF EXISTS tbl_student; CREATE TABLE tbl_student ( id INT(8) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键', sno VARCHAR(12) NOT NULL COMMENT '学号', name VARCHAR(5) NOT NULL COMMENT '姓名', age TINYINT(3) NOT NULL COMMENT '年龄', sex TINYINT(1) NOT NULL COMMENT '性别,1:男,2:女', PRIMARY KEY (id) ); INSERT INTO tbl_student(sno,name,age,sex) VALUES ('20190607001','李小龙',21,1), ('20190607002','王祖贤',16,2), ('20190608003','林青霞',17,2), ('20190608004','李嘉欣',15,2), ('20190609005','周润发',20,1), ('20190609006','张国荣',18,1); DROP TABLE IF EXISTS tbl_student_class; CREATE TABLE tbl_student_class ( id int(8) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键', sno varchar(12) NOT NULL COMMENT '学号', cno varchar(5) NOT NULL COMMENT '班级号', cname varchar(20) NOT NULL COMMENT '班级名', PRIMARY KEY (`id`) ) COMMENT='学生班级表'; INSERT INTO tbl_student_class VALUES ('1', '20190607001', '0607', '影视7班'), ('2', '20190607002', '0607', '影视7班'), ('3', '20190608003', '0608', '影视8班'), ('4', '20190608004', '0608', '影视8班'), ('5', '20190609005', '0609', '影视9班'), ('6', '20190609006', '0609', '影视9班'); SELECT * FROM tbl_student; SELECT * FROM tbl_student_class;
相信你们对 =、>、<、<>(!=)等比较运算符都很是熟悉,它们的正式名称就是比较谓词,使用示例以下htm
-- 比较谓词示例 SELECT * FROM tbl_student WHERE name = '王祖贤'; SELECT * FROM tbl_student WHERE age > 18; SELECT * FROM tbl_student WHERE age < 18; SELECT * FROM tbl_student WHERE age <> 18; SELECT * FROM tbl_student WHERE age <= 18;
当咱们想用 SQL 作一些简单的模糊查询时,都会用到 LIKE 谓词,分为 前一致、中一致和后一致,使用示例以下对象
-- LIKE谓词 SELECT * FROM tbl_student WHERE name LIKE '李%'; -- 前一致 SELECT * FROM tbl_student WHERE name LIKE '%青%'; -- 中一致 SELECT * FROM tbl_student WHERE name LIKE '青%'; -- 后一致
若是name字段上建了索引,那么前一致会利用索引;而中一致、后一致会走全表扫描。blog
当咱们想进行范围查询时,每每会用到 BETWEEN 谓词,示例以下教程
-- BETWEEN谓词 SELECT * FROM tbl_student WHERE age BETWEEN 15 AND 22; SELECT * FROM tbl_student WHERE age NOT BETWEEN 15 AND 22;
BETWEEN 和它以后的第一个 AND 组成一个范围条件;BETWEEN 会包含临界值 15 和 22索引
SELECT * FROM tbl_student WHERE age BETWEEN 15 AND 22; -- 等价于 SELECT * FROM tbl_student WHERE age >= 15 AND age <= 22;
若不想包含临界值,那就须要这么写了
SELECT * FROM tbl_student WHERE age > 15 AND age < 22;
NULL 的水很深,具体可看:神奇的 SQL 之温柔的陷阱 → 三值逻辑 与 NULL !
有这样一个需求:查询出年龄等于 1五、18以及20的学生,咱们会用 OR 来查
-- OR SELECT * FROM tbl_student WHERE age = 15 OR age = 18 OR age = 20;
用 OR 来查没问题,可是有一点不足,若是选取的对象愈来愈多,SQL会变得愈来愈长,阅读性会愈来愈差。因此咱们能够用 IN 来代替
-- IN SELECT * FROM tbl_student WHERE age IN(15,18,20);
IN 有一种其余谓词没有的使用方法:使用子查询做为其参数,这个在平时项目中也是用的很是多的,例如:查询出影视7班的学生信息
-- IN实现,但不推荐 SELECT * FROM tbl_student WHERE sno IN ( SELECT sno FROM tbl_student_class WHERE cname = '影视7班' ); -- 联表查,推荐 SELECT ts.* FROM tbl_student_class tsc LEFT JOIN tbl_student ts ON tsc.sno = ts.sno WHERE tsc.cname = '影视7班';
不少状况下,IN 是能够用联表查询来替换的
EXISTS也是 SQL 谓词,但平时用的很少,不是说适用场景少,而是它很差驾驭,咱们用很差它。它用法与其余谓词不同,并且很差理解,另外不少状况下咱们都用 IN 来替代它了。
在真正讲解 EXSITS 示例以前,咱们先来了解下理论知识:实体的阶层 、全称量化与存在量化
SQL 严格区分阶层,不能跨阶层操做。就用咱们经常使用的谓词来举例,一样是谓词,可是与 = 、BETWEEN 等相比,EXISTS 的用法仍是大不相同的。归纳来讲,区别在于“谓词的参数能够取什么值”;“x = y”或 “x BETWEEN y ” 等谓词能够取的参数是像 “21” 或者 “李小龙” 这样的单一值,咱们称之为标量值,而 EXISTS 能够取的参数到底是什么呢?从下面这条 SQL 语句来看,EXISTS 的参数不像是单一值
SELECT * FROM tbl_student ts WHERE EXISTS ( SELECT * FROM tbl_student_class tsc WHERE ts.sno = tsc.sno );
咱们能够看出 EXISTS 的参数是行数据的集合。之因此这么说,是由于不管子查询中选择什么样的列,对于 EXISTS 来讲都是同样的。在 EXISTS 的子查询里, SELECT 子句的列表能够有下面这三种写法。
1. 通配符:SELECT * 2. 常量:SELECT '1' 3. 列名:SELECT tsc.id
也就是说以下 3 条 SQL 查到的结果是同样的
-- SELECT * SELECT * FROM tbl_student ts WHERE EXISTS ( SELECT * FROM tbl_student_class tsc WHERE ts.sno = tsc.sno ); -- SELECT 常量 SELECT * FROM tbl_student ts WHERE EXISTS ( SELECT 1 FROM tbl_student_class tsc WHERE ts.sno = tsc.sno ); -- SELECT 列名 SELECT * FROM tbl_student ts WHERE EXISTS ( SELECT tsc.sno FROM tbl_student_class tsc WHERE ts.sno = tsc.sno );
用个图来归纳下通常的谓词与 EXISTS 的区别
从上图咱们知道,EXISTS 的特殊性在于输入值的阶数(输出值和其余谓词同样,都是逻辑值)。谓词逻辑中,根据输入值的阶数对谓词进行分类。= 或者 BETWEEEN 等输入值为一行的谓词叫做“一阶谓词”,而像 EXISTS 这样输入值为行的集合的谓词叫做 “二阶谓词”。关于 “阶” ,有兴趣的能够区看个人另外一篇博客:神奇的 SQL 之层级 → 为何 GROUP BY 以后不能直接引用原表中的列
谓词逻辑中有量词(限量词、数量词)这类特殊的谓词。咱们能够用它们来表达一些这样的命题:“全部的 x 都知足条件 P” 或者 “存在(至少一个)知足条件 P 的 x ”,前者称为“全称量词”,后者称为“存在量词”,分别记做 ∀(A的下倒)、∃(E的左倒)。
SQL 中的 EXISTS 谓词实现了谓词逻辑中的存在量词,然而遗憾的是, SQL 却并无实现全称量词。可是没有全称量词并不算是 SQL 的致命缺陷,由于全称量词和存在量词只要定义了一个,另外一个就能够被推导出来。具体能够参考下面这个等价改写的规则(德·摩根定律)。
∀ x P x = ¬ ∃ x ¬P(全部的 x 都知足条件 P =不存在不知足条件 P 的 x )
∃ x P x = ¬ ∀ x ¬Px(存在 x 知足条件 P =并不是全部的 x 都不知足条件 P)
所以在 SQL 中,为了表达全称量化,须要将"全部的行都知足条件P" 这样的命题转换成 "不存在不知足条件 P 的行"
上面的理论篇,你们看了之后可能仍是有点晕,咱们结合具体的实际案例来看看 EXISTS 的妙用
上面的 tbl_student中的学生都分配到了具体的班级,假设新来了两个学生(刘德华、张家辉),他们暂时还未被分配到班级,咱们如何将他们查询出来(查询未被分配到班级的学生信息)。
-- 新来、未被分配到班级的学生 INSERT INTO tbl_student(sno,name,age,sex) VALUES ('20190610010','刘德华',55,1), ('20190610011','张家辉',46,1);
咱们最容易想到的 SQL 确定是下面这条
-- NOT IN 实现 SELECT * FROM tbl_student WHERE sno NOT IN(SELECT sno FROM tbl_student_class);
其实用 NOT EXISTS 也是能够实现的
-- NOT EXISTS 实现 SELECT * FROM tbl_student ts WHERE NOT EXISTS ( SELECT * FROM tbl_student_class tsc WHERE ts.sno = tsc.sno );
EXISTS 谓词来表达全称量化,这是EXISTS 的用法中很具备表明性的一个用法。可是须要咱们打破常规思惟,习惯从全称量化 “全部的行都××” 到其双重否认 “不××的行一行都不存在” 的转换。
假设咱们有学生成绩表:tbl_student_score
-- 学生成绩表 DROP TABLE IF EXISTS tbl_student_score; CREATE TABLE tbl_student_score ( id INT(8) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键', sno VARCHAR(12) NOT NULL COMMENT '学号', subject VARCHAR(5) NOT NULL COMMENT '课程', score TINYINT(3) NOT NULL COMMENT '分数', PRIMARY KEY (id) ); INSERT INTO tbl_student_score(sno,subject,score) VALUES ('20190607001','数学',100), ('20190607001','语文',80), ('20190607001','物理',80), ('20190608003','数学',80), ('20190608003','语文',95), ('20190609006','数学',40), ('20190609006','语文',90), ('20190610011','数学',80); SELECT * FROM tbl_student_score;
一、查询出“全部科目分数都在 50 分以上的学生”
2019060700一、2019060800三、20190610011 这三个学生知足条件,咱们须要将这 3 个学生查出来,这个 SQL 该如何写? 咱们须要转换下命题,将查询条件“全部科目分数都在 50 分以上” 转换成它的双重否认 “没有一个科目分数不满 50 分”,而后用 NOT EXISTS 来表示转换后的命题
-- 没有一个科目分数不满 50 分 SELECT DISTINCT sno FROM tbl_student_score tss1 WHERE NOT EXISTS -- 不存在知足如下条件的行 ( SELECT * FROM tbl_student_score tss2 WHERE tss2.sno = tss1.sno AND tss2.score < 50 -- 分数不满50 分的科目 );
二、查询出“数学分数在 80 分以上(包含80)且语文分数在 50 分以上(包含)的学生”
结果应该是学号分别为 2019060700一、20190608003 的学生。像这样的需求,咱们在实际业务中应该会常常遇到,可是乍一看可能会以为不太像是全称量化的条件。若是改为下面这样的说法,可能咱们一会儿就能明白它是全称量化的命题了。
"某个学生的全部行数据中,若是科目是数学,则分数在 80 分以上;若是科目是语文,则分数在 50 分以上。"
咱们再转换成它双重否认:某个学生的全部行数据中,若是科目是数学,则分数不低于 80;若是科目是语文,则分数不低于 50 ;咱们能够按照以下顺序写出咱们想要的 SQL
-- 一、CASE 表达式,确定 CASE WHEN subject = '数学' AND score >= 80 THEN 1 WHEN subject = '语文' AND score >= 50 THEN 1 ELSE 0 END; -- 二、CASE 表达式,单重否认(加上 NOT EXISTS才算双重) CASE WHEN subject = '数学' AND score < 80 THEN 1 WHEN subject = '语文' AND score < 50 THEN 1 ELSE 0 END; -- 三、结果包含了 20190610011 的 SQL SELECT DISTINCT sno FROM tbl_student_score tss1 WHERE subject IN ('数学', '语文') AND NOT EXISTS ( SELECT *FROM tbl_student_score tss2 WHERE tss2.sno = tss1.sno AND 1 = CASE WHEN subject = '数学' AND score < 80 THEN 1 WHEN subject = '语文' AND score < 50 THEN 1 ELSE 0 END ); -- 四、20190610011 没有语文成绩,剔除掉 SELECT sno FROM tbl_student_score tss1 WHERE subject IN ('数学', '语文') AND NOT EXISTS ( SELECT * FROM tbl_student_score tss2 WHERE tss2.sno = tss1.sno AND 1 = CASE WHEN subject = '数学' AND score < 80 THEN 1 WHEN subject = '语文' AND score < 50 THEN 1 ELSE 0 END ) GROUP BY sno HAVING COUNT(*) = 2; -- 必须两门科目都有分数
关于 EXISTS 的案例有不少,这里就再也不举例了,有兴趣的小伙伴能够看看:SQL 中的 EXISTS 到底作了什么?
若是你们想掌握 EXISTS,但愿你们多看看 EXISTS 的案例,看多了你就会发现其中的通性:哪些场景适合用 EXISTS。
一、SQL 中的谓词分两种:一阶谓词和二阶谓词(EXISTS),区别主要在于接收的参数不一样,一阶谓词接收的是 行,而二阶谓词接收的是 行的集合;
二、SQL 中没有与全称量词至关的谓词,可使用 NOT EXISTS 代替;
三、EXISTS 之因此难用(不是很差用,而是不会用),主要是全称量词的命题转换(确定 ⇔ 双重否认)比较难(楼主也懵!)。实际工做中每每会舍弃 EXISTS,寻找它的替代方式,多是 SQL 的替代,也多是业务方面的转换,因此说,EXISTS 掌握不了不要紧,固然,能掌握那是最好了;
《SQL基础教程》
《SQL进阶教程》