select

1
2
3
4
5
6
7
SELECT
product_id
FROM
products
WHERE
low_fats = 'Y'
AND recyclable = 'Y'
1
2
3
4
5
6
7
SELECT
name
FROM
Customer
WHERE
referee_id != 2
or referee_id is NULL
1
2
3
4
5
6
7
8
9
SELECT
name,
population,
area
FROM
World
WHERE
area >= 3000000
OR population >= 25000000
1
2
3
4
5
6
7
8
SELECT
DISTINCT author_id AS id
FROM
Views
WHERE
author_id = viewer_id
ORDER BY
id ASC
1
2
3
4
5
6
SELECT
tweet_id
FROM
Tweets
WHERE
LENGTH(content) > 15

Basic Joins

内连接(Inner Join):
内连接用于将两个或多个表中满足连接条件的行合并在一起,只返回那些在连接条件下匹配的行。最常见的内连接类型是等值连接,但也可以进行非等值连接。

  • 等值连接(Equi Join): 等值连接是内连接的一种,它基于两个表之间的相等条件连接这些表。例如,如果你有一个包含顾客信息的表和一个包含订单信息的表,并且想要获取每个订单对应的顾客信息,你可以使用等值连接来基于顾客ID等条件连接这两个表。

  • 非等值连接(Non-Equi Join): 非等值连接也是内连接的一种,不仅基于相等条件,还可以使用其他比较操作符(如大于、小于等)来连接表。例如,你可以使用非等值连接来查找价格高于平均价格的产品,连接条件可能是产品价格大于平均价格。

  • 自连接(Self Join): 自连接是一种特殊的内连接,它用于将表与自身连接。这在表中包含层次结构或需要比较不同行之间的数据时非常有用。例如,如果你有一个包含员工信息的表,你可以使用自连接来找出员工之间的经理关系。

外连接(Outer Join):
外连接用于联接两个表,并返回一个结果集,包括了匹配条件下的行以及未匹配条件下的行。外连接有三种类型:

  • 左连接(Left Join): 左连接返回左表中的所有行,以及右表中与左表匹配的行。如果没有匹配的行,右表中的列将包含 NULL 值。左连接通常用于保留左表的所有数据,而只获取与之关联的右表数据。

  • 右连接(Right Join): 右连接与左连接相反,它返回右表中的所有行,以及左表中与右表匹配的行。如果没有匹配的行,左表中的列将包含 NULL 值。右连接通常用于保留右表的所有数据,而只获取与之关联的左表数据。

  • 全外连接(Full Outer Join): 全外连接返回左表和右表中的所有行,并将不匹配的行用 NULL 值填充。这意味着无论是左表中没有匹配的行,还是右表中没有匹配的行,都会包含在结果集中。

交叉连接(Cross Join):
交叉连接是一种特殊的连接,它将一个表的每一行与另一个表的每一行进行组合,从而生成一个笛卡尔积。交叉连接通常用于获取所有可能的组合,但在大多数情况下会生成非常大的结果集,因此需要谨慎使用。

1
2
3
4
5
6
SELECT
EmployeeUNI.unique_id,
Employees.name
FROM
Employees
LEFT JOIN EmployeeUNI ON Employees.id = EmployeeUNI.id
1
2
3
4
5
6
7
SELECT
product_name,
year,
price
FROM
Product p
RIGHT JOIN Sales s ON p.product_id = s.product_id

或者使用左连接

1
2
3
4
5
6
7
SELECT
product_name,
year,
price
FROM
Sales s
LEFT JOIN Product p ON p.product_id = s.product_id

或者使用内连接

A INNER JOIN B ,在A中也有,在B中也有的数据才能查询出来。INNER 可省。

1
2
3
4
5
6
7
SELECT
product_name,
year,
price
FROM
Product p
Join Sales s ON p.product_id = s.product_id
1
2
3
4
5
6
7
8
9
10
SELECT
customer_id,
COUNT(customer_id) AS count_no_trans
FROM
Visits v
LEFT JOIN Transactions t ON v.visit_id = t.visit_id
WHERE
transaction_id IS NULL
GROUP BY
customer_id
1
2
3
4
5
6
SELECT
w2.id
FROM
Weather w1
JOIN Weather w2 ON DATEDIFF(w1.recordDate, w2.recordDate) = -1
AND w1.temperature < w2.temperature

或者使用等值连接

1
2
3
4
5
6
7
8
SELECT
w2.id
FROM
Weather w1,
Weather w2
WHERE
DATEDIFF(w1.recordDate, w2.recordDate) = -1
AND w1.temperature < w2.temperature
1
2
3
4
5
6
7
8
9
10
11
12
13
14
SELECT
a1.machine_id,
round(
avg(a2.timestamp - a1.timestamp),
3
) AS processing_time
FROM
Activity a1
JOIN Activity a2 ON a1.machine_id = a2.machine_id
AND a1.process_id = a2.process_id
AND a1.activity_type = 'start'
AND a2.activity_type = 'end'
GROUP BY
a1.machine_id
1
2
3
4
5
6
7
8
9
SELECT
e.name,
b.bonus
FROM
Bonus b
RIGHT JOIN Employee e ON e.empId = b.empId
WHERE
b.bonus < 1000
OR b.bonus IS NULL

思考 为什么用了右连接,还要再加b.bonus IS NULL非空条件?

因为,除了ON的连接条件,后面又加了WHERE筛选bonus小于1000的情况,这样如果不添加该条件,没有奖金的员工将无法返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
SELECT
stu.student_id,
stu.student_name,
sub.subject_name,
COUNT(exam.subject_name) AS attended_exams
FROM
Students stu
CROSS JOIN Subjects sub
LEFT JOIN Examinations exam ON stu.student_id = exam.student_id
AND sub.subject_name = exam.subject_name
GROUP BY
sub.subject_name,
stu.student_id
ORDER BY
stu.student_id,
sub.subject_name

思考 为什么分组条件不是exam.subject_name,exam.student_id

因为Examinations表中可能不会一一对应Subjects表中的课程和Students表中的学生id号,这样会导致,如果学生没有参加任何考试,该分组条件对于这样的学生无效;

或者使用子查寻,先搜索出Examination中的部分,作为结果exam左连接至Students表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
SELECT
stu.student_id,
stu.student_name,
sub.subject_name,
COALESCE(exam.attended_exams, 0) AS attended_exams
FROM
Students stu
CROSS JOIN Subjects sub
LEFT JOIN(
SELECT
student_id,
subject_name,
COUNT(*) AS attended_exams
FROM
Examinations
GROUP BY
subject_name,
student_id
) exam ON stu.student_id = exam.student_id
AND sub.subject_name = exam.subject_name
ORDER BY
stu.student_id,
sub.subject_name
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
-- testcase 0:
-- | id | name | department | managerId |
-- +-----+-------+------------+-----------+
-- | 101 | John | A | None |
-- | 102 | Dan | A | 101 |
-- | 103 | James | A | 101 |
-- | 104 | Amy | A | 101 |
-- | 105 | Anne | A | 101 |
-- | 106 | Ron | B | 101 |
--------------------------------------------
-- +------+
-- | name |
-- +------+
-- | John |
-- +------+
-- testcase 1:
-- | id | name | department | managerId |
-- | --- | ----- | ---------- | --------- |
-- | 101 | John | A | null |
-- | 102 | Dan | A | 101 |
-- | 103 | James | A | 101 |
-- | 104 | Amy | A | 101 |
-- | 105 | Anne | A | 101 |
-- | 106 | Ron | B | 101 |
-- | 111 | John | A | null |
-- | 112 | Dan | A | 111 |
-- | 113 | James | A | 111 |
-- | 114 | Amy | A | 111 |
-- | 115 | Anne | A | 111 |
-- | 116 | Ron | B | 111 |
--------------------------------------------
-- | name |
-- | ---- |
-- | John |
-- | John |
-- testcase 2:
-- | id | name | department | managerId |
-- | --- | ----- | ---------- | --------- |
-- | 101 | John | A | null |
-- | 102 | Dan | A | 101 |
-- | 103 | James | A | 101 |
-- | 104 | Amy | A | 101 |
-- | 105 | Anne | A | 101 |
-- | 106 | Ron | B | 101 |
-- | 107 | Tom | A | 102 |
-- | 108 | Tommy | A | 102 |
-- | 109 | Peter | C | 102 |
-- | 110 | Dong | A | 102 |
-- | 111 | DIDI | D | 102 |
--------------------------------------------
-- | name |
-- | ---- |
-- | John |
-- | Dan |
SELECT
e1.name
FROM
Employee e1
JOIN Employee e2 ON e2.managerId = e1.id
GROUP BY
e2.managerId
HAVING
COUNT(e2.id) >= 5

注意 名字不是唯一的,只有工号才是主键,是唯一的。不可以为了让名字匹配不重复而使用DISTINCT(e1.name),应当按照e2.managerId进行分组。