我对 SQL 查询调优相当陌生。我一直在尝试了解如何编写等效的查询。在浏览J. Widom 教授的斯坦福在线视频讲座时,她提到了一些子查询,如果不JOIN
使用DISTINCT
. 例如,看这个:
4:45 / 20:13 - GPA 示例
SELECT GPA
FROM Student
WHERE sID in (select sID from Apply where major = 'CS');
Run Code Online (Sandbox Code Playgroud)6:39 / 20:13 - 学生申请 CS 而不是 EE
SELECT sID, sName
FROM Student
WHERE sID IN (select sID from Apply where major = 'CS')
AND sID NOT IN (select sID from Apply where major = 'EE');
Run Code Online (Sandbox Code Playgroud)我的问题是如何知道使用子查询编写的 SQL 语句是否将具有使用连接编写的等效语句。我很舒服,如果在答案中,有人喜欢使用关系代数表示法。
我在网上搜索了很多,找不到合适的答案。
Schema 和表创建(对于 PostgreSQL)如下,
CREATE TEMP TABLE college AS
SELECT cname::text, state::text, enrollment::int
FROM ( VALUES
('Stanford', 'CA', 15000),
('Berkeley', 'CA', 36000),
('MIT', 'MA', 10000),
('Cornell', 'NY', 21000)
) AS College(cname, state, enrollment);
CREATE TEMP TABLE student AS
SELECT sid::int, sname::text, gpa::real, sizehs::int
FROM ( VALUES
(123, 'Amy', 3.9, 1000),
(234, 'Bob', 3.6, 1500),
(345, 'Craig', 3.5, 500),
(456, 'Doris', 3.9, 1000),
(567, 'Edward', 2.9, 2000),
(678, 'Fay', 3.8, 200),
(789, 'Gary', 3.4, 800),
(987, 'Helen', 3.7, 800),
(876, 'Irene', 3.9, 400),
(765, 'Jay', 2.9, 1500),
(654, 'Amy', 3.9, 1000),
(543, 'Craig', 3.4, 2000)
) AS Student(sid, sname, gpa, sizehs);
CREATE TEMP TABLE apply AS
SELECT sid::int, cname::text, major::text, decision::text
FROM ( VALUES
(123, 'Stanford', 'CS', 'Y'),
(123, 'Stanford', 'EE', 'N'),
(123, 'Berkeley', 'CS', 'Y'),
(123, 'Cornell', 'EE', 'Y'),
(234, 'Berkeley', 'biology', 'N'),
(345, 'MIT', 'bioengineering', 'Y'),
(345, 'Cornell', 'bioengineering', 'N'),
(345, 'Cornell', 'CS', 'Y'),
(345, 'Cornell', 'EE', 'N'),
(678, 'Stanford', 'history', 'Y'),
(987, 'Stanford', 'CS', 'Y'),
(987, 'Berkeley', 'CS', 'Y'),
(876, 'Stanford', 'CS', 'N'),
(876, 'MIT', 'biology', 'Y'),
(876, 'MIT', 'marine biology', 'N'),
(765, 'Stanford', 'history', 'Y'),
(765, 'Cornell', 'history', 'N'),
(765, 'Cornell', 'psychology', 'Y'),
(543, 'MIT', 'CS', 'N')
) AS apply(sid, cname, major, decision);
Run Code Online (Sandbox Code Playgroud)
我的问题是如何知道使用子查询编写的 SQL 语句是否具有使用联接编写的等效 SQL 语句。如果在答案中有人喜欢使用关系代数符号,我会感到很舒服。
好问题。
那么让我们看看她给出的第一个问题..
SELECT GPA
FROM Student
WHERE sID in (SELECT sID from Apply where major = 'CS');
Run Code Online (Sandbox Code Playgroud)
然后,让我们看一下带有连接的类似查询,
SELECT *
FROM student
JOIN apply ON (student.sid = apply.sid AND major = 'CS');
Run Code Online (Sandbox Code Playgroud)
我故意更改了第一列和第二列中的列。这里的“GPA”,具体来说是student.GPA
。
如果如下,则相关子查询不能重写为简单的 join
,
SELECT
语句具有聚合WHERE gpa in (SELECT max(gpa)..)
(如果连接需要聚合或分组,则必须使用派生表或虚拟表:JOIN ( SELECT.. ) AS t
.JOIN
上面的条件不是唯一的,我们假设有一个student
匹配行同时具有公共sid
和major=CS
。该假设失败的结果集可能会增长或收缩。你可以很容易地测试一下..
SELECT sid, count(*)
FROM apply
WHERE major='CS'
GROUP BY sid
HAVING count(*) > 1;
Run Code Online (Sandbox Code Playgroud)
这会返回两行,两个sid
行都会在连接中被欺骗。
sid | count
-----+-------
123 | 2
987 | 2
Run Code Online (Sandbox Code Playgroud)
作为清晰度练习,您可以DISTINCT
自己向下推,并且可以再次使用 重写JOIN
。
SELECT GPA
FROM student
JOIN (
SELECT DISTINCT sid, major
FROM apply
) AS apply
ON (student.sid = apply.sid AND major = 'CS');
Run Code Online (Sandbox Code Playgroud)
甚至..
SELECT GPA
FROM student
JOIN (
SELECT DISTINCT sid, major
FROM apply
WHERE major = 'CS'
) AS apply
USING (sid);
Run Code Online (Sandbox Code Playgroud)
第二个例子,
SELECT sID, sName
FROM Student
WHERE sID IN (select sID from Apply where major = 'CS')
AND sID NOT IN (select sID from Apply where major = 'EE');
Run Code Online (Sandbox Code Playgroud)
正是第一个例子的受害者。但是,那NOT IN
,
UNIQUE
因为我们排除了结果。NOT IN
为反连接( LEFT OUTER JOIN ... ON NULL
) 可能会更快。major = 'EE'
这是一个例子,
SELECT student.sID, sName
FROM Student
LEFT OUTER JOIN apply ON (student.sid = apply.sid AND major = 'EE')
WHERE student.sID IN (select sID from Apply where major = 'CS')
AND apply.sid IS NULL;
Run Code Online (Sandbox Code Playgroud)
INNER JOINS
)可以增加或减少结果集中的行数。
JOIN
条件永远不为真,则 an 上的结果INNER JOIN
将被修剪。t1 JOIN t2
,
t1
、 和t2
没有重复行,那么SELECT t1.*, t2.*
应该没有重复行。有些东西应该有所不同。t1
具有t2
重复行,则JOIN
包含这些行的 a 将导致重复的输出行。SELECT
是 的子集t1
,或者 的子集t2
(就像你上面只选择 GPA 时所做的那样),那么可能会有一些看起来像是骗局的东西,尽管事实并非如此。如果您选择了,student.GPA, apply.*
您就会看到欺骗的原因。