MySQL:至少参与xxx参与的全部事件

MySQL:至少参与xxx参与的全部事件 – WhiteNight's Site

标签:MySQL, 数据库

这玩意,期末要考,还是重点。所以不得不仔细思考思考怎么写了。

什么时候用NOT EXISTS

双重否定表肯定

之前虽然已经讲过了,不过那一篇文章讲的是参与了全部事件的xxx,而本文相比于那篇文章还多了一个限制条件:参与的是xxx参与的全部事件。

等会我们会用具体例子来解释什么叫“参与了xxx参与的全部事件”,现在先来回顾一下什么时候要用嵌套NOT EXISTS查询。

假设我要找出购买了全部商品的顾客,能用EXISTS吗?不行。因为EXISTS只要碰到任意一条匹配的记录就会退出循环,并返回true。

那我们只能用NOT EXISTS去查询“没有购买全部商品的用户”,再在最外层套层NOT EXISTS,表示“查询不存在没有购买全部商品的用户”,原理总的来说就是:双重否定表肯定。

具体例子

查询至少参与了xxx参与的全部事件的对象

首先理解这句话是啥意思:查询至少参与了A参与的全部事件的对象B。

“至少”,那么意味着B除了参与了A参与的全部事件,还有可能参与了A没有参与的事件。

但这部分我们并不需要管,我们只需要先查询出A参与了哪些事件,得到结果集firstset;再对firstset查询B是否有参与firstset中的全部事件,最后即可得到结果。

这里以头歌的两道题为例。

用 NOT EXISTS 实现查询至少参与过”202002020217″选手参与过的所有比赛的选手信息

先筛选,再查询

任务描述

用 NOT EXISTS 实现查询至少参与过”202002020217″选手参与过的所有比赛的选手信息,contest_id不为NULL

相关知识

1、users为选手信息表; users表如下图(仅显示前几条):

,

现已构建users表,结构信息如下:

,

2、solution为选手提交的题目解答 solution表如下图(仅显示前几条):

,

现已构建solution表,结构信息如下:

,

怎么写呢?首先我们要先筛选,筛选出A参与了哪些比赛,再对得到的结果集进行二次筛选。

单纯查询“A参与的全部比赛”并不难。However,在solution表中,contest_id可能为空,表示“该题目不属于任意比赛。所以我们要作个非空判断。

SELECT *
FROM solution AS B
WHERE B.user_id='202002020217' AND B.contest_id IS NOT NULL

此时我们得到了第一个结果集firstset。这个结果集包含了所有A参与的比赛。那么接下来就是进行二次筛选,找出哪些人参与的比赛都能在firstset中找到记录。

那么最内层的NOT EXISTS就表示:

  • 如果有个B人对于A参与的任意比赛都没有参与,那它肯定不是我们要找的对象;
  • 如果有个B人只参与了部分A参与的比赛,虽然能匹配到部分比赛的记录,但是由于内层循环会匹配每一条记录,即它会去匹配所有A参与过的比赛。那么对于剩下B没参加的比赛而言它仍然找不到匹配的记录。

上面这两种情况都会导致内层的NOT EXISTS最后返回true。而true对与最外层的NOT EXISTS而言,就相当于在告诉它:不要选择这些条件为true的记录。所以最后我们才能得到“参与了A参与的全部比赛的选手”。

SELECT A.*
FROM users AS A 
WHERE NOT EXISTS(
    SELECT 1
    FROM solution AS B
    WHERE B.user_id='202002020217' AND B.contest_id IS NOT NULL
    AND NOT EXISTS(
        SELECT 1
        FROM solution AS C
        WHERE A.user_id=C.user_id AND B.contest_id=C.contest_id
    )
)

总的来说,这种题的重点

  • 先对哪个结果集进行筛选?(条件筛选)
  • 最后得到的结果集应由哪些记录组成?(逐条筛选)
  • 条件有没有可能为NULL?(因为NOT EXISTS无法判断NULL,它会把所有为NULL的记录都当作“存在”并返回true)

第7关:求至少用了供应商 S1所供应的全部零件的工程号 JNO

先看懂题

这种题要是能看懂题目,再套两个NOT EXISTS就差不多能写出来了。问题是能不能看懂题。

任务描述

求至少用了供应商 S1所供应的全部零件的工程号 JNO

相关知识

供应情况表SPJ由供应商代码(SNO)、零件代码(PNO)、工程项目代码(JNO)、供应数量(QTY)组成,标识某供应商 供应某种零件 给某工程项目的数量为QTY。 SPJ表如下图:

,

现已构建SPJ表,结构信息如下:

,

我反正是第一眼没看懂。什么玩意?什么叫用了供应商S1供应的全部零件?我看不懂啊。

再仔细看看,S1就供应了两种零件,P1和P2。简单来说,这么长一句话浓缩之后就是:找出用过P1和P2的工程。我也真的是服了出题人了,这句子都能给你想到。

那么还是老样子,先条件筛选再逐条筛选。条件是什么?S1生产的零件。那么先条件筛选

SELECT B.*
FROM SPJ AS B 
WHERE SNO='S1'

接下来对上面得到的结果集firstset逐条筛选,也就是套两个NOT EXISTS

SELECT DISTINCT A.JNO
FROM SPJ AS A 
WHERE NOT EXISTS(
    SELECT B.*
    FROM SPJ AS B 
    WHERE SNO='S1'
    AND NOT EXISTS(
        SELECT C.*
        FROM SPJ AS C 
        WHERE B.PNO=C.PNO AND A.JNO=C.JNO
    )
)

猜你喜欢

转载自blog.csdn.net/white_night_SZTU/article/details/134067011