理解数据库的Join

1.1. 为什么需要 JOIN?

当数据分散在多个表中时,JOIN 允许你通过共同的列(如主键和外键)合并这些表的数据,从而提取更完整的信息。我们将以sqlite数据库语法与实践这篇博客锁建立的表为例来理解JOIN的使用场景。为了好理解LEFT JOIN 和 RIGHT JOIN,先准备一些数据:

--插入一些没有未分配部门的员工
INSERT INTO Employees (name, email, hire_date) VALUES
('Alice', '[email protected]', '2011-03-20'),
('Bob', '[email protected]', '2012-05-25'),
('Charlie', '[email protected]', '2013-07-30'),
('David', '[email protected]', '2014-09-10'),
('Eve', '[email protected]', '2015-11-15'),
('Frank', '[email protected]', '2016-01-20'),
('Grace', '[email protected]', '2017-03-25');
--插入一些部门 还未分配员工的
INSERT INTO Departments(department_name, parent_department_id) VALUES
('三级部门3', 4),--6
('三级部门4', 5),--7
('三级部门5', 4),--6
('三级部门6', 5),--7
('三级部门7', 4),--6
('三级部门8', 5);--7

Tips:可以用vscode新家.sql文件来提高编辑效率。

1.2. SQLite 支持的 JOIN 类型

1.2.1. INNER JOIN

    • 作用:返回两个表中匹配的行(仅保留双方都存在的记录)。

语法

SELECT 列
FROM 表1
INNER JOIN 表2 ON 表1.列 = 表2.列;

示例:根据员工的部门ID查询员工所在的部门,列显示 姓名 邮件 部门名称

SELECT e.name, e.email, d.department_name
FROM Employees e
INNER JOIN Departments d ON e.department_id = d.department_id;

结果

1.2.2. LEFT JOIN(LEFT OUTER JOIN)

    • 作用:返回左表(表1)的所有行,即使右表(表2)没有匹配。右表无匹配时填充 NULL

语法

SELECT 列
FROM 表1
LEFT JOIN 表2 ON 表1.列 = 表2.列;

示例

返回左表中所有的记录 包括还没有分配部门的员工 查询所有员工及其所属部门(包括没有分配部门的员工)
SELECT e.name,e.email,d.department_name
FROM Employees e
LEFT JOIN Departments d ON e.department_id=d.department_id;

结果:所有员工(包括没有订单分配部门的)。

1.2.3. RIGHT JOIN

    • 作用:返回右表(表2)的所有行,即使左表(表1)没有匹配。左表无匹配时填充 不显示

语法

SELECT 列
FROM 表1
LEFT JOIN 表2 ON 表1.列 = 表2.列;

示例

--查询所有的部门,包括没有员工的部门
SELECT e.name, e.email, d.department_name
FROM Employees e
RIGHT JOIN Departments d ON e.department_id = d.department_id;

结果:所有部门(包括没有员工的)。

1.2.4. FULL JOIN

    • SQLite 不直接支持 FULL JOIN,但可以通过 UNION 模拟实现。

语法

SELECT 列
FROM 表1
LEFT JOIN 表2 ON 表1.列 = 表2.列;

UNION

SELECT 列
FROM 表1
RIGHT JOIN 表2 ON 表1.列 = 表2.列,
WHERE 列 IS NULL;

示例

--查询所有结果 包括没有分配部门的员工与没有员工的部门
SELECT e.name, e.email, d.department_name
FROM Employees e
LEFT JOIN Departments d ON e.department_id = d.department_id

UNION

SELECT e.name, e.email, d.department_name
FROM Employees e
RIGHT JOIN Departments d ON e.department_id = d.department_id
WHERE e.name IS NULL;

结果:所有的员工与部门。

1.2.5. CROSS JOIN

    • 作用:返回两个表的笛卡尔积(所有可能的行组合)。

语法

SELECT 列
FROM 表1
CROSS JOIN 表2;

示例

--CROSS JOIN 查询所有员工与所有部门的组合
SELECT e.name,e.email,d.department_name
FROM Employees e
CROSS JOIN Departments;

结果:每个客户与每个产品的组合(慎用,数据量可能极大),类似排列组合。

CROSS JOIN可以用ON如果用了ON 结果与INNER JOIN相同,但是正常并不会这样使用。

SELECT e.name,e.email,d.department_name
FROM Employees e
CROSS JOIN Departments d ON e.department_id=d.department_id;

1.2.6. NATURAL JOIN

    • 作用:类似INNER JOIN 可以自动根据相同的列进行匹配,需要两个表有相同的列

语法

SELECT 列
FROM 表1
NATURAL JOIN 表2;

示例

--自动根据表中同名列进行拼接
SELECT e.name, e.email, d.department_name
FROM Employees e
NATURAL JOIN Departments d;

结果

1.3. JOIN进阶操作

1.3.1. 多表联合查询

查询员工的姓名 email 部门 工资 最新通勤信息,奖惩信息(有则显示)请假信息(有则显示)

SELECT 
    e.name AS employee_name,
    e.email,
    d.department_name,
    s.basic_salary,
    s.bonus,
    ap.description AS last_award_punishment,
    ap.date AS last_award_punishment_date,
    c.commute_date AS last_commute_date,
    c.check_in_time AS last_check_in_time,
    c.check_out_time AS last_check_out_time,
    l.leave_start_date AS last_leave_start_date,
    l.leave_end_date AS last_leave_end_date,
    l.leave_reason AS last_leave_reason,
    clb.remaining_hours AS compensatory_leave_balance
    FROM Employees e
LEFT JOIN Departments d ON e.department_id = d.department_id
LEFT JOIN Salaries s ON e.employee_id = s.employee_id
LEFT JOIN AwardsPunishments ap ON e.employee_id = ap.employee_id
LEFT JOIN Commutes c ON e.employee_id = c.employee_id
LEFT JOIN Leaves l ON e.employee_id = l.employee_id
LEFT JOIN CompensatoryLeaveBalances clb ON e.employee_id = clb.employee_id
WHERE c.commute_date = (SELECT MAX(commute_date) FROM Commutes WHERE employee_id = e.employee_id);

结果

1.3.2. 使用子查询进行复杂条件过滤

假设我们需要查询在过去四年内获得过奖励的员工及其奖励信息。这可以通过子查询来实现。

1.3.2.1. 查询

sql

SELECT e.name AS employee_name,
e.email,
d.department_name,
ap.description AS award_description,
ap.date AS award_date
FROM Employees e
JOIN Departments d ON e.department_id=d.department_id
JOIN AwardsPunishments ap ON e.employee_id=ap.employee_id
WHERE ap.type='Award' AND ap.date >= strftime('%Y-%m-%d', 'now', '-4 year');

结果:

1.3.3. 性能优化:使用索引

为了提高查询性能,确保参与 JOIN 的字段上有索引。例如,employee_iddepartment_id 应该有索引。

创建索引:

CREATE INDEX idx_employee_id ON Employees(employee_id);
CREATE INDEX idx_department_id ON Departments(department_id);

1.3.4. 使用 JOINGROUP BY 进行数据汇总

假设我们需要查询每个部门的平均工资和最高工资。这可以通过 JOINGROUP BY 来实现。

查询:

SELECT 
    d.department_name,
    AVG(s.basic_salary) AS average_salary,
    MAX(s.basic_salary) AS max_salary
FROM Departments d
LEFT JOIN Employees e ON d.department_id = e.department_id
LEFT JOIN Salaries s ON e.employee_id = s.employee_id
GROUP BY d.department_name;

结果:

1.3.5. 使用 JOINHAVING 子句进行分组过滤

假设我们需要查询员工数量超过2人的部门。这可以通过 JOINHAVING 子句来实现。

查询:

SELECT 
    d.department_name,
    COUNT(e.employee_id) AS employee_count
FROM Departments d
LEFT JOIN Employees e ON d.department_id = e.department_id
GROUP BY d.department_name
HAVING COUNT(e.employee_id) > 2;

结果:

1.3.6. 使用 JOINUNION 进行数据整合

假设我们需要查询所有获得过奖励的员工和所有请假的员工。这可以通过 JOINUNION 来实现。

查询:

SELECT 
    e.name AS employee_name,
    e.email,
    'Award' AS type,
    ap.description AS description,
    ap.date AS date
FROM Employees e
JOIN AwardsPunishments ap ON e.employee_id = ap.employee_id
WHERE ap.type = 'Award'

UNION

SELECT 
    e.name AS employee_name,
    e.email,
    'Leave' AS type,
    l.leave_reason AS description,
    l.leave_start_date AS date
FROM Employees e
JOIN Leaves l ON e.employee_id = l.employee_id;

结果:

1.4. 总结

通过这些高级用法,可以更灵活地使用 JOIN 来组合和查询多个表中的数据。JOIN 不仅可以用于简单的表连接,还可以结合子查询、WHERE 子句、ORDER BY 子句、GROUP BY 子句和 HAVING 子句来解决复杂的查询需求。

猜你喜欢

转载自blog.csdn.net/MEABUG/article/details/145462126
今日推荐