多表查询 学习笔记

目录

1.多对多

2.一对一

3.内连接

隐式内连接

显式内连接

案例

隐式内连接

 显式内连接

4.外连接

左外连接

右外连接

案例

左外连接

右外连接

5.自连接

案例

6.联合查询

 案例

7.子查询

IN

NOT IN

ANY

SOME

ALL

标量子查询

例子

列子查询

例子

行子查询

例子

表子查询

例子


1.多对多

在数据库设计中,处理学生与课程之间的多对多关系,需要通过建立一张中间表来实现。

假设我们有两张表:学生表(student)和课程表(course)

-- 创建一个名为 `student` 的新表
CREATE TABLE student (
  -- 定义一个名为 `id` 的字段,类型为 INT,自动递增,并设置为表的主键
  id INT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
  -- 定义一个名为 `name` 的字段,类型为 VARCHAR,最大长度为 10 个字符,用于存储学生姓名
  name VARCHAR(10) COMMENT '姓名',
  -- 定义一个名为 `no` 的字段,类型为 VARCHAR,最大长度为 10 个字符,用于存储学生学号
  no VARCHAR(10) COMMENT '学号'
) -- 为整个表添加注释,说明这是一个学生表
COMMENT '学生表';
-- 创建一个名为 `course` 的新表
CREATE TABLE course (
  -- 定义一个名为 `id` 的字段,类型为 INT,自动递增,并设置为表的主键
  id INT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
  -- 定义一个名为 `name` 的字段,类型为 VARCHAR,最大长度为 10 个字符,用于存储课程名称
  name VARCHAR(10) COMMENT '课程名称'
) -- 为整个表添加注释,说明这是一个课程表
COMMENT '课程表';

如需给两张表中添加数据

-- 向 student 表中插入新数据
INSERT INTO student VALUES 
  (NULL, 'xx', 'xx'),  -- 插入第一条记录,id 自动递增,姓名为xx,学号为xx
  ...  
  (NULL, 'xx', 'xx'); 
-- 向 course 表中插入新数据
INSERT INTO course VALUES 
  (NULL, 'xx'), 
  ...  
  (NULL, 'xx'); 

中间表设计

-- 创建一个名为 `student_course` 的新表,用于存储学生和课程之间的关联
CREATE TABLE student_course (
  -- 定义一个名为 `id` 的字段,类型为 INT,自动递增,并设置为表的主键
  id INT AUTO_INCREMENT COMMENT '主键' PRIMARY KEY,
  -- 定义一个名为 `studentid` 的字段,类型为 INT,不为空,用于存储学生ID
  studentid INT NOT NULL COMMENT '学生ID',
  -- 定义一个名为 `courseid` 的字段,类型为 INT,不为空,用于存储课程ID
  courseid INT NOT NULL COMMENT '课程ID',
  -- 为 `courseid` 字段添加外键约束,引用 `course` 表的 `id` 字段
  CONSTRAINT fk_courseid FOREIGN KEY (courseid) REFERENCES course (id),
  -- 为 `studentid` 字段添加外键约束,引用 `student` 表的 `id` 字段
  CONSTRAINT fk_studentid FOREIGN KEY (studentid) REFERENCES student (id)
) -- 为整个表添加注释,说明这是一个学生课程中间表
COMMENT '学生课程中间表';

-- 向 `student_course` 表中插入关联记录
INSERT INTO student_course VALUES 
  (NULL, 1, 1),  -- 插入第一条记录,自动递增ID,学生ID为1,课程ID为1
 ...
  (NULL, 3, 4);  -- 插入第n条记录,自动递增ID,学生ID为3,课程ID为4

查询某个学生的所有课程

SELECT c.name AS CourseName
FROM course c
JOIN student_course sc ON c.id = sc.courseid
JOIN student s ON sc.studentid = s.id
WHERE s.id = studentIdValue;

查询某门课程的所有学生

SELECT s.name AS StudentName
FROM student s
JOIN student_course sc ON s.id = sc.studentid
JOIN course c ON sc.courseid = c.id
WHERE c.id = courseIdValue;

2.一对一

创建两个表来表示用户与用户详情之间的一对一关系。

CREATE TABLE tb_user (
    id INT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
    name VARCHAR(10) COMMENT '姓名',
    age INT COMMENT '年龄',
    gender CHAR(1) COMMENT '性别,1:男,2:女',
    phone CHAR(11) COMMENT '手机号'
) COMMENT '用户基本信息表';
CREATE TABLE tb_user_edu (
    id INT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
    degree VARCHAR(20) COMMENT '学历',
    major VARCHAR(50) COMMENT '专业',
    primaryschool VARCHAR(50) COMMENT '小学',
    middleschool VARCHAR(50) COMMENT '中学',
    university VARCHAR(50) COMMENT '大学',
    userid INT UNIQUE COMMENT '用户ID',
    CONSTRAINT fk_userid FOREIGN KEY (userid) REFERENCES tb_user(id)
) COMMENT '用户教育信息表';

插入数据同上...

--消除笛卡尔积无效部分
select * from emp , dept
where emp.dept_id = dept.id;

3.内连接

隐式内连接

隐式内连接不使用JOIN关键字,而是通过WHERE子句来指定连接条件。

SELECT 字段列表
FROM 表1, 表2
WHERE 表1.字段 = 表2.字段;

显式内连接

显式内连接使用INNER JOIN关键字,并通过ON子句来明确指定连接条件。

SELECT 字段列表
FROM 表1
[INNER] JOIN 表2 ON 表1.字段 = 表2.字段;

案例

查询每一个员工的姓名,及关联的部门的名称

表结构: emp , dept
连接条件: emp.dept_id = dept.id

隐式内连接

-- 未使用别名的查询
SELECT emp.name, dept.name 
FROM emp, dept 
WHERE emp.dept_id = dept.id;

-- 使用别名的查询,简化SQL编写
SELECT e.name, d.name 
FROM emp e, dept d 
WHERE e.dept_id = d.id;

 显式内连接

-- 未使用别名的查询
SELECT e.name, d.name 
FROM emp e 
INNER JOIN dept d ON e.dept_id = d.id;

-- 使用别名的查询,简化SQL编写
SELECT e.name, d.name 
FROM emp e 
JOIN dept d ON e.dept_id = d.id;

4.外连接

左外连接

左外连接返回左表(表1)的所有记录,即使右表(表2)中没有匹配的记录。如果左表的记录在右表中没有匹配项,则结果集中的右表部分将为NULL。

SELECT 字段列表
FROM 表1 LEFT [OUTER] JOIN 表2 ON 条件;

右外连接

右外连接返回右表(表2)的所有记录,即使左表(表1)中没有匹配的记录。如果右表的记录在左表中没有匹配项,则结果集中的左表部分将为NULL。

SELECT 字段列表
FROM 表1 RIGHT [OUTER] JOIN 表2 ON 条件;

案例

左外连接

查询emp表的所有数据以及对应的部门信息

表结构: emp , dept
连接条件: emp.dept_id = dept.id

-- 使用LEFT OUTER JOIN查询
SELECT e.*, d.name 
FROM emp e 
LEFT OUTER JOIN dept d ON e.dept_id = d.id;

-- 省略OUTER关键字的查询,效果相同
SELECT e.*, d.name 
FROM emp e 
LEFT JOIN dept d ON e.dept_id = d.id;

右外连接

查询dept表的所有数据以及对应的员工信息

表结构: emp , dept
连接条件: emp.dept_id = dept.id

使用右外连接,以确保即使某些部门没有员工,这些部门的信息也会被包括在查询结果中。

-- 使用RIGHT OUTER JOIN查询
SELECT d.*, e.*
FROM emp e 
RIGHT OUTER JOIN dept d ON e.dept_id = d.id;

-- 省略OUTER关键字的查询,效果相同
SELECT d.*, e.*
FROM emp e 
RIGHT JOIN dept d ON e.dept_id = d.id;

5.自连接

SELECT a.字段1, b.字段2
FROM 表名 a, 表名 b
WHERE a.条件字段 = b.条件字段;
SELECT a.字段1, b.字段2
FROM 表名 a
JOIN 表名 b ON a.条件字段 = b.条件字段;

案例

-- 使用自连接查询员工及其所属领导的名字
SELECT a.name AS '员工', b.name AS '领导'
FROM emp a, emp b
WHERE a.managerid = b.id;
-- 使用左外连接查询所有员工及其领导的名字,包括没有领导的员工
SELECT a.name AS '员工', b.name AS '领导'
FROM emp a
LEFT JOIN emp b ON a.managerid = b.id;

注意事项:
自连接查询必须要为表起别名,不然不清楚所指定的条件、返回的字段,到底是哪─张表的字段。

6.联合查询

联合查询(UNION)用于将两个或多个SELECT语句的结果集合并成一个结果集

SELECT 字段列表 FROM 表A
UNION [ALL]
SELECT 字段列表 FROM 表B;

以下是关于联合查询的要点:

  1. 使用UNION时,会自动去除结果集中的重复行。
  2. 使用UNION ALL时,不会去除重复行,会将两个或多个查询的结果集直接合并在一起。
  3. 联合查询中,每个SELECT语句的字段数量必须相同。
  4. 每个查询中对应的字段类型应该兼容,以便在合并结果时不会出现类型错误。

 案例

使用UNION ALL联合查询薪资低于5000的员工和年龄大于50岁的员工

-- 使用UNION ALL查询薪资低于5000的员工
SELECT * FROM emp WHERE salary < 5000
UNION ALL
-- 使用UNION ALL查询年龄大于50岁的员工
SELECT * FROM emp WHERE age > 50;

 去除可能重复的员工记录(即那些既薪资低于5000又年龄大于50岁的员工)

-- 使用UNION查询薪资低于5000的员工
SELECT * FROM emp WHERE salary < 5000
UNION
-- 使用UNION查询年龄大于50岁的员工
SELECT * FROM emp WHERE age > 50;

7.子查询

子查询外部的语句可以是INSERT / UPDATE / DELETE / SELECT的任何一个。

一些SQL中常用的操作符及其描述:

IN

  • 描述IN 操作符用于确定给定的值是否与子查询或列表中的值匹配。如果值在指定的集合范围内,则返回 TRUE,否则返回 FALSE
  • 例子
    SELECT * FROM emp WHERE dept_id IN (1, 2, 3);
    

NOT IN

  • 描述NOT IN 操作符用于确定给定的值是否不在子查询或列表中的值匹配。如果值不在指定的集合范围内,则返回 TRUE,否则返回 FALSE
  • 例子
    SELECT * FROM emp WHERE dept_id NOT IN (1, 2, 3);
    

ANY

  • 描述ANY 操作符与子查询一起使用,用于比较外部查询的值与子查询返回的任何值。如果外部查询的值满足子查询返回的任意一个值,则返回 TRUE
  • 例子
    SELECT * FROM emp WHERE salary > ANY (SELECT salary FROM emp WHERE dept_id = 1);
    

SOME

  • 描述SOME 操作符是 ANY 的同义词,可以互换使用。它用于比较外部查询的值与子查询返回的任意值。如果外部查询的值满足子查询返回的任意一个值,则返回 TRUE
  • 例子
    SELECT * FROM emp WHERE salary > SOME (SELECT salary FROM emp WHERE dept_id = 1);
    

ALL

  • 描述ALL 操作符与子查询一起使用,用于比较外部查询的值与子查询返回的所有值。如果外部查询的值满足子查询返回的所有值,则返回 TRUE
  • 例子
    SELECT * FROM emp WHERE salary > ALL (SELECT salary FROM emp WHERE dept_id = 1);
    

使用操作符时,需要注意子查询必须返回一个列的值,以便与外部查询进行比较。

ANY 和 SOME 在功能上是相同的,可以互换使用。ALL 则要求外部查询的值必须大于或小于(取决于比较操作符)子查询返回的所有值。

1.标量子查询

标量子查询返回单个值(一个标量)。这个值通常用于与外部查询的比较操作。

例子

查询薪资高于部门平均薪资的员工信息。

SELECT *
FROM emp
WHERE salary > (SELECT AVG(salary) FROM emp);

2.列子查询

列子查询返回一列值,这些值通常用于与外部查询中的对应列进行比较。

例子

查询与部门ID为1的员工薪资相同的所有员工。

SELECT *
FROM emp
WHERE salary IN (SELECT salary FROM emp WHERE dept_id = 1);

3.行子查询

行子查询返回一行或多行,每行包含多个列。这个子查询的返回结果通常用于与外部查询中的行进行比较。

例子

查询与部门ID为1且薪资最高的员工具有相同薪资和部门的所有员工。

SELECT *
FROM emp
WHERE (salary, dept_id) = (SELECT MAX(salary), dept_id FROM emp WHERE dept_id = 1);

4.表子查询

表子查询返回多行多列的结果集,通常这个结果集被外部查询当作一个临时表来使用。

例子

查询薪资高于部门平均薪资的所有员工。

SELECT e.*
FROM emp e
WHERE e.salary > (SELECT AVG(salary) FROM emp WHERE dept_id = e.dept_id);
  • 18
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值