查找供应每个零件的供应商的 sid
Find the sids of the suppliers who supply every part
3 tables:供应商(sid、sname、地址)、零件(pid、pname、颜色)、目录(sid、pid、成本)
找到供应每个零件的所有供应商的答案是:
SELECT C.sid
FROM Catalog C
WHERE NOT EXISTS (
SELECT P.pid
FROM Parts P
WHERE NOT EXISTS (
SELECT C1.sid
FROM Catalog C1
WHERE C1.sid = C.sid
AND C1.pid = P.pid
)
)
谁能给我解释一下这个答案?我只是有点迷路!
我听说它被解释为“寻找不存在的供应商”
他们不卖的一部分”,但我正在努力了解
SELECT C1.sid
FROM Catalog C1
WHERE C1.sid = C.sid
AND C1.pid = P.pid
)
完成那个。
所以如果我有
目录Table
詹姆斯 |锤子
詹姆斯 |铁砧
詹姆斯 |扳手
亨利 |锤子
乐华 |铁砧
部分Table
锤子
铁砧
扳手
那么在最里面的子句之后究竟返回了什么?
是吗
詹姆斯 |锤子
詹姆斯 |铁砧
詹姆斯 |扳手
亨利 |锤子
乐华 |铁砧
然后 NOT EXISTS 成功了
詹姆斯 | --
亨利 |铁砧、扳手
乐华 |锤子、扳手?
部件 table 如何减去这些值?抱歉,如果这些问题不是很清楚,我还是 SQL 的新手。
这是一个双重嵌套的 NOT EXISTS
查询(不是真的,我通常看到它就是这么叫的),它专门用于回答此类问题,即 "Are there any x true for all y?"
Here's MySQL's page on EXISTS and NOT EXISTS,里面专门提到了这个技巧
首先,在最里面的 SELECT
查询中,您要选择每家商店携带的零件。然后,在第一个 NOT EXISTS
子句中,您要选择每家商店不提供的零件。最后,在外部 NOT EXISTS
子句中,您要选择为内部 NOT EXISTS
子句返回空集的商店,这意味着它们包含每个部分。
Here is a SQLFiddle 正在执行此查询。
一句警告:如果你正在使用SQL,那么成套地思考和工作总是好的,并且用线性的方式思考,比如什么是即将跟随会让你很快陷入麻烦。不要养成习惯!
但是,有时在尝试找出像这样的复杂查询时,将这些事情视为循环会有所帮助。
所以,以fiddle中的数据为例,我们有:
suppliers:
sid, name
9, 'AAA'
8, 'BBB'
7, 'CCC'
parts:
pid, name
1, 'wood'
2, 'stone'
3, 'paper'
catalog:
cid, pid, sid
1,1,9
2,2,9
3,1,8
4,1,7
5,2,7
6,3,7
所以根据这个数据,AAA载木石,BBB只载木,CCC载木石纸。
现在让我们逐行执行查询。我们正在从 suppliers
中进行选择,我们正在决定将哪些行包含在结果集中,因此从 suppliers
中的第一行开始:9,'AAA'
。我们暂时称这一行为 S
。如果内部结果集中没有任何内容,我们将只包括这一行,所以让我们看一下。
suppliers:
sid, name
S => 9, 'AAA'
8, 'BBB'
7, 'CCC'
这个结果集是从parts
中选择的,我们将逐行检查它。当我们这样做时,S
仍然等于 9,'AAA'
。所以从 parts
中的第一行开始:1,'wood'
。我们暂时将此行称为 P
。如果下一级结果集中没有任何内容,我们只会将这一行包含在第一个内部结果集中,所以让我们移到那里。请记住 S
= 9,'AAA'
和 P
= 1,'wood'
.
suppliers:
sid, name
S => 9, 'AAA'
8, 'BBB'
7, 'CCC'
parts:
pid, name
P => 1, 'wood'
2, 'stone'
3, 'paper'
这个最里面的查询是从 'catalog' 中选择的。我们正在寻找任何行,我们将其称为 C
,在 catalog
中,其中 C.sid
等于 S.sid
并且 C.pid
等于 P.pid
。这意味着当前供应商拥有该零件。我们想要当前供应商不提供的零件,这就是我们反转结果集的原因。我们知道 S
的 sid 是 9,我们知道 P
的 pid 是 1。C
中是否有匹配的行? C
中的第一行匹配,所以我们知道这个结果集不为空。
suppliers:
sid, name
S => 9, 'AAA'
8, 'BBB'
7, 'CCC'
parts:
pid, name
P => 1, 'wood'
2, 'stone'
3, 'paper'
catalog:
cid, pid, sid
C => 1,1,9 --Match found! Don't include P in outer result set
2,2,9
3,1,8
4,1,7
5,2,7
6,3,7
现在跳回下一个最外层循环。内部结果集不为空,因此我们知道 1,'wood
不会成为此循环结果集的一部分。所以我们移动到 parts
、2,'stone'
中的下一行。我们更新 P
的值以等于该行。我们应该在结果集中包含这一行吗?我们必须使用 P
的新值再次 运行 内部查询(S
仍然没有改变)。所以我们在catalog
中寻找任何sid
等于9且pid
等于2的行。第二行匹配,所以有一个结果集。
suppliers:
sid, name
S => 9, 'AAA'
8, 'BBB'
7, 'CCC'
parts:
pid, name
1, 'wood'
P => 2, 'stone'
3, 'paper'
catalog:
cid, pid, sid
1,1,9
C => 2,2,9 --Match found! Don't include P in outer result set
3,1,8
4,1,7
5,2,7
6,3,7
跳回下一个最外层循环。内部结果集不为空,因此 2,'stone'
不会成为此循环结果集的一部分。
当我们为 3,'paper'
再次检查所有这些时,我们发现 catalog
中没有包含 sid = 9
和 pid = 3
的行。最里面的结果集是空的,所以我们知道在下一个最外面的循环中包含 P
这个值。
suppliers:
sid, name
S => 9, 'AAA'
8, 'BBB'
7, 'CCC'
parts:
pid, name
1, 'wood'
2, 'stone'
P => 3, 'paper'
catalog:
cid, pid, sid
1,1,9
2,2,9
3,1,8
4,1,7
5,2,7
6,3,7
C => --No match found, include P in outer result set
至此我们已经遍历了整个parts
table,所以我们得到了第二次循环的最终结果集,它不为空,这意味着我们找到了一部分S
不携带,所以我们知道我们不能在最终外层循环的结果集中包含 S
的当前值。
所以我们继续 suppliers
中的下一行并重新开始整个过程:
suppliers:
sid, name
9, 'AAA'
S => 8, 'BBB'
7, 'CCC'
一旦我们到达 S = 7,'CCC'
我们将经历所有这些循环,并且将在内部循环中为提供的每个 P 值找到一个匹配项,这意味着第二个循环将具有一个空集。我们找不到任何供应商不携带的零件,因此 S
的值被添加到结果集中,这意味着它们携带一切!
3 tables:供应商(sid、sname、地址)、零件(pid、pname、颜色)、目录(sid、pid、成本)
找到供应每个零件的所有供应商的答案是:
SELECT C.sid
FROM Catalog C
WHERE NOT EXISTS (
SELECT P.pid
FROM Parts P
WHERE NOT EXISTS (
SELECT C1.sid
FROM Catalog C1
WHERE C1.sid = C.sid
AND C1.pid = P.pid
)
)
谁能给我解释一下这个答案?我只是有点迷路!
我听说它被解释为“寻找不存在的供应商” 他们不卖的一部分”,但我正在努力了解
SELECT C1.sid
FROM Catalog C1
WHERE C1.sid = C.sid
AND C1.pid = P.pid
)
完成那个。
所以如果我有
目录Table
詹姆斯 |锤子
詹姆斯 |铁砧
詹姆斯 |扳手
亨利 |锤子
乐华 |铁砧
部分Table
锤子
铁砧
扳手
那么在最里面的子句之后究竟返回了什么?
是吗
詹姆斯 |锤子
詹姆斯 |铁砧
詹姆斯 |扳手
亨利 |锤子
乐华 |铁砧
然后 NOT EXISTS 成功了
詹姆斯 | --
亨利 |铁砧、扳手
乐华 |锤子、扳手?
部件 table 如何减去这些值?抱歉,如果这些问题不是很清楚,我还是 SQL 的新手。
这是一个双重嵌套的 NOT EXISTS
查询(不是真的,我通常看到它就是这么叫的),它专门用于回答此类问题,即 "Are there any x true for all y?"
Here's MySQL's page on EXISTS and NOT EXISTS,里面专门提到了这个技巧
首先,在最里面的 SELECT
查询中,您要选择每家商店携带的零件。然后,在第一个 NOT EXISTS
子句中,您要选择每家商店不提供的零件。最后,在外部 NOT EXISTS
子句中,您要选择为内部 NOT EXISTS
子句返回空集的商店,这意味着它们包含每个部分。
Here is a SQLFiddle 正在执行此查询。
一句警告:如果你正在使用SQL,那么成套地思考和工作总是好的,并且用线性的方式思考,比如什么是即将跟随会让你很快陷入麻烦。不要养成习惯!
但是,有时在尝试找出像这样的复杂查询时,将这些事情视为循环会有所帮助。
所以,以fiddle中的数据为例,我们有:
suppliers:
sid, name
9, 'AAA'
8, 'BBB'
7, 'CCC'
parts:
pid, name
1, 'wood'
2, 'stone'
3, 'paper'
catalog:
cid, pid, sid
1,1,9
2,2,9
3,1,8
4,1,7
5,2,7
6,3,7
所以根据这个数据,AAA载木石,BBB只载木,CCC载木石纸。
现在让我们逐行执行查询。我们正在从 suppliers
中进行选择,我们正在决定将哪些行包含在结果集中,因此从 suppliers
中的第一行开始:9,'AAA'
。我们暂时称这一行为 S
。如果内部结果集中没有任何内容,我们将只包括这一行,所以让我们看一下。
suppliers:
sid, name
S => 9, 'AAA'
8, 'BBB'
7, 'CCC'
这个结果集是从parts
中选择的,我们将逐行检查它。当我们这样做时,S
仍然等于 9,'AAA'
。所以从 parts
中的第一行开始:1,'wood'
。我们暂时将此行称为 P
。如果下一级结果集中没有任何内容,我们只会将这一行包含在第一个内部结果集中,所以让我们移到那里。请记住 S
= 9,'AAA'
和 P
= 1,'wood'
.
suppliers:
sid, name
S => 9, 'AAA'
8, 'BBB'
7, 'CCC'
parts:
pid, name
P => 1, 'wood'
2, 'stone'
3, 'paper'
这个最里面的查询是从 'catalog' 中选择的。我们正在寻找任何行,我们将其称为 C
,在 catalog
中,其中 C.sid
等于 S.sid
并且 C.pid
等于 P.pid
。这意味着当前供应商拥有该零件。我们想要当前供应商不提供的零件,这就是我们反转结果集的原因。我们知道 S
的 sid 是 9,我们知道 P
的 pid 是 1。C
中是否有匹配的行? C
中的第一行匹配,所以我们知道这个结果集不为空。
suppliers:
sid, name
S => 9, 'AAA'
8, 'BBB'
7, 'CCC'
parts:
pid, name
P => 1, 'wood'
2, 'stone'
3, 'paper'
catalog:
cid, pid, sid
C => 1,1,9 --Match found! Don't include P in outer result set
2,2,9
3,1,8
4,1,7
5,2,7
6,3,7
现在跳回下一个最外层循环。内部结果集不为空,因此我们知道 1,'wood
不会成为此循环结果集的一部分。所以我们移动到 parts
、2,'stone'
中的下一行。我们更新 P
的值以等于该行。我们应该在结果集中包含这一行吗?我们必须使用 P
的新值再次 运行 内部查询(S
仍然没有改变)。所以我们在catalog
中寻找任何sid
等于9且pid
等于2的行。第二行匹配,所以有一个结果集。
suppliers:
sid, name
S => 9, 'AAA'
8, 'BBB'
7, 'CCC'
parts:
pid, name
1, 'wood'
P => 2, 'stone'
3, 'paper'
catalog:
cid, pid, sid
1,1,9
C => 2,2,9 --Match found! Don't include P in outer result set
3,1,8
4,1,7
5,2,7
6,3,7
跳回下一个最外层循环。内部结果集不为空,因此 2,'stone'
不会成为此循环结果集的一部分。
当我们为 3,'paper'
再次检查所有这些时,我们发现 catalog
中没有包含 sid = 9
和 pid = 3
的行。最里面的结果集是空的,所以我们知道在下一个最外面的循环中包含 P
这个值。
suppliers:
sid, name
S => 9, 'AAA'
8, 'BBB'
7, 'CCC'
parts:
pid, name
1, 'wood'
2, 'stone'
P => 3, 'paper'
catalog:
cid, pid, sid
1,1,9
2,2,9
3,1,8
4,1,7
5,2,7
6,3,7
C => --No match found, include P in outer result set
至此我们已经遍历了整个parts
table,所以我们得到了第二次循环的最终结果集,它不为空,这意味着我们找到了一部分S
不携带,所以我们知道我们不能在最终外层循环的结果集中包含 S
的当前值。
所以我们继续 suppliers
中的下一行并重新开始整个过程:
suppliers:
sid, name
9, 'AAA'
S => 8, 'BBB'
7, 'CCC'
一旦我们到达 S = 7,'CCC'
我们将经历所有这些循环,并且将在内部循环中为提供的每个 P 值找到一个匹配项,这意味着第二个循环将具有一个空集。我们找不到任何供应商不携带的零件,因此 S
的值被添加到结果集中,这意味着它们携带一切!