MySql数据库期末复习①

数据库模型分为层次模型,网状模型,关系模型

整数类型

类型 字节数 有符号范围 无符号范围
TINYINT 1 -128 ~ 127 0 ~ 255
SMALLINT 2 -32,768 ~ 32,767 0 ~ 65,535
MEDIUMINT 3 -8,388,608 ~ 8,388,607 0 ~ 16,777,215
INT / INTEGER 4 -2,147,483,648 ~ 2,147,483,647 0 ~ 4,294,967,295
BIGINT 8 -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 0 ~ 18,446,744,073,709,551,615
  • UNSIGNED:加在类型后面,表示无符号,不能存负数,范围翻倍。比如 age TINYINT UNSIGNED 存 0~255
  • AUTO_INCREMENT:只能用于整数,自动递增,通常用 INTBIGINT

浮点数(近似值)

类型 字节数 有效数字 说明
FLOAT 4 约7位 单精度
FLOAT(p) 4或8 p≤24为4字节 根据p决定
DOUBLE 8 约15位 双精度
DOUBLE PRECISION 8 约15位 DOUBLE 同义词
  • 不是精确存储 ,存在精度问题,如 0.1 可能存成 0.100000001
  • 不要用于金额计算
  • 适合:科学计算、统计数据、评分等可容忍微小误差的场景

定点数(精确值)

类型 说明
DECIMAL(M, D) M=总位数(165),D=小数位数(030)
NUMERIC(M, D) 与 DECIMAL 完全等价
DEC 等价于 DECIMAL(10,0)
  • 精确存储,不会有浮点误差
  • 以字符串形式存储,空间随 M 变化
  • 默认 DECIMAL = DECIMAL(10, 0)
  • 适合:金额、财务数据等必须精确的场景

BIT(位类型)

类型 长度 说明
BIT 1位 等价 BIT(1)
BIT(M) 1~64位 存二进制位
  • 存储二进制的 0 和 1 序列
  • 查询时显示为二进制字符,转十进制用 字段名 + 0
  • 适合:开关标志、权限位掩码等

字符串类型

类型 最大长度 存储方式 特点
CHAR(M) 255字符 定长 不够补空格,取时去空格
VARCHAR(M) 65535字节 变长 额外1~2字节存长度

CHAR 定长:

  • 声明 CHAR(10) 存 "abc",实际占 10 个字符空间
  • 读取快,适合固定长度数据
  • 最大 255 个字符

VARCHAR 变长:

  • 声明 VARCHAR(10) 存 "abc",只占 3 个字符 + 1字节记录长度
  • 节省空间,适合变长数据
  • 最大 65535 字节(注意是字节,不是字符,utf8mb4下一个字符占4字节,所以最多65535/4=16383个字符)
sql 复制代码
id_card CHAR(18)          -- 身份证固定18位
name VARCHAR(50)          -- 名字长短不一
email VARCHAR(100)        -- 邮箱长度不固定
场景 用 CHAR 用 VARCHAR
数据长度固定
数据长度变化大
频繁更新 ❌(可能产生碎片)
短字符串 差别不大

TEXT 系列(大文本)

类型 最大长度 字节
TINYTEXT 255 字节 256B-1
TEXT 64 KB 65,535
MEDIUMTEXT 16 MB 16,777,215
LONGTEXT 4 GB 4,294,967,295
  • 存大量文本数据
  • 不能有默认值
  • 查询时不会完全加载到内存,适合大段文字
  • 与 VARCHAR 区别:TEXT 有独立的存储区域
sql 复制代码
introduction TEXT         -- 简介
article MEDIUMTEXT        -- 文章内容
log_data LONGTEXT         -- 超长日志

BLOB 系列(二进制大对象)

类型 最大长度 对应 TEXT
TINYBLOB 255 字节 TINYTEXT
BLOB 64 KB TEXT
MEDIUMBLOB 16 MB MEDIUMTEXT
LONGBLOB 4 GB LONGTEXT
  • 存二进制数据(图片、音频、文件等)
  • 与 TEXT 区别:TEXT 存字符有字符集,BLOB 存字节没有字符集
  • 实际开发中,大文件通常存文件服务器,数据库只存路径
sql 复制代码
avatar BLOB              -- 头像图片
file_data MEDIUMBLOB     -- 文件内容

ENUM(枚举)

类型 说明
ENUM('值1','值2',...) 单选,最多 65535 个值

讲解:

  • 存一个字符串,但内部用数字索引存储(省空间)
  • 插入时必须是列表中的值,否则报错(严格模式下)
  • 排序按索引顺序,不是字母顺序
sql 复制代码
gender ENUM('男','女','保密')
status ENUM('正常','禁用','待审核')

SET(集合)

类型 说明
SET('值1','值2',...) 多选,最多 64 个值
  • 可以选多个值,内部用位掩码存储
  • 查询和插入有特殊语法
sql 复制代码
hobby SET('阅读','运动','音乐','旅游')
操作 SQL
插入多个 INSERT INTO t VALUES ('阅读,音乐')
查找包含 WHERE FIND_IN_SET('音乐', hobby)
精确匹配 WHERE hobby = '阅读,音乐'

BINARY 和 VARBINARY

类型 说明
BINARY(M) 定长二进制,不够补 \0
VARBINARY(M) 变长二进制
  • 和 CHAR/VARCHAR 类似,但存的是字节,不涉及字符集
  • BINARY 比较时按字节精确匹配
  • 适合:哈希值、加密数据、UUID 等
sql 复制代码
sha_hash BINARY(32)        -- SHA256 哈希值
token VARBINARY(64)        -- 随机令牌
比较 CHAR BINARY
'a' = 'A' ✅(看排序规则) ❌(字节不同)

日期时间类型

类型 格式 范围 占字节
DATE YYYY-MM-DD 1000-01-01 ~ 9999-12-31 3
TIME HH:MM:SS -838:59:59 ~ 838:59:59 3
DATETIME YYYY-MM-DD HH:MM:SS 1000-01-01 00:00:00 ~ 9999-12-31 23:59:59 5~8
TIMESTAMP YYYY-MM-DD HH:MM:SS 1970-01-01 00:00:01 ~ 2038-01-19 03:14:07 4~7
YEAR YYYY 1901 ~ 2155 1

JSON 类型

类型 说明 引入版本
JSON 存储 JSON 格式数据 5.7.8
sql 复制代码
info JSON

常用操作:

sql 复制代码
-- 插入
INSERT INTO t VALUES ('{"name":"张三", "age":18}');
-- 插入一条数据,info 列存的是一段 JSON,里面有一个 name 字段是"张三",一个 age 字段是 18

-- 查询字段
SELECT info->'$.name' FROM t;              --  从 info 里把 name 字段取出来,返回带引号的 "张三"
-- `->` 是提取 JSON 字段的操作符
-- `'$.name'` 是路径,`$` 代表整个 JSON,`.name` 代表里面的 name
SELECT info->>'$.name' FROM t;             -- 返回不带引号的 张三
SELECT JSON_EXTRACT(info, '$.age') FROM t; -- 提取值

-- 修改
UPDATE t SET info = JSON_SET(info, '$.age', 20) WHERE id = 1;

JSON 就是一种数据格式,长这样:

json 复制代码
{"name":"张三", "age":18, "hobby":["篮球","游戏"]}

本质上就是一个字符串,但是有固定写法:用花括号包起来,里面是"键:值"对。

传统你得像这样建表,把所有可能的字段都建好:

sql 复制代码
CREATE TABLE user (
  id INT,
  name VARCHAR(50),
  wechat VARCHAR(50),     -- 可能为空
  qq VARCHAR(20),         -- 可能为空
  address VARCHAR(200),   -- 可能为空
  hobby1 VARCHAR(20),     -- 可能为空
  hobby2 VARCHAR(20),     -- 可能为空
  hobby3 VARCHAR(20)      -- 可能为空
);

列越来越多,很多都是空的,结构太死板。

用 JSON 方式:

sql 复制代码
CREATE TABLE user (
  id INT,
  name VARCHAR(50),
  info JSON      -- 其他乱七八糟的都塞这里
);

然后插入:

sql 复制代码
INSERT INTO user VALUES (1, '张三', '{"wechat":"zhangsan","hobby":["篮球","游戏"]}');
INSERT INTO user VALUES (2, '李四', '{"qq":"123456","address":"北京市","age":25}');

张三有微信和爱好,李四有QQ和地址------结构可以完全不一样,互不影响。

写法 结果 区别
info->'$.name' "张三" 带双引号,仍是 JSON 格式
info->>'$.name' 张三 去掉引号,变普通文本

完整类型速查总表

大类 类型 用途举例
整数 TINYINT 年龄、状态码
SMALLINT 小范围计数
MEDIUMINT 中等计数
INT 主键ID、普通计数
BIGINT 大计数、自增ID
精确小数 DECIMAL 金额、财务数据
浮点 FLOAT 评分、百分比
DOUBLE 科学计算
BIT 开关标志、权限掩码
定长文本 CHAR 手机号、身份证号
变长文本 VARCHAR 姓名、邮箱、地址
长文本 TEXT/MEDIUMTEXT/LONGTEXT 文章、评论、日志
二进制 BLOB/MEDIUMBLOB/LONGBLOB 图片、文件
定长二进制 BINARY 哈希值
变长二进制 VARBINARY 令牌
枚举 ENUM 性别、状态
集合 SET 多选标签
日期 DATE 生日
时间 TIME 时长
日期时间 DATETIME/TIMESTAMP 创建时间、更新时间
YEAR 年份
JSON JSON 灵活数据结构
空间 POINT/LINESTRING/POLYGON 位置、路径、区域
布尔 BOOLEAN 是否启用

进入MySQL

sql 复制代码
cd C:\Program Files\MySQL\MySQL Server 5.7\bin
SET CHARACTER_SET_DATABASE='gbk';
SET CHARACTER_SET_SERVER='gbk';
MySQL -uroot -p
13579qetuo

数据库操作

数据库创建

sql 复制代码
CREATE DATABASE [IF NOT EXISTS] 数据库名

数据库删除

sql 复制代码
DROP DATABASE [IF EXISTS] 数据库名;

数据库查看和选择

sql 复制代码
SHOW DATABASES;                    -- 查看所有数据库
USE 数据库名;                      -- 选择/切换数据库
SELECT DATABASE();                 -- 查看当前使用的数据库

表的操作

表的创建

sql 复制代码
CREATE TABLE student (
  id INT PRIMARY KEY AUTO_INCREMENT,-- 设置主键和自增
  name VARCHAR(50) NOT NULL,-- 不允许为空
  gender ENUM('男','女') DEFAULT '男',--不填默认男
  age TINYINT UNSIGNED ,--无符号小整数
  birthday DATE,-- DATE --- 日期类型,格式是 YYYY-MM-DD
  phone CHAR(11),
  create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
  -- TIMESTAMP --- 时间戳类型
  -- DEFAULT CURRENT_TIMESTAMP --- 默认值是插入数据时的当前时间,自动记录创建时间
);

列出当前数据库所有表名

sql 复制代码
SHOW TABLES;

查看表结构

sql 复制代码
DESC student;

查看建表的完整 SQL 语句

sql 复制代码
SHOW CREATE TABLE 表名;

查看这张表的所有数据

sql 复制代码
SELECT * FROM student;

删除表

sql 复制代码
DROP TABLE IF EXISTS 表名;

删除表中所有数据

sql 复制代码
DELETE FROM 表名;

插入数据

sql 复制代码
INSERT INTO student (name, gender, age, birthday, phone) 
VALUES ('张三', '男', 18, '2008-05-10', '13800138001');

插入多行数据

sql 复制代码
INSERT INTO student (name, gender, age, birthday, phone) VALUES 
('李四', '女', 19, '2007-08-15', '13800138002'),
('王五', '男', 20, '2006-03-20', '13800138003');

插入图片

sql 复制代码
INSERT INTO xs
     VALUES('221102', '程明', '计算机', 1, '2005-02-01', 15, NULL, LOAD_FILE('E:\mysql5\data\chenmin.jpg'));

修改表中张三的年龄

sql 复制代码
UPDATE student SET age = 21 WHERE name = '张三';
-- WHERE 会把所有人 age 都改成 21

将xs1表和xs2表中所有学生的总学分都加4。

sql 复制代码
UPDATE  xs1,xs2
       SET xs2.总学分= xs2.总学分+4, xs1.总学分= xs1.总学分+4
       WHERE xs2.学号 = xs1.学号;
SELECT 学号,姓名,总学分 FROM xs1;					#(a)
SELECT 学号,姓名,总学分 FROM xs2;	

删除id为3 的那一行数据

sql 复制代码
DELETE FROM student WHERE id = 3;

查询所有男生的姓名和年龄

sql 复制代码
SELECT name, age FROM student WHERE gender = '男' ORDER BY age DESC;-- (DESC 降序,ASC 升序)

创建一个和 student 结构完全一样的新表,只复制结构,不复制数据

sql 复制代码
CREATE TABLE student_backup LIKE student;

结构和数据一起复制,但注意不会复制主键、索引、自增等约束。

sql 复制代码
CREATE TABLE student_copy AS SELECT * FROM student;

完全复制表

sql 复制代码
-- 第一步:复制结构(含约束、索引、自增等)
CREATE TABLE student_full_copy LIKE student;

-- 第二步:复制数据
INSERT INTO student_full_copy SELECT * FROM student;

复制部分数据

sql 复制代码
-- 只复制 age > 18 的数据
CREATE TABLE student_adult AS 
SELECT * FROM student 
WHERE age > 18;

-- 只要部分列
CREATE TABLE student_brief AS 
SELECT name, phone 
FROM student;

-- 部分列 + 部分行
CREATE TABLE student_male AS 
SELECT name, age, phone 
FROM student 
WHERE gender = '男';

-- 想保留主键和索引
-- 第一步:复制完整结构
CREATE TABLE student_part LIKE student;
-- 第二步:只插入符合条件的数据
INSERT INTO student_part SELECT * FROM student WHERE age > 18;

添加列

sql 复制代码
ALTER TABLE student ADD id_card CHAR(18) FIRST;-- 不加FIRST表示默认放在最后一列,加入放在第一行,换成after+列名表示放某列之后

删除列

sql 复制代码
ALTER TABLE student DROP COLUMN address;-- 从 student 表中删除 address 这一列

修改列

sql 复制代码
ALTER TABLE student MODIFY name VARCHAR(100) NOT NULL;
-- 把 name 的类型从 VARCHAR(50) 改成 VARCHAR(100)。
ALTER TABLE student CHANGE phone mobile CHAR(11);
-- phone 是旧列名,mobile 是新列名,后面跟类型。

设置 gender 列的默认值为"保密",之后插入数据如果不填 gender,就自动填"保密"

sql 复制代码
ALTER TABLE student ALTER COLUMN gender SET DEFAULT '保密';

把id设为主键

sql 复制代码
ALTER TABLE student ADD PRIMARY KEY (id);

删除主键约束

sql 复制代码
ALTER TABLE student MODIFY id INT;
ALTER TABLE student DROP PRIMARY KEY;
--  删除主键约束。注意:如果有自增列,必须先去掉自增才能执行这句。

添加唯一约束,给 phone 列加唯一约束,保证手机号不重复。

sql 复制代码
ALTER TABLE student ADD CONSTRAINT uk_phone UNIQUE (phone);

删除唯一约束,MySQL 中唯一约束底层也是索引,所以用 DROP INDEX。

sql 复制代码
ALTER TABLE student DROP INDEX uk_phone;

重命名表,把 student 表重命名为 stu。

sql 复制代码
ALTER TABLE student RENAME TO stu;

RENAME TABLE stu TO student;-- 把 stu 改回 student。

ALTER TABLE 做不到,而RENAME TABLE可以

sql 复制代码
-- 同时改两个表名
RENAME TABLE 
  student TO stu,
  class TO cla;

-- 把表从一个库移到另一个库
RENAME TABLE db1.student TO db2.student;

外键约束

sql 复制代码
ALTER TABLE student ADD COLUMN class_id INT;
-- 在 student 表里加一个新列,叫 class_id,用来存班级编号。

ALTER TABLE student ADD CONSTRAINT fk_class 
  FOREIGN KEY (class_id) REFERENCES class(class_id);
-- 给 class_id 列加一个"锁",以后往 class_id 填的值,必须是 class 表中已经存在的班级编号,不能乱填。
-- fk_class --- 给这把"锁"起个名字,叫 fk_class(方便以后找到它、删掉它)
-- FOREIGN KEY (class_id) --- 把 student 表的 class_id 列设为外键(就是引用别人的键)
-- REFERENCES class(class_id) --- 它引用的对象是 class 表的 class_id 列

ALTER TABLE student DROP FOREIGN KEY fk_class;
--删除约束本身(那个校验规则),不会删除 class_id 这个列

创建kc表

sql 复制代码
USE xscj;
CREATE TABLE kc
(
	课程号	  char(3)		NOT NULL PRIMARY KEY,
	课程名	  varchar(8) 	NOT NULL,	
	开课学期	  tinyint		NOT NULL,
	学时	  tinyint		NOT NULL,
	学分	  tinyint		NOT NULL
); 

创建cj表

sql 复制代码
CREATE TABLE cj
(
	学号	 char(6)		NOT NULL,
	课程号	 char(3)		NOT NULL,
	成绩	 tinyint		NULL,
	PRIMARY KEY(学号, 课程号)
);

存储引擎在第三章PPT 68页左右

表分区也在

关系运算

选择运算

sql 复制代码
-- 原表 student
SELECT * FROM student WHERE age > 18;
-- 等价于 σ_age>18_(student)

投影运算

sql 复制代码
SELECT DISTINCT name, age FROM student;
-- 等价于 π_name, age_(student)

连接运算

等值连接

自然连接

sql 复制代码
-- 学生和班级按 class_id 连接
SELECT DISTINCT * FROM student s
JOIN class c ON s.class_id = c.class_id;

自然连接

自动找出同名列,按这些列的值相等来连接,结果中同名列只保留一列

sql 复制代码
-- 前提:student 和 class 都有 class_id 列
SELECT * FROM student NATURAL JOIN class;
-- 自动按 class_id 连接,且 class_id 只在结果中出现一次

外连接(Outer Join)

保留左/右/双侧所有行,无匹配的填 NULL。

左外连接 ⟕:保留左表所有行

右外连接 ⟖:保留右表所有行

全外连接 ⟗:保留两边所有行

sql 复制代码
-- 左外:所有学生,没有班级的显示NULL
SELECT * FROM student s LEFT JOIN class c ON s.class_id = c.class_id;

-- 全外(MySQL 不支持 FULL JOIN,用 UNION 模拟)
(SELECT * FROM student s LEFT JOIN class c ON ...)
UNION
(SELECT * FROM student s RIGHT JOIN class c ON ...);

选择输出

按专业查询学生表中各个学生的学号、姓名和总学分。

sql 复制代码
SELECT * FROM xs  WHERE 专业= '计算机';						
SELECT 学号 AS ID, 姓名 AS NAME, 总学分 AS 'Total credit'
	FROM xs
	WHERE 专业= '通信工程';

把成绩表百分制成绩分成等级

sql 复制代码
SELECT 学号,成绩,
		CASE 
		WHEN 成绩>=90  THEN '优秀'
		WHEN 成绩>=80 AND  成绩<90 THEN  '良好'
		WHEN 成绩>=70 AND  成绩<80 THEN  '中等'
		WHEN 成绩>=60 AND  成绩<70 THEN  '及格'
		ELSE  '不及格'
		END AS 等级
	FROM  cj
	WHERE 课程号 = '206';

查询2022年入学学生的年龄。

sql 复制代码
SELECT 学号, 姓名, 出生日期, year(now())-year(出生日期)+1 AS年龄
	FROM xs
	WHERE left(学号,2)='22';

消除重复行

sql 复制代码
SELECT DISTINCT 课程号
FROM cj;

聚合函数

以下是 MySQL 中全部聚合函数的完整列表(基于 MySQL 8.0):

函数名 说明
COUNT() 返回查询结果的行数。用法:COUNT(*) 统计所有行,COUNT(列) 忽略 NULL,COUNT(DISTINCT 列) 去重统计
MAX() 返回指定列的最大值
MIN() 返回指定列的最小值
SUM() 返回指定列所有值的总和(忽略 NULL)
AVG() 返回指定列所有值的平均值(忽略 NULL)
STD() STDDEV_POP() 的同义词,返回总体标准差
STDDEV() STDDEV_POP() 的同义词,返回总体标准差
STDDEV_POP() 返回总体标准差
STDDEV_SAMP() 返回样本标准差
VARIANCE() VAR_POP() 的同义词,返回总体方差
VAR_POP() 返回总体方差
VAR_SAMP() 返回样本方差
GROUP_CONCAT() 将分组中的列值连接成一个字符串,可自定义分隔符、排序、去重
BIT_AND() 返回所有位的按位与结果
BIT_OR() 返回所有位的按位或结果
BIT_XOR() 返回所有位的按位异或结果
JSON_ARRAYAGG() 将分组中的值聚合为一个 JSON 数组
JSON_OBJECTAGG() 将分组中的键值对聚合为一个 JSON 对象
  • STDSTDDEVVARIANCE 是旧版本函数,推荐使用 STDDEV_POPVAR_POP 等更明确的函数。
  • BIT_ANDBIT_ORBIT_XOR 用于位运算聚合。
  • GROUP_CONCAT 常用于将多行某列的值拼接,如 GROUP_CONCAT(name SEPARATOR ', ')
  • JSON_ARRAYAGGJSON_OBJECTAGG 是 MySQL 5.7.22 后引入的,用于直接生成 JSON 结构。

求选修101号课程的学生的人数、最高分、最低分和平均成绩。

sql 复制代码
SELECT COUNT(成绩),MAX(成绩), MIN(成绩), AVG(成绩)
    	FROM cj
    	WHERE 课程号 = '101';

求选修了206号课程的学生的学号。

sql 复制代码
SELECT GROUP_CONCAT(学号)
	FROM cj
	WHERE 课程号= '206';

查询(xsk)学生表共同的爱好和所有的爱好。

sql 复制代码
SELECT * FROM xsk;					#(a)
SELECT BIT_OR(爱好), BIT_AND(爱好) FROM xsk;			#(b)

限制输出行

sql 复制代码
SELECT 学号, 姓名, 专业, 性别, 出生日期, 总学分
	FROM xs
	ORDER BY 学号
	LIMIT 5;
	-- 第一行到第五行													#(a)
SELECT 学号, 姓名, 专业, 性别, 出生日期, 总学分
	FROM xs
	ORDER BY 学号
	LIMIT 5, 5;	
	-- 第五行到第十行

全连接

查找所有学生选过的课程信息。

sql 复制代码
SELECT DISTINCT kc.课程名, cj.课程号,学时,学分
    	FROM kc, cj 
    	WHERE kc.课程号=cj.课程号;

join连接

内连接

查找所有学生选过的课程名和课程号。

sql 复制代码
SELECT  DISTINCT 课程名, cj.课程号
	FROM KC INNER JOIN cj
		ON  (kc.课程号=cj.课程号); 

查找选修了206号课程且成绩80分以上的学生的姓名及成绩。

sql 复制代码
SELECT姓名,成绩
	FROM xs JOIN cj ON xs.学号 = cj.学号
 	WHERE 课程号='206'  AND 成绩>=80;

查找选修了计算机基础课程且成绩不少于80分的学生的学号、姓名、课程名及成绩。

sql 复制代码
SELECT xs.学号, 姓名, 课程名, 成绩
    	FROM cj JOIN  xs  ON  cj.学号 = xs.学号
		    JOIN  kc  ON  cj.课程号 = kc.课程号
    	WHERE 课程名='计算机导论' AND 成绩>=80 ;

自连接

查找相同成绩的学生的学号、课程号和成绩

sql 复制代码
SELECT a.学号,a.课程号,b.课程号,a.成绩
	FROM cj as a JOIN  cj as b
		ON a.成绩=b.成绩 AND a.课程号!=b.课程号
	WHERE a.成绩>=85;

查询课程表中所有学生选过的课程号和课程名。

sql 复制代码
SELECT DISTINCT 课程号,课程名
    	FROM kc INNER JOIN cj 
		USING (课程号);

查询前5个成绩记录学生的学号、课程名和成绩。

sql 复制代码
SELECT  学号,课程名,成绩
    	FROM kc INNER JOIN cj 
		USING (课程号)
		LIMIT 5;
		USING (课程号);

外连接

左外连接(LEFT JOIN)保留左表的所有行,右表匹配不上则填 NULL。

sql 复制代码
SELECT s.id, s.name, s.class_id, c.class_name
FROM student s
LEFT JOIN class c ON s.class_id = c.class_id;

右外连接(RIGHT JOIN)保留右表的所有行,左表匹配不上则填 NULL。

sql 复制代码
SELECT s.id, s.name, c.class_id, c.class_name
FROM student s
RIGHT JOIN class c ON s.class_id = c.class_id;

全外连接(FULL JOIN)保留左右两表的所有行,任何一边匹配不上都填 NULL。

sql 复制代码
-- 模拟全外连接
SELECT s.id, s.name, s.class_id, c.class_name
FROM student s
LEFT JOIN class c ON s.class_id = c.class_id
UNION
SELECT s.id, s.name, c.class_id, c.class_name
FROM student s
RIGHT JOIN class c ON s.class_id = c.class_id;

查找所有学生情况及他们选修的课程号,若学生未选修任何课程,也要包括其情况。

sql 复制代码
SELECT xs.* , 课程号, 成绩
FROM xs LEFT OUTER JOINcjON xs.学号 = cj.学号;

查找被选修了的课程的选修情况和所有开设的课程名。

sql 复制代码
SELECT 课程名,cj.* 
    	FROM cj RIGHT JOIN kc ON cj.课程号=kc.课程号
		LIMIT 40,10;

使用自然连接查询成绩表中的课程号和课程名。

sql 复制代码
SELECT课程名, 课程号
	FROM kc 
	WHERE课程号IN
		(SELECT DISTINCT 课程号 FROM kc NATURAL RIGHT OUTER JOIN cj);

交叉连接

假设左表有 M 行,右表有 N 行,交叉连接的结果就是 M × N 行,没有任何过滤或匹配条件。每一行都和其他所有行配对。

查询学生表所有可能的选课情况。

sql 复制代码
SELECT xs.学号, 姓名, 课程号
	FROM xs CROSS JOIN cj
	LIMIT 40,10;

查询条件

运算符 示例 说明
= age = 18 等于
<=> phone <=> NULL NULL 安全等于
<> / != status <> 1 不等于
<, <=, >, >= score >= 90 大小比较
BETWEEN score BETWEEN 60 AND 80 闭区间范围
IN class_id IN (1,2) 属于列表
NOT IN id NOT IN (1,2) 不属于列表
LIKE name LIKE '张%' 通配符匹配
NOT LIKE name NOT LIKE '张%' 否定通配
REGEXP name REGEXP '^A' 正则匹配
IS NULL phone IS NULL 是空值
IS NOT NULL phone IS NOT NULL 非空

模式匹配

REGEXP运算符

符号 说明 示例 匹配结果
^ 字符串开头 '^A' 以 A 开头的字符串
$ 字符串结尾 'ing$' 以 ing 结尾
. 匹配任意单个字符(除换行符) 'a.b' a + 任意字符 + b
* 前一个字符重复 0 次或多次 'go*' g, go, goo, gooo...
+ 前一个字符重复 1 次或多次 'go+' go, goo, gooo... (不含 g)
? 前一个字符重复 0 次或 1 次 'colou?r' color 或 colour
{n} 恰好 n 次 'a{3}' aaa
{n,} 至少 n 次 'a{2,}' aa, aaa, aaaa...
{n,m} n 到 m 次 'a{2,4}' aa, aaa, aaaa
[abc] 匹配括号内任意一个字符 '[aeiou]' 元音字母
[^abc] 匹配不在括号内的任意字符 '[^0-9]' 非数字
[a-z] 范围,a 到 z '[A-Z]' 大写字母
`(abc def)` 分组,匹配 abc 或 def `'^(Tom
\ 转义特殊字符 '\\.' 匹配字面点号 .
\\b 单词边界 '\\bcat\\b' 匹配单独的单词 cat
\\d 数字(等价 [0-9] '^\\d+$' 纯数字
\\w 单词字符(字母数字下划线) '\\w+' 至少一个单词字符
\\s 空白字符 'a\\sb' a空格b 或 a制表符b

查询学生表中姓"王"的学生的学号、姓名及性别。

sql 复制代码
SELECT 学号,姓名,性别
	FROM xs
	WHERE姓名LIKE '王%';

查询学生表中大三(学号第二个数字为0)的学生的学号、姓名及专业。

sql 复制代码
SELECT 学号,姓名,专业
	FROM xs
	WHERE学号LIKE '_0%';

查询学生表姓"李"的学生的学号、姓名和专业。

sql 复制代码
SELECT 学号,姓名,专业
	FROM xs
 	WHERE姓名REGEXP '^李';
sql 复制代码
SELECT学号,姓名,专业
	FROM xs
	WHERE学号REGEXP '[4,5,6]';

查询学生表学号以22开头、以01结尾的学生的学号、姓名和专业。

sql 复制代码
SELECT学号,姓名,专业
	FROM xs
	WHERE学号REGEXP '^22.*01$';

范围比较的关键字有两个:BETWEEN和IN。

查询学生表中2004年出生的学生的情况。

sql 复制代码
SELECT 学号, 姓名, 专业, 出生日期
 	FROM xs
	WHERE出生日期BETWEEN '2004-1-1' AND '2004-12-31';

查询学生表中计算机、通信工程或电气工程专业的学生的情况。

sql 复制代码
SELECT *
	FROM xs
	WHERE专业IN  ('计算机', '通信工程', '电气工程');

空值判断

查询学生表备注为空的学生的情况。

sql 复制代码
SELECT *
	FROM xs
	WHERE备注 IS NULL;

子查询

查找xscj数据库中选修了206号课程的学生的姓名、学号。

sql 复制代码
SELECT姓名,学号
	FROM xs
	WHERE 学号IN
		( SELECT 学号
			FROM cj
			WHERE 课程号 = '206' 
		);

查找未选修离散数学课程的学生的姓名、学号、专业。

sql 复制代码
SELECT 姓名,学号,专业
	FROM xs
	WHERE 学号 NOT IN
		(SELECT 学号
			FROM cj
			WHERE 课程号 IN
				( SELECT 课程号
					FROM kc
					WHERE课程名 = '离散数学'
				)
		);

查找选修了离散数学课程的学生的学号。

sql 复制代码
SELECT count(学号)
	FROM cj
	WHERE 课程号 =
		(SELECT 课程号
			FROM kc
			WHERE课程名 ='离散数学'
		);

查找学生表中比所有计算机专业的学生出生日期都大的学生的学号、姓名、专业、出生日期。

sql 复制代码
SELECT 学号, 姓名, 专业, 出生日期
	FROM xs
	WHERE  出生日期> ALL
		(SELECT 出生日期
			FROM xs
			WHERE 专业='计算机'
		);

查找课程号为206的成绩不低于课程号为102的最低成绩的学生的学号。

sql 复制代码
SELECT学号
	FROMcj
	WHERE 课程号 = '206'  AND 成绩>=ANY 
		(SELECT 成绩
			FROMcj
			WHERE课程号 ='102'
		);

查找选修206号课程的学生的姓名。

sql 复制代码
SELECT姓名
	FROM xs
	WHERE EXISTS
		(SELECT *
			FROM cj
			WHERE学号 = xs.学号AND课程号 = '206' 
		);

查找选修了全部课程的学生的姓名。

sql 复制代码
SELECT 姓名
 	FROM xs
 	WHERE NOT EXISTS
        	(SELECT *
             	FROM kc
 			WHERE NOT EXISTS
                 	( SELECT *
                       	FROMcj
					WHERE学号=xs.学号AND课程号=kc.课程号
         			)
   		 );

从xs表中查找总学分大于50分的男学生的姓名、学号和总学分。

sql 复制代码
SELECT  姓名,学号,性别,总学分
              			FROM xs
				WHERE 总学分>50
 		  	) AS student
	WHERE 性别;

从学生表中查找所有女学生的姓名、学号,以及与221101号学生的年龄差距。

sql 复制代码
SELECT 学号,姓名, YEAR(出生日期)-YEAR( 
                      ( SELECT 出生日期
                           	FROM xs 
 					WHERE学号='221101'
                       )  )  AS年龄差距
FROM xs
 WHERE 性别=FALSE;

查找与学号为221101的学生性别相同、总学分相同的学生的学号和姓名。

sql 复制代码
SELECT学号,姓名
 	FROM xs 
	WHERE  (性别,总学分)=( SELECT性别,总学分
						FROM xs
						WHERE学号='221101'
					);

查询分组

求xscj数据库中各专业的学生数。

sql 复制代码
SELECT 专业,COUNT(*) AS '学生数'
 	FROM xs
	GROUP BY专业;

求被选修的各门课程的平均成绩和选修该课程的人数。

sql 复制代码
SELECT 课程号, AVG(成绩) AS '平均成绩' ,COUNT(学号) AS' 选修人数'
  	FROM cj
	GROUP BY 课程号;

查询每个专业的男生人数、女生人数、总人数,以及学生总人数。

sql 复制代码
SELECT专业, 性别, count(*)  AS '人数'
 	FROM xs
	GROUP BY专业,性别
  	WITH  ROLLUP;

查询每门课程各专业的平均成绩、每门课程的总平均成绩和所有课程的总平均成绩。

sql 复制代码
SELECT 课程名, 专业, AVG(成绩) AS '平均成绩'
	FROMcj, kc,xs
 	WHEREcj.课程号 = kc.课程号ANDcj.学号 = xs.学号
 	GROUP BY 课程名, 专业
    	WITH ROLLUP;

分组后过滤

查询平均成绩在85分以上的学生的学号和平均成绩。

sql 复制代码
SELECT 学号, AVG(成绩) AS 平均成绩
 	FROMcj
	GROUP BY 学号
    	HAVING 平均成绩>=85;

查找选修课程超过2门且成绩都在70分以上的学生的学号。

sql 复制代码
SELECT 学号
    	FROM cj
    	WHERE 成绩>= 70
    	GROUP BY 学号
    	HAVING COUNT(*) > 2;

查找通信工程专业平均成绩在85分以上的学生的学号和平均成绩。

sql 复制代码
SELECT 学号,AVG(成绩) AS '平均成绩'
	FROMcj
	WHERE学号 IN 
        	 (  SELECT 学号
				FROM xs
				WHERE 专业= '通信工程'
         	)
	GROUP BY学号
 	HAVING AVG(成绩) >=85;

输出排序

将计算机专业学生的计算机导论课程成绩按降序排列。

sql 复制代码
SELECT xs.学号,姓名,成绩
	FROM xs,kc,cj
	WHERE  xs.学号= cj.学号
 		ANDcj.课程号= kc.课程号
 		AND课程名= '计算机导论'  
		AND 专业= '计算机'
	ORDER BY 成绩 DESC;

将出生日期2003年后的学生按专业名拼音排序,相同专业按照出生日期从小到大排序

sql 复制代码
SELECT 学号,姓名,专业,出生日期
	FROM  xsb
	WHERE 出生日期 >= '2004-1-1'
	ORDER BY 专业 DESC, 出生日期

将通信工程专业学生的情况按平均成绩升序排列。

sql 复制代码
SELECT学号, 姓名, 总学分
	FROM xs 
    	WHERE  专业= '通信工程' 
    	ORDER BY ( SELECT AVG(成绩)
			FROM cj
			GROUP by cj.学号
			HAVING xs.学号=cj.学号
           );

联合查询

联合计算机专业和通信工程专业2022学生信息。

sql 复制代码
SELECT *
	FROM xs_jsj
	WHERE 学号 LIKE '22%'
	UNION
	SELECT *
		FROM xs_txgc
		WHERE 学号 LIKE '22%';

视图

创建视图

创建只包含学生部分信息的视图

sql 复制代码
CREATE VIEW v_student AS
SELECT 学号, 姓名, 性别
FROM xs;

创建一个包含成绩和课程名的视图,并重命名列

sql 复制代码
CREATE VIEW v_score(学号, 姓名, 课程, 成绩) AS
SELECT xs.学号, xs.姓名, kc.课程名, cj.成绩
FROM xs
JOIN cj ON xs.学号 = cj.学号
JOIN kc ON cj.课程号 = kc.课程号;

创建xscj数据库上的cs_kc视图,包括计算机专业各学生的学号、选修的课程号及成绩。要保证对该视图的修改都符合专业为计算机这个条件。

sql 复制代码
CREATE OR REPLACE VIEW  xscj.cj_kc 
	AS
	SELECT xs.学号,课程号,姓名,成绩
		FROM xscj.xs, xscj.cj
		WHERE  xs.学号 = cj.学号 and xs.专业 = '计算机'
 		WITH CHECK OPTION;

创建计算机专业学生平均成绩视图cj_kc_avg视图,包括学号(在视图中的列名为vnum)和平均成绩(在视图中的列名为vavg)。

sql 复制代码
USE xscj;
CREATE VIEW cj_kc_avg(vnum, vavg)
    	AS
    	SELECT 学号,AVG(成绩)
         	FROM cj_kc
         	GROUP BY 学号;

查看视图

sql 复制代码
-- 查看所有视图(MySQL中视图和表在同一列表里,可通过 TABLE_TYPE 区分)
SHOW FULL TABLES WHERE TABLE_TYPE = 'VIEW';

-- 查看视图创建语句
SHOW CREATE VIEW 视图名;

删除视图

sql 复制代码
DROP VIEW [IF EXISTS] 视图名;

使用视图

视图查询和表完全一样:

sql 复制代码
SELECT * FROM v_score WHERE 成绩 > 80;

在视图cs_kc中查找计算机专业学生的学号和选修的课程号。

sql 复制代码
SELECT 学号,姓名,课程号,成绩
	FROM cj_kc
	LIMIT 5;

查找计算机专业平均成绩在75分以上的学生的学号和平均成绩。

sql 复制代码
SELECT *
 	FROM cj_kc_avg
 	WHEREvavg>=75;

创建视图,包含计算机专业的学生信息,并向视图中插入一条记录:

('221255','李牧','计算机',1,'2004-10-14',13,NULL,NULL)。

sql 复制代码
CREATE OR REPLACE VIEW xs_vjsj
 	AS
 	SELECT *
		FROM xs
		WHERE专业 = '计算机'
 	WITH CHECK OPTION;

INSERT INTO xs_vjsj
   	VALUES('221155', '李牧', '计算机', 1, '2004-10-14', 50, NULL, NULL);

视图本身不存数据,但可以作为代理将数据插入到基表。

只有结构简单、映射清晰的视图才能进行增删改。

插入时,MySQL 会自动把操作翻译成对基表的操作,就像你间接操作基表。

复杂视图(含聚合、分组、多表连接)则无法进行数据修改。

显示插入视图记录效果:

sql 复制代码
ELECT * 
	FROM xs_vjsj
	WHERE 学号='221155';

显示插入视图记录对源表的效果:

sql 复制代码
SELECT * 
	FROM  xs
	WHERE 学号='221155';

在xs_vjsj视图中修改'李牧'的学号为"221150"

UPDATE xs_vjsj

SET 学号 = '221150'

WHERE 学号 = '221155';