1.多表查询概念
用途:每张表都有一些信息,需要将它们整合起来查看
2.连接类型
|------|------------|----------------------|
| 连接类型 | 关键字 | 描述 |
| 内连接 | INNER JOIN | 取两表交集(只显示能找到匹配项的记录) |
| 左连接 | LEFT JOIN | 左表为主表,右表为从表(左核心,右补充) |
| 右连接 | RIGHT JOIN | 右表为主表,左表为从表(右核心,左补充) |
| 全连接 | FULL JOIN | 取两表并集(全部显示) |
| 交叉连接 | CROSS JOIN | 排列组合(不常用) |
[连接类型]
3.示例表结构
-- 部门表
CREATE TABLE departments (
dept_id INT PRIMARY KEY,
dept_name VARCHAR(50)
);
-- 员工表
CREATE TABLE employees (
emp_id INT PRIMARY KEY,
emp_name VARCHAR(50),
dept_id INT, -- 外键,关联departments表
salary DECIMAL(10, 2)
);
-- 项目表 (用于演示多对多)
CREATE TABLE projects (
project_id INT PRIMARY KEY,
project_name VARCHAR(50)
);
-- 员工-项目关联表
CREATE TABLE emp_project (
emp_id INT,
project_id INT,
PRIMARY KEY (emp_id, project_id)
);
INSERT INTO departments VALUES (1, '研发部'), (2, '市场部'), (3, '财务部');
INSERT INTO employees VALUES
(1, '张三', 1, 10000),
(2, '李四', 1, 12000),
(3, '王五', 2, 8000),
(4, '赵六', NULL, 9000); -- 注意:赵六没有部门
INSERT INTO projects VALUES (101, '项目A'), (102, '项目B');
INSERT INTO emp_project VALUES (1, 101), (1, 102), (2, 101), (3, 102);

4.查询示范
4.1内连接
SELECT a.字段名 b.字段名 FROM 表1 a INNER JOIN 表2 b ON a.共有字段名 = b.共有字段名;(其中a与b为表别名)
示例:查询每个员工姓名及其部门名称。
SELECT e.emp_name,d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id = d.dept_id;

没有赵六,因为他的
dept_id是NULL,在departments表找不到匹配项。
4.2左连接
SELECT a.字段名 b.字段名 FROM 表1 a LEFT JOIN 表2 b ON a.共有字段名 = b.共有字段名;(其中a与b为表别名)
示例:查询所有员工姓名及其部门名称(包括没有部门的员工)。
SELECT e.emp_name,d.dept_name FROM employees e LEFT JOIN departments d ON e.dept_id = d.dept_id;

4.3右连接
SELECT a.字段名 b.字段名 FROM 表1 a RIGHT JOIN 表2 b ON a.共有字段名 = b.共有字段名;(其中a与b为表别名)
示例:查询所有部门及其员工信息(包括没有员工的部门)。
SELECT e.emp_name,d.dept_name FROM employees e RIGHT JOIN departments d ON e.dept_id = d.dept_id;

4.4多表连接与表别名
示例:查询每个员工的名字、部门名称以及他参与的项目名称。
SELECT e.emp_name,d.dept_name,p.project_name FROM employees e LEFT JOIN departments d ON e.dept_id = d.dept_id INNER JOIN emp_project ep ON ep.emp_id = e.emp_id INNER JOIN projects p ON p.project_id = ep.project_id;

5.联合查询
作用 :合并多个
SELECT语句的结果集。要求:列数、数据类型必须相同或兼容。
注意: UNION vs UNION ALL:UNION会去重,UNION ALL不去重,性能更高。
示例:查询所有部门名字和员工的名字,合并成一个列表。
SELECT dept_name FROM departments UNION SELECT emp_name FROM employees;

6.全连接
实质:左/右连接 UNION 左/右连接(UNION左右互相取对立)
示例:查询所有部门名字和员工的名字
select e.emp_name,d.dept_name from employees e left join departments d on e.dept_id=d.dept_id union select e.emp_name,d.dept_name from employees e right join departments d on e.dept_id=d.dept_id;

7.例题一(双表查询)
7.1数据准备
表一
create database company;
create table employee6( emp_id int auto_increment primary key, emp_name varchar(50), age int, dept_id int);
desc employee6;
insert into employee6(emp_name,age,dept_id) values('xiaoli',19,200),('tom',26,201),('jack',30,201),('alice',24,202),('robin',40,200),('zhangsan',28,204);
select * from employee6;
表二
create table department6(dept_id int, dept_name varchar(100));
desc department6;
insert into department6 values
(200,'hr'),
(201,'it'),
(202,'sale'),
(203,'op');
select * from department6;


7.2问题
7.2.1只找出有相同部门的员工
SELECT e.emp_name,d.dept_name FROM employee6 e INNER JOIN department6 d ON e.dept_id = d.dept_id;

7.2.2查找出所有员工及所属部门
SELECT e.emp_name,IFNULL(d.dept_name,'暂无部门') FROM employee6 e LEFT JOININ department6 d ON e.dept_id = d.dept_id;

7.2.3找出所有部门包含的员工
SELECT IFNULL(e.emp_name,'暂无员工'),d.dept_name FROM employee6 e RIGHT JOINOIN department6 d ON e.dept_id = d.dept_id;

7.2.4找出所有部门包含的员工,所有员工所属的部门名称
SELECT IFNULL(e.emp_name,'暂无员工'),IFNULL(d.dept_name,'暂无部门') FROM employee6 e RIGHT JOIN department6 d ON e.dept_id = d.dept_id UNION SELECT e.emp_name,IFNULe,'暂无L(d.dept_name,'暂无部

8.例题二(三表查询)
8.1数据准备
--表一:用户表(主表)
CREATE TABLE users (
user_id INT PRIMARY KEY AUTO_INCREMENT, -- 用户ID,主键
username VARCHAR(50) NOT NULL, -- 用户名
email VARCHAR(100) NOT NULL UNIQUE -- 邮箱
);
--表二:分类表(主表)
CREATE TABLE categories (
category_id INT PRIMARY KEY AUTO_INCREMENT, -- 分类ID,主键
category_name VARCHAR(50) NOT NULL UNIQUE -- 分类名
);
--表三:文章表(关联表,包含两外键)
CREATE TABLE articles (
article_id INT PRIMARY KEY AUTO_INCREMENT, -- 文章ID,主键
title VARCHAR(200) NOT NULL, -- 文章标题
content TEXT, -- 文章内容
user_id INT, -- 外键:关联用户表 (文章作者)
category_id INT, -- 外键:关联分类表 (文章分类)
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 创建时间
-- 定义外键约束(可选,但强烈建议,用于保证数据完整性)
FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE SET NULL,
FOREIGN KEY (category_id) REFERENCES categories(category_id) ON DELETE SET NULL
);
--插入数据
INSERT INTO users (username, email) VALUES
('张三', 'zhangsan@email.com'),
('李四', 'lisi@email.com'),
('王五', 'wangwu@email.com');
INSERT INTO categories (category_name) VALUES
('技术'),
('生活'),
('旅游');
INSERT INTO articles (title, content, user_id, category_id) VALUES
('MySQL入门教程', '...正文内容...', 1, 1), -- 张三写了技术文章
('Python编程技巧', '...正文内容...', 1, 1), -- 张三写了技术文章
('我的周末生活', '...正文内容...', 2, 2), -- 李四写了生活文章
('西藏之旅', '...正文内容...', 3, 3); -- 王五写了旅游文章
insert into users values(4,'NATASHA','NATASHA@163.com');
8.2问题
8.2.1查询所有文章的详细信息,包括作者名 和分类名。(只查有作者且有分类的文章)
SELECT u.username,a.title,category_name FROM users u INNER JOIN articlesa ON a.user_id = u.user_id INNER JOIN categories c ON c.category_id = a.category_id;

8.2.2查询所有用户及其发表的文章和分类(即使用户没写文章也要显示)
SELECT u.username,IFNULL(a.title,'暂无文章'),IFNULL(c.category_name,'暂无分类') FROM users u LEFT JOIN articles a ON u.user_id = a.user_id LEFT JOIN categgories c ON a.category_id = c.category_id;

8.2.3查询所有分类下都有哪些文章,由谁所写(即使分类下没有文章也要显示)。
SELECT IFNULL(c.category_name,'玄幻'),IFNULL(a.title,'暂无文章'),u.username FROMme FROM categories c LEFT JOIN articles a ON c.category_id = a.category_id RIGHT JOIN users u On u.user_id = a.user_id;

8.2.4统计每个分类下的文章数量。
SELECT c.category_name,COUNT(a.article_id) FROM categories c LEFT JOIN articles a ON c.category_id = a.category_id GROUP BY c.category_id;
