SQL08 EXISTS谓词

SQL08 EXISTS谓词

理论篇

EXISTS谓词定义

谓词是一种特殊的函数,返回值是真值 。SQL中的真值有3种:True,False,Unknown。
谓词可以按照阶数进行划分:

  • 一阶谓词:输入值为一行数据:比如 =
  • 二阶谓词:输入值为行的集合:比如EXISTS
  • 三阶谓词:输入值为“集合的集合”的谓词
  • ……

全称量化和存在量化

  • 全称量化:所有的x 都满足条件P”
  • 存在量化:存在(至少一个)满足条件P 的x,也就是EXISTS谓词
    我们可以用存在量化也就是EXISTS谓词去表示全称量化。
    即 所有的x 都满足条件P 等价于 不存在不满足条件P 的x

实践篇

查询不存在的数据

SQL08 EXISTS谓词
查询没有参加会议的某人
思路如下:假设所有人每场会议都参加了,将这个集合减去现有的集合就是没有参加会议的集合。
首先生成会议和 出席者的集合

Select DISTINCT(m1.meeting,m2.persion)
FROM meeting m1,meeting m2

接着使用EXISTS谓词实现减法

SELECT DISTINCT(m1.meeting,m2.person)
  FROM meeting m1,
       meeting m2
 WHERE NOT EXISTS(
        SELECT *
          FROM meeting m3
         WHERE m1.meeting=m3.meeting
           AND m2.person=m3.person
       )

全称量化到双重否定

SQL08 EXISTS谓词
请查询所有科目分数都在50分以上的同学
等价于
请查询不存在科目分数小于50分的同学

SELECT DISTINCT student_id
  FROM testscores t1
 WHERE NOT EXISTS(
        SELECT *
          FROM testscores t2
         WHERE t1.student_id=t2.student_id
           AND t2.score<50
       )

接着我们进阶一下,请求出满足下述条件的学生

  • 数学的分数在80分以上
  • 语文的分数在50分以上
SELECT DISTINCT student_id
  FROM testscores t1
 WHERE NOT EXISTS(
        SELECT *
          FROM testscores t2
         WHERE t1.student_id=t2.student_id
           AND 1= CASE WHEN t2.subject='语文' AND t2.score<80 THEN 1 
   				  CASE WHEN t2.subject='数学' AND t2.score<50 THEN 1
                  ELSE 0 END
       )

对于400这个学生,他没有语文成绩,需要排除掉他

SELECT DISTINCT student_id
  FROM testscores t1
 WHERE subject IN ('数学', '语文')
   AND NOT EXISTS(
        SELECT *
          FROM testscores t2
         WHERE t1.student_id=t2.student_id
           AND 1= CASE WHEN t2.subject='语文' AND t2.score<80 THEN 1 
   				  CASE WHEN t2.subject='数学' AND t2.score<50 THEN 1
                  ELSE 0 END
       )
GROUP BY t1.student_id
HAVING COUNT(*)=2 -- 排除400的学生



全称量化和谓词

SQL08 EXISTS谓词

从上述表中查询出哪些项目已经完成到了工程1

集合的解法

对于某个工程,当工程编号小于等于1时,必须是完成,当工程编号大于1时,必须是等待。等待的个数+完成的个数=总步数。

SELECT project_id
  FROM projects
 GROUP BY project_id
HAVING COUNT(*)=SUM(CASE WHEN step_nbr <=1 AND status='完成' THEN 1 CASE WHEN tep_nbr >1 AND status='等待' THEN 1 ELSE 0 END)

谓词的解法

对于某个工程,当工程编号小于等于1时,必须是完成,当工程编号大于1时,必须是等待。
等价于
对于某个工程,当工程编号小于等于1时,不存在状态是等待,当工程编号大于1时,不存在状态是完成
条件可以写成

CASE WHEN step_nbr <=1 THEN  status='完成'  ELSE status='等待'END

完整SQL如下

SELECT project_id
  FROM projects p1
 WHERE NOT EXISTS(
        SELECT *
          FROM projects p2
         WHERE p2.status<>CASE WHEN step_nbr <=1 THEN status='完成'
                    ELSE status='等待'
                     END
       )

列的全称量词

SQL08 EXISTS谓词
查询全为1的行

SELECT *
  FROM ArrayTbl
 WHERE col1 = 1
   AND col2 = 1 
   · · ·
   AND col10 = 1

利用ALL谓词

SELECT *
FROM ArrayTbl
WHERE 1 = ALL (col1, col2, col3, col4, col5, col6, col7, col8, col9, col10);

至少有一个9

SELECT *
FROM ArrayTbl
WHERE 9 = ANY (col1, col2, col3, col4, col5, col6, col7, col8, col9, col10);

查询全是NULL的行

SELECT *
FROM ArrayTbl
WHERE COALESCE(col1, col2, col3, col4, col5, col6, col7, col8, col9, col10) IS NULL;

SQL08 EXISTS谓词
选出全为1的key

SELECT *
  FROM ArrayTbl2 a1
  WHERE NOT EXISTS(
  SELECT *
  FROM ArrayTbl2 a2
    WHERE a1.key=a2.key AND a2.val<>1
  )

然而这样不只选出了C还出现了A。
原因在于SQL是三值逻辑,而EXISTS是二值逻辑。对于EXISTS而言,没有返回值就是FALSE,返回值就是TRUE。
所以A也会返回。
正确的写法应该排除全为NULL的情况

SELECT *
  FROM ArrayTbl2 a1
  WHERE NOT EXISTS(
  SELECT *
  FROM ArrayTbl2 a2
    WHERE a1.key=a2.key AND (a2.val<>1 OR a2.val IS NULL)
  )

或者使用集合的思想
极值函数解决

SELECT key
FROM ArrayTbl2
GROUP BY key
HAVING MAX(val) = 1
AND MIN(val) = 1;
SELECT key
FROM ArrayTbl2
GROUP BY key
HAVING COUNT(*)=SUM(CASE WEHN val=1 THEN 1 ELSE 0 END)
上一篇:关键词


下一篇:mysql中exists的使用问题