SQL学习—EXISTS谓词的用法

1-8 EXISTS谓词的用法

SQL中的谓词逻辑
SQL的基础理论:

  • 集合论
  • 谓词逻辑

理论篇

谓词:如"=、<、>、BETWEEN、LIKE、IN、IS NULL"等。
谓词逻辑提供谓词为了判断命题的真假。在关系数据库里,表中的一行可以看作是一个命题。表常常被认为是行的集合,但从谓词逻辑的观点看,可以认为是命题的集合。
数据库这种叫法有点名不副实,它存储的与其说是数据,还不如说是命题(Database in Depth: Relational Theory for Practitioners , O’Reilly Media,2005)
实体的阶层
与"="、"BETWEEN"相比,EXISTS的用法区别在于“谓词的参数可以取什么值”。
“x = y ”或“x BETWEEN y ”等谓词可以取的参数是单一值,我们称之为标量值。而EXISTS可以取的是参数是行数据的集合。由此可见,EXISTS的特殊性在于输入值的阶数。=或者BETWEEN等输入值为一行的谓词叫作“一阶谓词”。而EXISTS输入值为行的集合的谓词叫作“二阶谓词”。

实践篇

查询表中“不”存在的数据
应用场景:查找没有参加某次会议的人,目标结果如下所示,是各次会议缺席者的列表。
问题:并不是根据存在的数据查询“满足这样那样条件”的数据,而是查询“数据是否存在”。即所谓“二阶查询”。
思路:假设所有人都参加了全部会议,生成一个集合,然后减去实际参加会议的人。
(1) 所有人都参加了全部会议的集合可以通过交叉连接求得。

SELECT DISTINCT
    M1.meeting, M2.person
FROM
    Meetings M1
        CROSS JOIN
    Meetings M2

(2) 从这张表减去实际参会者的集合。

/* 用于求出缺席者的SQL语句(1):存在量化的应用 */
SELECT DISTINCT M1.meeting, M2.person
  FROM Meetings M1 CROSS JOIN Meetings M2
 WHERE NOT EXISTS
        (SELECT *
           FROM Meetings M3
          WHERE M1.meeting = M3.meeting
            AND M2.person = M3.person);

还可以用集合论的方法来解答。即使用差集运算。

/* 用于求出缺席者的SQL语句(2):使用差集运算 */
SELECT M1.meeting, M2.person
  FROM Meetings M1, Meetings M2
EXCEPT
SELECT meeting, person
  FROM Meetings;

NOT EXISTS 具备了差集运算的功能。

全称量化(1)
“肯定”<=>“双重否定”
“所有的行都xx”到其双重否定“不xx的行一行都不存在”
应用场景:查询出“所有科目分数都是50分以上的学生”
思路:将查询条件“所有科目分数都在50分以上”转换成双重否定“没有一个科目分数不满50分”

/* 全称量化(1):习惯“肯定<=>双重否定”之间的转换 */
SELECT DISTINCT student_id
  FROM TestScores TS1
 WHERE NOT EXISTS  /* 不存在满足以下条件的行 */
        (SELECT *
           FROM TestScores TS2
          WHERE TS2.student_id = TS1.student_id
            AND TS2.score < 50);   /* 分数不满50分的科目 */

查询满足下列条件的学生:
01 数学分数在80分以上
02 语文分数在50分以上
换言之,查询“某个学生的所有行数据中,如果科目是数学,则分数在80分以上;如果科目是语文,则分数在50分以上”
针对同一个集合内的行数据进行了条件分支后的全称量化。

/* 全称量化(1):习惯“肯定<=>双重否定”之间的转换 */
SELECT 
    student_id
FROM
    TestScores TS1
WHERE
    subject IN ('数学' , '语文')
        AND NOT EXISTS( SELECT 
            *
        FROM
            TestScores TS2
        WHERE
            TS2.student_id = TS1.student_id
                AND 1 = CASE
                WHEN subject = '数学' AND score < 80 THEN 1
                WHEN subject = '语文' AND score < 50 THEN 1
                ELSE 0
            END)
GROUP BY student_id
HAVING COUNT(*) = 2

全称量化(2)
应用场景:查询哪些项目已经完成到了工程1。
思路
(1) HAVING 子句用面向集合的方法
针对每个项目,将工程编号为1以下且状态为“完成”的行数,和工程编号大于1且状态为“等待”的行数加在一起,如果和等于该项目数据的总行数,则该项目符号查询条件。

/* 查询完成到了工程1的项目:面向集合的解法 */
SELECT project_id
  FROM Projects
 GROUP BY project_id
HAVING COUNT(*) = SUM(CASE WHEN step_nbr <= 1 AND status = '完成' THEN 1
                           WHEN step_nbr > 1 AND status = '等待' THEN 1
                           ELSE 0 END);

(2) 全称量化命题
“某个项目的所有行数据中,如果工程编号是1以下,则该工程已完成;如果工程编号比1大,则该工程还在等待”
这个条件仍然可以用 CASE 表达式来描述。

step_status = CASE WHEN step_nbr <= 1
                                  THEN '完成'
                                   ELSE '等待' END
/* 查询完成到了工程1的项目:谓词逻辑的解法 */
SELECT *
  FROM Projects P1
 WHERE NOT EXISTS
        (SELECT status
           FROM Projects P2
          WHERE P1.project_id = P2. project_id  /* 以项目为单位进行条件判断 */
            AND status <> CASE WHEN step_nbr <= 1 /* 使用双重否定来表达全称量化命题 */
                               THEN '完成'
                               ELSE '等待' END);

猜你喜欢

转载自blog.csdn.net/weixin_43387060/article/details/86509403