Skip to main content

JOIN

Prerequisites

为了演示相关语法,需要先准备实验数据。
已有 Table:

-- employee
CREATE TABLE employee (empid VARCHAR(10), name VARCHAR(10), age INT);
INSERT INTO employee (empid, name, age)
VALUES ('A101', '佐藤', 40),
('A102', '高桥', 28),
('A103', '中川', 20),
('A104', '渡边', 23),
('A105', '西泽', 35);

-- sales
DROP TABLE IF EXISTS sales;
CREATE TABLE sales (empid VARCHAR(20), sales INT, month INT);
INSERT INTO sales VALUES ('A103',101,4);
INSERT INTO sales VALUES ('A102',54,5);
INSERT INTO sales VALUES ('A104',181,4);
INSERT INTO sales VALUES ('A101',184,4);
INSERT INTO sales VALUES ('A103', 17,5);
INSERT INTO sales VALUES ('A101',300,5);
INSERT INTO sales VALUES ('A102',205,6);
INSERT INTO sales VALUES ('A104', 93,5);
INSERT INTO sales VALUES ('A103', 12,6);
INSERT INTO sales VALUES ('A107', 87,6);

创建新 Table

-- 其他营业所的 employee 
DROP TABLE IF EXISTS other_employee;
CREATE TABLE other_employee (empid VARCHAR(10), name VARCHAR(10), age INT);
INSERT INTO other_employee VALUES("A106", "中村", 26);
INSERT INTO other_employee VALUES("A107", "田中", 24);
INSERT INTO other_employee VALUES("A108", "铃木", 23);
INSERT INTO other_employee VALUES("A109", "村井", 25);
INSERT INTO other_employee VALUES("A110", "吉田", 27);

-- 营业员出生地
DROP TABLE IF EXISTS region_table;
CREATE TABLE region_table (empid varchar(10), region varchar(10));
INSERT into region_table values('A101', '东京都');
INSERT into region_table values('A102', '埼玉县');
INSERT into region_table values('A103', '神奈川县');
INSERT into region_table values('A104', '北海道');
INSERT into region_table values('A105', '静冈县');

UNION

合并显示两张表的查询结果。

SELECT * FROM employee
UNION
SELECT * FROM other_employee;

也可以合并多张表,只需要继续在后面添加 UNION 以及 SELECT
同样可以添加筛选条件 WHERE

UNION 默认会合并重复记录。当数据量较大时,这项操作会产生额外的时间消耗。可以使用 UNION ALL,不进行合并操作。

INNER JOIN

把两个 Table 通过某个连接键连接在一起处理,被称为连接(JOIN)。
只有当连接键同时存在于两张表时,INNER JOIN 进行提取。

SELECT employee.empid, name, sales
FROM employee
JOIN sales
ON employee.empid = sales.empid;

当两个表有相同 column 时,需要用 tableName.columnName 的形式。
为了清晰表明 Table 的含义,可以使用 AS 做别名。
如果 ON 做条件判断的 key 相同,可以使用 USING
也可以添加 WHERE 做筛选。

SELECT x.empid, x.name, y.sales
FROM employee as x
JOIN sales as y
USING (empid)
WHERE y.sales > 100;

JOIN 多个表。

SELECT employee.empid, name, sales, region
FROM employee
JOIN sales USING (empid)
JOIN region_table USING (empid);

LEFT JOIN

与 INNER JOIN 相对,LEFT JOIN 与 RIGHT JOIN 均为 OUTER JOIN。
LEFT JOIN 会提取所有左侧 Table 中的记录。当右侧 Table 列不存在对应键值时,仅存在于右侧 Table 中的列值将为 NULL。

SELECT employee.empid, name, sales
FROM employee
LEFT JOIN sales USING (empid);

RIGHT JOIN

RIGHT JOIN 会提取所有右侧 Table 中的记录。

SELECT employee.empid, name, sales
FROM employee
RIGHT JOIN sales USING (empid);

SELF JOIN

自连接是将数据表与自身连接。因为表明相同,所以必须使用 alias。
表中的每一条记录,都会互相产生连接。比如,employee 表中当前有 5 条记录,自连接会,将会有 25 条记录。

SELECT *
FROM employee as a
JOIN employee as b;

自连接用于排序

SELF JOIN 的结果的结果为:

empidnameageempidnameage
A105西泽35A101佐藤40
A104渡边23A101佐藤40
A103中川20A101佐藤40
A102高桥28A101佐藤40
A101佐藤40A101佐藤40
A105西泽35A102高桥28
A104渡边23A102高桥28
A103中川20A102高桥28
A102高桥28A102高桥28
A101佐藤40A102高桥28

……

可见,左侧为表中全部记录按顺序排列,右侧为第一条数据,重复。之后,左侧再次全部记录按顺序排列,右侧为第二条数据……
所以排序的思路为,对于右侧的一条数据,SELECT 左侧所有大于等于该数据(某一特定列)的值,GROUP BY 后做 COUNT。即可得到该记录该列在全部数据中的排名。

SELECT a.empid, a.name, a.age, COUNT(*)
FROM employee AS a
JOIN employee AS b
WHERE a.age >= b.age
GROUP BY b.empid;

会遇到报错,与 ONLY_FULL_GROUP_BY 规则冲突。此时可以

  • 去除 ONLY_FULL_GROUP_BY 模式,参考
  • 给列名加 ANY_VALUE 函数。ANY_VALUE(a.empid), ANY_VALUE(a.age)

子查询

使用子查询可以完成两个阶段的处理:执行查询,然后使用检索到的记录进一步执行查询。
许多使用了子查询的处理可以用其他方法取代,例如内连接。但是,子查询的处理方式更容易理解,处理效率也更高。

查询 sales 最大的记录

SELECT * FROM sales
WHERE sales IN (SELECT MAX(sales) from sales);

查询 sales 大于平均数的记录

SELECT * FROM sales
WHERE sales >= (SELECT AVG(sales) from sales);

查询 employee 表中员工,对应 sales 表中 sales 大于 200 的记录

SELECT * FROM employee
WHERE empid IN (SELECT empid FROM sales WHERE sales > 200)

EXIST

返回 employee 表中,有销售额的员工

SELECT * FROM employee
WHERE EXISTS(SELECT * FROM sales WHERE employee.empid = sales.empid)

返回 employee 表中,没有销售额的员工

SELECT * FROM employee
WHERE EXISTS(SELECT * FROM sales WHERE employee.empid = sales.empid)