[数据库] MySQL之SQL查询

一 关系表的介绍

* 课程表
* 学生表
* 选课表

二 SQL查询语句格式

/* SQL查询的一般格式
	SELECT [ALL | DISTINCT] [* | coulmnNameA,coulmnNameB,...] 
		FROM [tableName | viewName | (select语句)] 
		[AS 结果表的别名]
		[WHERE 条件表达式x]
		[GROUP BY coulmnName1 [HAVING 条件表达式y]]
		[ORDER BY coulmnName2 [ASC | DESC]]
		[LIMIT startRow,targetRows]
	【各子句的处理顺序】FROM - WHERE - GROUP BY - HAVING - SELECT - ORDER BY
	【关键词释义】
		+ GROUP BY coulmnName : 将结果按 coulmnName 的值分组·,该属性列中值相等的分为一组
		+ HAVING 条件表达式 : 若 GROUP BY 子句带HAVING短语,则: 只有满足指定条件的组才予以输出
*/

三 查询专题一

3.1 单表查询:选择表中的若干列

3.1.1 查询指定列/全部列

-- 1查询全体学生的姓名、学号、所在专业
SELECT s.sname,s.sno,s.sdept 
    FROM student AS s;

3.1.2 查询经过计算后的值的列

-- 2查询全体学生的姓名及其出生年份
SELECT s.sname,year(now())-s.sage 
    FROM student AS s;
-- 3查询全体学生的姓名、出生年份和所在院系(要求:用小写字母表示专业名)
SELECT s.sname,year(now())-s.sage, LOWER(s.sdept) 
    FROM student AS s;

3.2 单表查询:选择表中的若干行(元组/记录)

3.2.1 消除取值重复的行(DISTINCT)

若不消除查询结果中的重复行,则:不声明 DISTINCT ,即使用默认的 ALL

-- 4查询已选过课程的学生学号
SELECT DISTINCT s.sno 
    FROM student AS s;

3.3 WHERE子句 :查询满足条件的若干行(元组/记录)

查询满足指定条件的元组可通过 WHERE 子句 实现。
但,记住:【WHERE子句】中是不能用【聚集函数】作为条件表达式的;
WHERE 子句 常用的查询条件,如下图所示:

-- 5查询计算机科学系(CS)全体学生的名单
SELECT s.sno,s.sname
    FROM student AS s
    WHERE s.sdept='CS'
-- 6查询考试不及格的学生的学号和姓名 (多表查询)
SELECT s.sno, s.sname 
	FROM student AS s
	WHERE s.sno IN (
			SELECT sc.sno 
				FROM select_course AS sc
				WHERE sc.grade<60
	);
-- 7查询计算机科学专业(CS)、数学专业(MA)和信息专业(IS)学生的姓名和性别
SELECT s.sname, s.ssex 
    FROM student AS s
    WHERE s.sdept IN ('CS', 'MA', 'IS');

(涉及字符串的匹配)

-- 8查询姓'刘'的学生的姓名、学号和性别
SELECT s.sname, s.sno, s.ssex 
    FROM student AS s
    WHERE s.sname LIKE '刘%'; -- %表示满足任意长度(length≥0)的字符串
-- 9查询第二个字为'晨'的学生的姓名、学号和性别
SELECT s.sname, s.sno, s.ssex 
    FROM student AS s
    WHERE s.sname LIKE '_晨%'; -- _表示任意的单个字符

(涉及空值的查询)

-- 10某些学生选修课程后没有参加考试,故存在选课记录;但没有考试成绩。
-- 查询:缺少成绩的学生的学号和对应的课程号
SELECT sc.sno, sc.cno
    FROM select_course AS sc
    WHERE sc.grade IS NULL; /* Grade为空值; 此处 IS 无法用等号(=)替代 */

(涉及同一表中多重条件的查询)

-- 11查询:计算机科学专业中年龄在20岁以下的学生的学号和姓名
SELECT s.sno, s.sname
    FROM student AS s
    WHERE s.sdept='CS' AND s.sage<20;

(涉及聚集函数)
注意1: 【WHERE子句】中是不能用聚集函数作为条件表达式的;
注意2: 聚集函数只能用于【SELECT子句】和【GROUP BY中的HAVING子句】

-- 12查询:选修了课程的学生人数
SELECT COUNT(DISTINCT s.sno) AS 选课总人数
    FROM student AS s;
-- 之所以DISTINCT,是因为1名学生可以选N个课程
-- 13查询:选修1号课程的学生平均成绩
SELECT AVG(sc.grade)
    FROM select_course AS sc
    WHERE sc.cno='1';

3.4 GROUP BY 子句 > HAVING子句

  • GROUP BY子句 将查询结果按某一列或多列的值分组,值相等的为一组。
    • 对查询结果分组的目的是为了细化聚集函数的作用对象。
    • 若未对查询结果分组,聚集函数将作用于整个查询结果。
  • 【分组后,聚集函数将作用于每一个组,即 每个组都有一个函数值】
  • WHERE子句与GROUP BY子句的比较
    • 无GROUP BY子句: 聚集函数将作用于【整个查询结果】
    • 有GROUP BY子句: 聚集函数将作用于【满足条件的分组】
  • WHERE子句与HAVING子句的区别:作用对象不同
    • WHERE子句 作用于: 基表or视图,从中选择满足条件的【元组】
      • 聚集函数不能作用于: WHERE子句
    • HAVING子句 作用于: 查询结果中的分组,从中选择满足条件的【分组】
-- 14查询:求各课程号及相应的选课人数
SELECT sc.cno,COUNT(sc.sno)
    FROM select_course AS sc
    GROUP BY sc.cno;
-- 15查询:选修了3门以内课程的学生学号
SELECT sc.sno
    FROM select_course AS sc
    GROUP BY sc.sno
        HAVING COUNT(*)<=3;
-- 16查询:平均成绩≤90分的学生学号和平均成绩
SELECT sc.sno, AVG(sc.grade)
    FROM select_course AS sc
    GROUP BY sc.sno
        HAVING AVG(sc.grade)>=90;

3.5 嵌套查询:查询块 / 外层查询(父查询) / 内层查询(子查询)

  • 在SQL语言中,【1个SELECT-FROM-WHERE语句】称为【1个查询块】。

  • 将1个查询块【嵌套】在【另1个查询块】的WHERE子句或HAVING短语中的条件中的查询,称为:【嵌套查询(nested query)】

  • SQL语句中嵌套SELECT语句,称为嵌套查询,又称子查询

SELECT * FROM t1 WHERE column1 = ( SELECT column1 FROM t2);
  • 子查询外部的语句可以是 INSERT / UPDATE / DELETE / SELECT 的任何一个

  • 根据子查询位置可分为:

  • WHERE 之后
  • FROM 之后
  • SELECT 之后
  • SQL语言允许多层嵌套查询 即:1个子查询中还可嵌套其他子查询
    • 子查询的SELECT语句中【不能使用 ORDER BY 子句】
      • ∵ORDER BY子句只能对最终查询结果排序
    • 子查询的分类:按照 子查询的查询条件是否依赖于父查询
      • 子查询不依赖父查询:【不相关子查询】(如:查询任务21/22)
      • 子查询有依赖父查询:【相关子查询】 (如:查询任务23)
  • 有些情况下,【连接查询】与【嵌套查询】可相互转换。

3.5.1 带有IN谓词的子查询

-- 21查询:查询与“刘晨”在同一个系学习的学生信息
-- A方式 嵌套查询
SELECT * 
    FROM student AS s
    WHERE s.sdept IN (
        SELECT sdept
            FROM student
            WHERE student.sname = '刘晨'
          );

-- B方式 自身连接查询
SELECT s1.sno,s1.sname,s1.sdept
	FROM student AS s1, student AS s2
	WHERE s1.sdept = s2.sdept AND s2.sname='刘晨';
-- 22查询:选修了课程名为“信息系统”的学生的学号、姓名
SELECT s.sno, s.sname # SELECT 1 (第1层 父查询)
	FROM student AS s
	WHERE s.sno IN(
		SELECT sc.sno # SELECT 2 (第2层 子查询)
			FROM select_course AS sc
			WHERE sc.cno IN (
				SELECT cno # SELECT 3 (第2层 子查询的子查询)
					FROM course AS c
					WHERE c.cname = '信息系统'
			)
	);

3.5.2 带有比较运算符的子查询

-- 23查询:找出每个学生达到【他们自己】选修课程平均成绩的课程号
SELECT sc.sno, sc.cno, sc.grade
    FROM select_course AS sc
    WHERE sc.grade >= (
        SELECT AVG(sub_sc.grade)
            FROM select_course AS sub_sc
            WHERE sub_sc.sno = sc.sno # 子查询的sub_sc 调用/依赖 了外层父查询的sc的学号信息
    );

3.5.3 带有ANY/SOME或ALL谓词的子查询

-- 24查询:非计算机科学系中不超过计算机科学系任意一个学生年龄的学生姓名和年龄
-- 【A 嵌套查询+比较+ANY谓词法】
SELECT s.sname, s.sage
    FROM student AS s
    WHERE s.sage <= ANY(
        SELECT sage
            FROM student
            WHERE student.sdept='CS'
    ) AND s.sdept <> 'CS';

-- 【B 嵌套查询+比较+聚集函数法】
SELECT s.sname, s.sage
    FROM student AS s
    WHERE s.sage <= (
        SELECT MIN(student.sage)
            FROM student
            WHERE student.sdept='CS'
    ) AND s.sdept <> 'CS';

3.5.4 带有EXISTS谓词的子查询

  • EXISTS:代表存在量词∃。
  • 带有EXISTS谓词的子查询不返回任何数据,只产生逻辑真值true 或 逻辑假值false
  • 可利用EXISTS来判断:x∈S、S介绍
-- 25查询:所有选修了1号课程的学生姓名
-- 【A 带有EXISTS谓词的子查询】
SELECT s.sno, s.sname
    FROM student AS s
    WHERE EXISTS (
        SELECT * 
        FROM select_course AS sub_sc
        WHERE sub_sc.sno = s.sno AND sub_sc.cno='1'  
    );

-- 【B 普通的连接查询】
SELECT s.sno, s.sname
    FROM student AS s, select_course AS sc
    WHERE s.sno = sc.sno AND sc.cno = '1';
-- 26查询:没有选修1号课程的学生姓名
-- 【A 带有EXISTS谓词的子查询】
SELECT s.sno, s.sname
    FROM student AS s
    WHERE NOT EXISTS (
        SELECT * 
        FROM select_course AS sub_sc
        WHERE sub_sc.sno = s.sno AND sub_sc.cno='1'  
    );

-- 【B 普通的连接查询】
SELECT DISTINCT s.sno, s.sname
    FROM student AS s LEFT JOIN select_course AS sc ON s.sno = sc.sno
    WHERE 
        sc.cno IS NULL # sc.cno IS NULL :没有任何选课记录的同学
	OR # 或者
        sc.sno NOT IN ( # 选过课,但没有选过1号课的同学
            SELECT sub_sc.sno # 选过1号课的所有同学的记录
	        FROM select_course AS sub_sc
		WHERE sub_sc.cno = '1'
	);
-- 27查询:选修了全部课程的学生姓名 即: 查找没有一门课没有不选的同学
SELECT s.sno, s.sname 
	FROM student AS s
	WHERE NOT EXISTS ( 
		SELECT * FROM course AS c 
			WHERE NOT EXISTS (
				SELECT * FROM select_course AS sc
					WHERE sc.sno = s.sno AND sc.cno = c.cno
		)
);

3.5.5 标量子查询

-- 查询销售部所有员工
select id from dept where name = '销售部';
-- 根据销售部部门ID,查询员工信息
select * from employee where dept = 4;
-- 合并(子查询)
select * from employee where dept = (select id from dept where name = '销售部');
-- 查询xxx入职之后的员工信息
select * from employee where entrydate > (select entrydate from employee where name = 'xxx');

3.5.6 列子查询

  • 返回的结果是一列(可以是多行)。

  • 常用操作符:
    | 操作符 | 描述 |
    | ------ | -------------------------------------- |
    | IN | 在指定的集合范围内,多选一 |
    | NOT IN | 不在指定的集合范围内 |
    | ANY | 子查询返回列表中,有任意一个满足即可 |
    | SOME | 与ANY等同,使用SOME的地方都可以使用ANY |
    | ALL | 子查询返回列表的所有值都必须满足 |

3.5.7 行子查询

  • 返回的结果是一行(可以是多列)。
  • 常用操作符:=, <, >, IN, NOT IN
-- 查询与xxx的薪资及直属领导相同的员工信息
select * from employee where (salary, manager) = (12500, 1);

select * from employee where (salary, manager) = (select salary, manager from employee where name = 'xxx');

3.5.8 表子查询

  • 返回的结果是多行多列
  • 常用操作符:IN
-- 查询与xxx1,xxx2的职位和薪资相同的员工
select 
	* 
from employee 
where (job, salary) in ( select job, salary from employee where name = 'xxx1' or name = 'xxx2' );

-- 查询入职日期是2006-01-01之后的员工,及其部门信息
select 
	e.*, d.* 
from (
	select * from employee where entrydate > '2006-01-01'
) as e 
left join dept as d on e.dept = d.id;

3.6 集合查询(交 INTERSECT | 并 UNION | 差 EXCEPT)

  • SELECT语句的查询结果是元组的集合,故:多个SELECT语句的结果可进行集合操作
  • 集合操作,主要包括:
    • 并操作 UNION
    • 交操作 INTERSECT
      • 【MYSQL不支持 INTERSECT功能,需其它途径实现(Eg: INNER JOIN、多条件+AND的WHERE子句)】
    • 差操作 EXCEPT
      • 【MYSQL不支持 EXCEPT 功能,需其它途径实现(Eg: 左(外)连接(LEFT OUTER JOIN)、多条件+AND的WHERE子句等)】
  • 参加集合操作的各查询结果的【列数】必须相同;对应项的【数据类型】也必须相同。

(求并集)

-- 28查询: 计算机科学系的学生【和】年龄不大于19岁的学生信息
-- 【A UNION集合操作】
(
SELECT *
    FROM student AS s
    WHERE s.sdept='CS'
)
UNION
(
SELECT *
    FROM student AS s
    WHERE s.sage<=19
);

-- 【B 普通的多条件的WHERE子句】
SELECT *
    FROM student AS s
    WHERE s.sdept='CS' OR s.sage<=19;

(求交集,But:【MYSQL不支持 INTERSECT 功能,需其它途径实现(Eg: INNER JOIN、多条件+AND的WHERE子句)】)

-- 29查询: 计算机科学系的学生【与】年龄不大于19岁的【交集】的学生信息
-- 【A 利用内连接(INNER JOIN) 间接实现 求交集(INTERSECT)功能】
SELECT a.sno, a.sname, a.ssex, a.sdept
FROM
		(
		SELECT *
				FROM student AS s
				WHERE s.sdept='CS'
		) a
		INNER JOIN
		(
		SELECT *
				FROM student AS s
				WHERE s.sage<=19
		) b
		ON a.sno = b.sno;

-- 【B 普通的多条件WHERE子句】
SELECT s.*
	FROM student AS s
	WHERE s.sdept='CS' AND s.sage<=19;

(求差集,But:【MYSQL不支持 EXCEPT 功能,需其它途径实现(Eg: 左(外)连接(LEFT OUTER JOIN)、多条件+AND的WHERE子句等)】)

-- 30查询: 计算机科学系的学生【与】年龄不大于19岁的【差集】的学生信息 即: 查询 计算机科学系中年龄大于19岁的学生
-- 【A 利用左(外)连接(LEFT [OUTER] JOIN) 间接实现 求差集(EXCEPT)功能】
SELECT a.*, b.*
FROM
		(
		SELECT *
				FROM student AS s
				WHERE s.sdept='CS'
		) a
		LEFT OUTER JOIN
		(
		SELECT *
				FROM student AS s
				WHERE s.sage<=19
		) b
		ON a.sno = b.sno
WHERE b.sno IS NULL; # 选择 a 中 不满足 b条件的那部分元组

-- 【B 普通的多条件WHERE子句】
SELECT s.*
	FROM student AS s
	WHERE s.sdept='CS' AND s.sage>19;

3.7 基于【派生表】的查询

  • 子查询不仅可出现在WHERE子句中,还可出现在FROM子句中,这时子查询生成的【临时派生表】成为主查询的查询对象。
-- 【回顾:23查询】23查询:找出每个学生达到【他们自己】选修课程平均成绩的课程号
-- 【A方式 嵌套查询+WHERE子句】
SELECT sc.sno, sc.cno AS '【课程号(cno)】', sc.grade
    FROM select_course AS sc
    WHERE sc.grade > ( -- 当前学生的平均成绩
        SELECT AVG(sub_sc.grade)
            FROM select_course AS sub_sc
            WHERE sub_sc.sno = sc.sno # 子查询的sub_sc 调用/依赖 了外层父查询的sc的学号信息
    );

-- 【B方式 嵌套查询+FROM子句(临时派生表查询)】
SELECT sc.sno,sc.cno
    FROM 
        select_course AS sc, 
        (SELECT sub_sc.sno, AVG(sub_sc.grade) as avg_grade
            FROM select_course AS sc
            GROUP BY sub_sc.sno
        ) AS sub_svg_sc
    WHERE sc.sno=sub_svg_sc.sno AND sc.grade >= sub_svg_sc.avg_grade;

四 查询专题二:连接查询(多表查询)

4.0 总述

4.0.1 单表查询 VS 多表查询

  • 多表集合查询 : union, union all

把多次查询的结果合并,形成一个新的查询集
UNION ALL 会有重复结果,UNION 不会
联合查询比使用or效率高,不会使索引失效

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

4.0.2 悬浮元组(R/S): 内连接[不保留] VS 外连接[保留] VS 交叉连接(笛卡尔集)

  1. 内连接

隐式内连接:

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

显式内连接:

SELECT 字段列表 FROM 表1 [ INNER ] JOIN 表2 ON 连接条件 ...;
  1. 外连接查询
  • 左外连接:查询左表所有数据,以及两张表交集部分数据
SELECT 字段列表 FROM 表1 LEFT [ OUTER ] JOIN 表2 ON 条件 ...;

相当于查询表1的所有数据,包含表1和表2交集部分数据

  • 右外连接:查询右表所有数据,以及两张表交集部分数据
SELECT 字段列表 FROM 表1 RIGHT [ OUTER ] JOIN 表2 ON 条件 ...;
  1. 自连接查询

当前表与自身的连接查询,自连接必须使用表别名

SELECT 字段列表 FROM 表A 别名A JOIN 表A 别名B ON 条件 ...;

自连接查询,可以是内连接查询,也可以是外连接查询

4.0.3 JOIN 的关联逻辑

4.0.4 小结

4.1 交叉连接/交叉集/笛卡尔集([CROSS JOIN]) := NO JOIN

4.1.1 语法

select * from R,S;
或
select * from R cross join S;

返回的记录数: size(R) * size(S)

4.1.2 案例1

使用下列命令同时查询玩具表的toy列和男孩表的boy列,得到的结果就是交叉连接

SELECT 
  t.toy,b.boy
FROM toys AS t
CROSS JOIN boys AS b;

其中,CROSS JOIN可省略,简写为:

SELECT 
  t.toy, b.boy
FROM toys AS t, boys AS b;

交叉连接回把第一张表的每个值与第二张表的每个值进行匹配,结果如下:

4.1.2 案例2 (必读)

4.2 内连接([INNER] JOIN)

  • [inner] join 也可简写为 join
  • INNER JOIN可以不等:select * from t1 inner join t2 on t1.id<>t2.id
  • 等值连接 的两个集合的key必须相等 => 从集合论角度看:等值连接内连接的子集

4.2.1 重要特例: 等值连接 | (等值)内连接 inner join on a.key = b.key <=> 等值(交叉)连接 [cross join] ... where a.key = b.key

-- 17查询:每个学生及其选修课程的情况
-- A方式:等值连接
SELECT 
  s.*, c.*, sc.*
FROM 
  student AS s, 
  select_course AS sc, 
  course AS c
WHERE 
  s.sno = sc.sno 
  AND sc.cno = c.cno;

或:

SELECT 
  s.*, c.*, sc.*
FROM student AS s,
INNER JOIN select_course AS sc ON s.sno = sc.sno
INNER JOIN course AS c ON sc.cno = c.cno;

4.2.2 重要特例: 自然连接 <=> 等值连接(的特例)

-- B方式:自然连接(把**等值连接**中`目标列`内**重复的属性列**去掉)
SELECT s.sno,s.sname,s.ssex,s.sage, c.cno, c.cname, sc.grade AS '课程成绩'
    FROM student AS s, select_course AS sc, course AS c
    WHERE s.sno = sc.sno AND sc.cno = c.cno;
-- 18查询:选修2号课程且成绩在85分以上的所有学生的学号、姓名、课程名和成绩
SELECT s.sno,s.sname, c.cname, sc.grade
    FROM student AS s, select_course AS sc, course AS c
    WHERE s.sno = sc.sno AND sc.cno = c.cno AND sc.cno='2' AND sc.grade>85;

4.3 左外连接(LEFT [OUTER] JOIN) := 左连接(LEFT JOIN)

  • left joinleft outer join 的简写,两者含义一样的。
  • out joininner join 相对
-- 外连接

-- 左外连接/左连接 :  tableA LEFT [OUTER] JOIN tableB ON tableA.columnX = tableB.columnX;
-- 20查询:每个学生的选课情况。且满足:没有选课的同学也需保留(即 若某个学生没有选课,仍把Student的悬浮元组保留在结果关系中。)
-- (本查询任务,可对比、参见查询17的等值连接、自然连接)
-- 【A简单版本】(仅student左连接select_course)
--     即 :仅显示select_course表中的课程号/成绩,不显示course表中的课程名
SELECT s.sno,s.sname,s.ssex,s.sage, sc.cno, sc.grade
			FROM student AS s 
					 LEFT OUTER JOIN 
					 select_course AS sc 
					 ON s.sno = sc.sno;
-- 【B复杂/详细版本】(student左连接select_course AS TMP(第1重);TMP左连接course (第2重))
--     即: 不但显示select_course表中的课程号/成绩,还显示course表中的课程名
-- 本查询任务,极易错误(一定不能使用WHERE子句的等值查询,一旦使用,就会出现:可能将未选课的学生记录除去,因为它们并没有选课记录)
SELECT *
	FROM
		(SELECT s.sno,s.sname,s.ssex,s.sage, sc.cno, sc.grade
			FROM student AS s 
				 LEFT OUTER JOIN 
				 select_course AS sc 
				 ON s.sno = sc.sno) AS tmp 
		LEFT JOIN course AS c 
		ON tmp.cno = c.cno;

4.4 右外连接(RIGHT [OUTER] JOIN) := 右连接(RIGHT JOIN)

同理,有【左外连接】,便有【右外连接】

-- 右外连接/右连接 :  tableA RIGHT [OUTER] JOIN tableB ON tableA.columnX = tableB.columnX;
-- (案例,暂略)

(补充笔记↓:对自然连接、全外连接的再次案例解释)

-- 回顾:20查询:每个学生的选课情况。且满足:没有选课的同学信息也需保留(即 若某个学生没有选课,仍把Student的悬浮元组保留在结果关系中。)
-- A 自然连接(NATURAL JOIN)
SELECT s.sno,s.sname,s.ssex,s.sage, sc.cno, sc.grade
    FROM student AS s 
         NATURAL JOIN 
         select_course AS sc;
↑可知,通过该自然连接语句,查询任务未能实现

4.5 全外连接(FULL [OUTER] JOIN)

-- B 全外连接(MYSQL不支持全外连接;但可通过:全外连接=左外连接 UNION 右外连接【间接实现】)
(
SELECT s.sno,s.sname,s.ssex,s.sage, sc.cno, sc.grade
    FROM student AS s 
         LEFT OUTER JOIN
         select_course AS sc 
         ON s.sno=sc.sno
)
UNION
(
SELECT s.sno,s.sname,s.ssex,s.sage, sc.cno, sc.grade
    FROM student AS s 
         RIGHT OUTER JOIN
         select_course AS sc 
         ON s.sno=sc.sno
);
↑可知,通过该自然连接语句,查询任务未能实现

4.X 补充: 自身连接

(自身连接:1个表与其自身表进行连接,称为:表的【自身连接】)

-- 自身连接
-- 19查询:每一门课的间接先修课(即 先修课的先修课)
SELECT c1.cno AS '当前课程号(c1)', c1.cname AS '当前课程名(c1)', c1.cpno AS '当前的先修课号(c1)', 
       c2.cno AS '当前的先修课号(c2)', c2.cname AS '当前的先修课程名(c2)', c2.cpno AS '【当前的先修课程的先修课程号(c2)】'
    FROM course AS c1, course AS c2
    WHERE c1.cpno = c2.cno;

五 临时表

参考文献

MySQL 临时表在我们需要保存一些临时数据时是非常有用的。
临时表只在当前连接可见,当关闭连接时,Mysql会自动删除表并释放所有空间。

如果你使用了其他MySQL客户端程序连接MySQL数据库服务器来创建临时表,那么只有在关闭客户端程序时才会销毁临时表,当然你也可以手动销毁。

5.1 创建临时表

CREATE TEMPORARY TABLE SalesSummary (
    -> product_name VARCHAR(50) NOT NULL
    -> , total_sales DECIMAL(12,2) NOT NULL DEFAULT 0.00
    -> , avg_unit_price DECIMAL(7,2) NOT NULL DEFAULT 0.00
    -> , total_units_sold INT UNSIGNED NOT NULL DEFAULT 0
);

5.2 插入临时表记录

INSERT INTO SalesSummary
    -> (product_name, total_sales, avg_unit_price, total_units_sold)
    -> VALUES
    -> ('cucumber', 100.25, 90, 2);

5.3 查询临时表记录

SELECT * FROM SalesSummary;

5.4 删除临时表

DROP TABLE SalesSummary;

当你使用 SHOW TABLES命令显示数据表列表时,你将无法看到 SalesSummary表。
如果你退出当前MySQL会话,再使用 SELECT命令来读取原先创建的临时表数据,那你会发现数据库中没有该表的存在,因为在你退出时该临时表已经被销毁了。

Y 经典查询场景

Q: 从deptId, parentId的树节点列表中找出根节点

问题描述

  • 表情况、数据情况。假定有一张表sys_dept表,其内含dept_idparent_idagentId。通过agentId可过滤出1个deptId, parentId的组织的树节点列表
select 
	dept_id 
    , parent_id
    , agent_id 
from sys_dept a
where agent_id = '1'

query result: list_x

dept_id parent_id agent_id
100 0 1
101 100 1
103 101 1
200 103 1
  • 查询需求:将list_x中的root节点查询出来。

解决方法

SELECT 
	t2.dept_id
    -- , t2.agent_id
	, t1.lvl
FROM ( 
	SELECT 
		@r AS _id
		, ( SELECT @r := parent_id FROM sys_dept WHERE dept_id = _id ) AS parent_id 
		, @l := @l + 1 AS lvl 
	FROM (
		SELECT 
			@r := ( select dept_id from sys_dept where agent_id = '1' ORDER BY RAND() limit 1 )  -- 目标树中,任意取1个节点ID
		    --  @r := '{deptId}' -- 树中的任意节点ID 
			, @l := 0
	) vars, sys_dept 
	WHERE @r != 0
) t1 
JOIN sys_dept t2 ON t1._id = t2.dept_id 
where 1 = 1 
  -- t2.and agent_id is null -- 添加任意过滤条件
ORDER BY t1.lvl DESC
-- limit 1

食用方式:再在最后加个过滤条件————始终只取第1行查询结果即可。

query result : (由于是从list_x中随机取的1条,所以每次查询的结果行数都会有变化。但始终第1条————root节点,始终会存在的)

X 参考文献

X 参考与推荐文献

6.1 参考文献

6.2 推荐文献 (必读)

posted @ 2020-03-20 23:00  千千寰宇  阅读(918)  评论(0编辑  收藏  举报