MySQL学习笔记
- 安装与配置
- myini文件内容:
- 初始化MySQL:
- MySQL语法:
- SQL-DDL
- 数据库
- 1.创建数据库
- 2.查看数据库
- 3.修改
- 4.删除
- 数据库中的表管理
- 1.创建表
- 2.查找
- 3.修改
- 4.删除
- 5.截断表
- SQL-DML
- 1.添加数据
- 1.1插入多条数据
- 1.2表内容复制
- 2.修改数据
- 3.删除
- TRUNCATE和DELETE的区别:
- SQL-约束Primary Key
- 创建主键约束
- 单列主键
- 联合主键
- **验证主键约束**
- 删除主键约束
- 设置主键自增AUTO_INCREMENT
- delete和truncate删除后,主键的自增
- SQL-唯一约束UNIQUE
- 创建唯一约束
- 修改唯一约束
- 删除唯一约束
- SQL-非空约束NOT NULL
- 创建非空约束
- 删除非空约束
- SQL-外键约束FOREIGN KEY
- 1.特点
- 2.作用
- 3.关键字:foreign key
- 4.主表和从表/父表和子表
- (1)建表时
- (2)建表后(了解)
- 5.如何查看外键约束名
- 6.如何查看外键字段索引
- 7.如何删除外键约束(了解)
- 8.SQL演示
- 9.演示问题
- 10.设置外键约束等级
- SQL-检查性约束CHECK
- 添加检查性约束
- 删除检查性约束
- SQL-默认值约束Default
- 添加默认值约束
- SQL-零填充约束zerofill
- 删除零填充约束
- SQL-DQL
- 重要地位
- 简单查询
- select简单查询
- ⭐查全表
- ⭐查询指定的字段
- 数据准备
- ⭐别名(AS)
- ⭐消除重复行(DISTINCT去重)
- 算数运算符
- 0.优先级
- ⭐1.算数运算符
- ⭐2.比较运算符
- ⭐3.逻辑运算符
- ⭐4.位运算符
- 空值NULL
- 空值参与运算
- 条件查询
- 普通条件查询
- 特殊比较运算符
- BETWEEN...AND...
- IN
- LIKE
- IS NULL
- least,greatest运算符
- ⭐对查询结果排序【排序查询】
- **简介:**
- **语法:**
- 排序规则说明
- 按列名排序
- 按别名排序
- 按列序号排序
- 按多列排序
- ⭐聚合函数
- 聚合函数处理NULL值的小测试
- ⭐Group By 分组
- Having是分组后进行筛选
- ⭐分页查询LIMIT
- ⭐SQL语法顺序最终版
- ⭐执行顺序
- 多表查询
- 内连接
- 外连接
- 数据准备
- 左外连接
- 右外连接
- 满外连接
- 嵌套查询(子查询)
- 1.单行单列
- 2.多行单列
- 3.多行多列
- 子查询关键字
- ALL
- ANY
- IN
- EXISTS
- 自关联查询
- 数据准备
- DQL总练习题
- DQL难题
- SQL函数
- 数据准备
- group_concat函数
- 数学相关函数
- abs()取绝对值
- sqrt()开平方
- mod(x,y)求余数
- ceil(x)和ceiling(x)向上取整
- floor(x)向下取整
- rand()生成随机数
- round()四舍五入函数
- sign()判断值的范围
- pow(x,y),x的y次方
- sin()函数
- truncate(x,y)截断
- 字符串相关的函数
- lower() 将大写转小写
- upper() 将小写转大写
- concat() 字符串连接
- substr()/substring()截取字符串
- length() 返回字符串的长度
- char_length() character_length() 返回一共有多少个字
- instr() 在字符串中找某个字符串第一次出现的位置
- LPAD()、RPAD()指定字符串输出的长度
- trim去除字符串的字符/空格
- replace() 替换字符串
- repeat() 重复字符串
- reverse() 反转字符串
- format() 格式化字符串
- 日期时间相关函数
- 获取日期相关函数
- 获取日期
- 获取时间
- 获取当前天
- 日期相关的转换
- 格式化时间
- extract函数
- 日期相关计算问题(向后/向前偏移时间)
- datediff(end_date,start_date) 和指定时间差多少天
- 练习
- 流程控制函数
- if()类似三目运算
- ifnull()判断是否为空
- nullif(expr1,expr2) 比较两个字符串是否相等,相等为null,不相等返回expr1
- 练习
- 开窗函数MySQL8特有
- 数据准备
- 排序函数
- 开窗聚合函数
- 滑动窗口
- 练习
- 前后函数
- 首尾函数
- 分箱函数
- MySQL事务(待补充)
- 练习题1
安装与配置
官网:
MySQL :: Download MySQL Installer
阿里云:
MySQL8 https://www.alipan.com/s/auhN4pTqpRp 点击链接保存,或者复制本段内容,打开「阿里云盘」APP ,无需下载极速在线查看,视频原画倍速播放。
作为数据库的景象数据库
1.将解压文件解压到你安装的目录:E:\mysql-8.0.11-winx64 不要放在有中文名字和空格的的目录下.例如
2.在mysql-8.0.11-winx64文件夹下面新建一个my.ini文件和一个data文件夹,配置初始化的my.ini文件的文件,并写入基本配置
myini文件内容:
[mysqld]
# 设置3306端口
port=3306
# 设置mysql的安装目录
basedir=C:\Program Files\MySQL
# 设置mysql数据库的数据的存放目录
datadir=C:\Program Files\MySQL\Data
# 允许最大连接数
max_connections=200
# 允许连接失败的次数。
max_connect_errors=10
# 服务端使用的字符集默认为utf8mb4
character-set-server=utf8mb4
# 创建新表时将使用的默认存储引擎
default-storage-engine=INNODB
# 默认使用“mysql_native_password”插件认证
#mysql_native_password
default_authentication_plugin=mysql_native_password
[mysql]
# 设置mysql客户端默认字符集
default-character-set=utf8mb4
[client]
# 设置mysql客户端连接服务端时默认使用的端口
port=3306
default-character-set=utf8mb4
注意:配置文件中的路径要和实际存放的路径一致,注意路径的反斜杠要改成斜杠
初始化MySQL:
在安装时,避免出错我们尽量全部使用管理员身份运行CMD,否则在安装时会报错,会导致安装失败的情况
3.进入mysql的bin目录
4.在MySQL目录下的bin目录下执行命令:
mysqld --initialize --console
5.安装mysql服务
安装mysql服务(注意,一定要是管理员权限)
执行下面的命令:
进入DOS命令后,对MySQL服务器开启或暂停操作
net stop 服务名称(mysql);
net start 服务名称(mysql);
mysqld --install [服务名](服务名可以不加默认为mysql)
成功了!
成功后有个标识,右键此电脑-服务-计算机管理 -服务和应用程序
6.启动MySQL 服务
服务安装成功之后通过命令net start mysql启动MySQL的服务
7.修改密码
mysql的bin目录下 进行数据库连接 mysql -u root -p
再输入密码 回车
有了mysql> 这个的时候 你就可以去改密码了!
ALTER USER 'root'@'localhost' IDENTIFIED BY '123456';
8.密码修改成功后
exit;(退出mysql)
连接MySQL
相信大家都有的Navicat,小海豚等数据库图形化工具
OK了
MySQL语法:
SQL-DDL
数据库
1.创建数据库
直接创建:CREATE DATABASE 数据库名;
CREATE DATABASE db1;
如果不存在则创建:CREATE DATABASE IF NOT EXISTS 数据库名;
CREATE DATABASE if NOT EXISTS db2;
创建数据库时并设置字符集:CREATE DATABASE 数据库名 CHARACTER SET 字符集;
CREATE DATABASE db3 CHARACTER SET GBK;
2.查看数据库
查看所有的数据库:SHOW DATABASES;
SHOW DATABASES;
看某个数据库的定义信息:SHOW CREATE DATABASE 数据库名;
SHOW CREATE DATABASE db3;
查看正在使用的数据库:SELECT DATABASE();
SELECT DATABASE();
切换数据库: use 数据库名;
USE db1;
3.修改
修改数据库字符集:ALTER DATABASE 数据库名 DEFAULT CHARACTER SET 字符集;
ALTER DATABASE db3 DEFAULT CHARACTER SET UTF8MB4;
4.删除
删除数据库 DROP DATABASE 数据库名;
DROP DATABASE db3;
数据库中的表管理
1.创建表
CREATE TABLE [IF NOT EXISTS]表名( 字段名1 字段类型, 字段名2 字段类型, 字段名3 字段类型 );
CREATE TABLE student(
sno INT,
sname VARCHAR(10),
sgender CHAR(1),
birthday date
);
根据某个旧表的结构,创建一个新表:CREATE TABLE 新表名 LIKE 旧表名;
-- 复制表,只复制表结构
CREATE TABLE student3 LIKE student2;
复制某个表中的所有的信息,包含数据 :CREATE TABLE 新表名 查询语句;
-- 复制表,包含表中的数据student
CREATE TABLE student4 SELECT * FROM student2;
2.查找
查看某个数据库中的所有的表:SHOW TABLES;
SHOW TABLES;
查看某个表的结构信息:DESC 表名;
DESC stu;
查看某个表的创建SQL语句 : SHOW CREATE TABLE 表名;
-- 查看表的创建信息
SHOW CREATE TABLE stu;
3.修改
给表中新增一列:ALTER TABLE 表名 ADD 列名 类型(长度);
-- 为已经存在的表添加新列
ALTER TABLE student2 ADD birthday DATE;
修改已经存在的列的数据类型:ALTER TABLE 表名 MODIFY 列名 新数据类型;
-- 修改已经存在的列的数据类型
ALTER TABLE student2 MODIFY sname VARCHAR(20);
修改列名:ALTER TABLE 表名 CHANGE 旧列名 新列名 类型;
-- 修改列名
ALTER TABLE student2 CHANGE sgender gender CHAR(1);
删除列:ALTER TABLE 表名 DROP 列名;
-- 删除列
ALTER TABLE student2 DROP birthday;
修改表名:RENAME TABLE 表名 TO 新表名;
-- 修改表名
RENAME TABLE student2 TO stu;
修改字符集: ALTER TABLE 表名 character set 字符集;
-- 修改表的字符集
ALTER TABLE stu CHARACTER SET GBK;
4.删除
直接删除表: DROP TABLE 表名;
DROP TABLE copy_emp;
先判断是否存在再删除: DROP TABLE IF EXISTS 表名;
-- 判断表是否存在,如果不存在则删除
DROP TABLE if EXISTS student4;
5.截断表
TRUNCATE 【TABLE】 表名 ;删除表和表中的数据,重新创建的表和原来的表的结构一样
TRUNCATE TABLE copy_emp;
SQL-DML
数据操作的语言,主要对MySQL进行增加、删除、修改
1.添加数据
INSERT INTO 表名(列名1,列名2,....) VALUES (值1,值2,....)
INSERT INTO 表名(列名1,列名2,....) VALUES (值1,值2,....),(值1,值2,....)....
INSERT INTO 表名(列名)
--添加数据,单行添加部分列
INSERT INTO dept(deptno,dname) VALUES (1,'设计部');
INSERT INTO dept(deptno,dname) VALUES (2,'研发部');--添加数据,单行添加所有列
INSERT INTO dept(deptno,dname,location) VALUES (3,'研发部','西安');
INSERT INTO dept(deptno,dname,location) VALUES (4,'研发部',NULL);
INSERT INTO dept VALUES (5,'研发部',NULL);-- 添加多行数据
INSERT INTO `dept` VALUES(6,'测试部','上海'),(7,'人事部','武汉');
1.1插入多条数据
一次性插入多条语句
INSERT INTO 表名 (列名1,列名2,…) 查询语句;
-- 插入多条数据
CREATE TABLE product2(NAME VARCHAR(10),jiage DOUBLE
)-- 一次性插入多条语句
-- INSERT INTO 表名 (列名1,列名2,...) 查询语句;INSERT INTO product2(NAME,jiage)
SELECT pname,price
FROM product
WHERE category_id IN('c001','c003');SELECT * FROM product2;
-- product3统计每类商品有多少个,通过category_id商品分类进行操作
CREATE TABLE product3(category_id VARCHAR(10),product_count int
)
-- 将分组查询的结果插入到数据中
INSERT INTO product3
SELECT category_id,COUNT(*)
FROM product
GROUP BY category_idSELECT * FROM product3
1.2表内容复制
方式1
-- product4表创建时,复制表结构
CREATE TABLE product4
SELECT *
FROM product
WHERE 1=0;DESC product4;
-- 表内容复制
INSERT INTO product4 SELECT * FROM product;
方式2
-- 表结构复制
CREATE TABLE product5
SELECT *
FROM product
WHERE 1=1;SELECT * FROM product5;
2.修改数据
UPDATE 表名 SET 列名1=值1,列名2=值2...
UPDATE 表名 SET 列名1=值1,列名2=值2... WHERE 条件表达式
修改表中符合条件的某些列
UPDATE emp SET deptno=30 WHERE empno=2296;
修改表中的所有列
UPDATE emp SET sal=sal-1000,deptno=deptno-10;
3.删除
将表中的数据均删除
DELETE FROM 表名
删除表中符合条件表达式的记录
DELETE FROM 表名 WHERE 条件表达式
删除部分行
删除job为ABC的行
DELETE FROM emp WHERE ename='ABC';
删除所有的行
DELETE FROM emp;
TRUNCATE和DELETE的区别:
TRUNCATE删除表和表中的数据,重新创建的表和原来的表的结构一样
取消自增的默认值,从默认值1开始(自定义的默认值也不存在)
TRUNCATE TABLE emp;DELETE删除的是表中的数据,可以跟条件表达式
DELETE FROM 表名 WHERE 条件表达式
自增的默认值是从断点的位置自增的数据的值上自增
SQL-约束Primary Key
主键不可重复,主键不能为NULL
一个表中只能有一个主键(包含联合主键)
添加主键的列的值不能为空,也不能重复
唯一标识表中的一行数据,提高查询效率
创建主键约束
单列主键
方式1:
列名 数据类型 PRIMARY KEY
-- 添加主键的约束方式1
-- 列名 数据类型 PRIMARY KEY
CREATE TABLE test1(NO INT PRIMARY KEY,NAME VARCHAR(10),age INT
);
方式2:
创建表完成后,修改表结构
ALTER TABLE 表名 ADD PRIMARY KEY(列名);
-- 添加主键的约束方式2
-- ALTER TABLE 表名 ADD PRIMARY KEY(列名);
CREATE TABLE test2(NO INT,NAME VARCHAR(10),age INT
);
ALTER TABLE test2 ADD PRIMARY KEY(NO);
单列主键的约束规则:
一张表只能有一个单列主键,不能重复,不能为空
联合主键
创建方式1
-- 创建方式1:
-- [CONSTRAINT pk1] PRIMARY KEY(列名1,列名2,...)
CREATE TABLE emp1(NAME VARCHAR(10),eid INT,sal INT,CONSTRAINT pk1 PRIMARY KEY(NAME,eid)
)DESC emp1;
创建方式2
-- 创建方式2:创建好后添加主键约束
CREATE TABLE emp2(NAME VARCHAR(10),eid INT,sal INT
)ALTER TABLE emp2 ADD PRIMARY KEY(NAME,eid);
DESC emp2;
验证主键约束
-- 验证主键约束
INSERT INTO emp1(eid,NAME,sal) VALUES(1,'张三',2000);
INSERT INTO emp1(eid,NAME,sal) VALUES(2,'李四',2000);
-- 不能添加,主键不能重复
INSERT INTO emp1(eid,NAME,sal) VALUES(2,'李四',2000);
-- 不能添加,主键不能为null
INSERT INTO emp1(eid,NAME,sal) VALUES(NULL,'王五',2000);
INSERT INTO emp1(eid,NAME,sal) VALUES(3,NULL,2000);
-- 注意,这里的'NULL'为字符串,可以添加
INSERT INTO emp1(eid,NAME,sal) VALUES(3,'NULL',2000);
删除主键约束
ALTER TABLE 表名 DROP PRIMARY KEY;
-- 删除主键
ALTER TABLE emp2 DROP PRIMARY KEY;
DESC emp2;
设置主键自增AUTO_INCREMENT
注意:无论SQL执行是否成功,都会自增
方式1
类名 数据类型 PRIMARY KEY AUTO_INCREMENT
-- 主键自增并设置起始值方式1
CREATE TABLE student3(id INT PRIMARY KEY AUTO_INCREMENT,sno INT,NAME VARCHAR(10)
)AUTO_INCREMENT=100;
-- AUTO_INCREMENT=100;设置默认值
DESC student3;
-- 添加数据查看自增从指定的100开始,每次自增1
INSERT INTO student3(sno,NAME) VALUES(1,'爪爪');
INSERT INTO student3(sno,NAME) VALUES(2,'猫猫');SELECT * FROM student3;
方式2
ALTER TABLE 表名 MODIFY COLUMN 字段名 字段数据类型 AUTO_INCREMENT PRIMARY KEY;
-- 主键自增并设置起始值方式2
CREATE TABLE student4(id INT,sno INT,NAME VARCHAR(10)
);
ALTER TABLE student4 MODIFY COLUMN id INT AUTO_INCREMENT PRIMARY KEY;
-- 设置默认值
ALTER TABLE student4 AUTO_INCREMENT = 20; DESC student4;
-- 添加数据查看自增从指定的100开始,每次自增1
INSERT INTO student4(sno,NAME) VALUES(1,'爪爪');
INSERT INTO student4(sno,NAME) VALUES(2,'猫猫');SELECT * FROM student4;
delete和truncate删除后,主键的自增
delete在删除之后从断点开始自增
DELETE FROM student4 WHERE id=21;--表中最后一个
INSERT INTO student4(sno,NAME) VALUES(2,'猫猫');-- id=22DELETE FROM student4;-- 删除表
INSERT INTO student4(sno,NAME) VALUES(3,'李郁');-- id=23
truncate数据之后自动增长默认从起始值1开始,和表之前创建时或者设置的指定自增无关
TRUNCATE student4;-- 截断表,实际上是删除表和表中的数据然后复制表结构
INSERT INTO student4(sno,NAME) VALUES(1,'爪爪');-- id=1
INSERT INTO student4(sno,NAME) VALUES(2,'猫猫');-- id=2
SQL-唯一约束UNIQUE
唯一约束,该约束的键所在的列不能重复,但可以为null
元素不能重复,但是值可以为NULL
一个表之中可以有多列为唯一约束
创建唯一约束
方式1
字段名 数据类型 UNIQUE
icard CHAR(18) UNIQUE
CREATE TABLE student5(id INT PRIMARY KEY AUTO_INCREMENT,sno INT,NAME VARCHAR(10),icard CHAR(18) UNIQUE
);
DESC student5;
SELECT * FROM student5;
-- 添加的元素只要不重复,均可添加,也可以为null
INSERT INTO student5(sno,NAME,icard) VALUES(1001,'猫猫','610721200509160985');
INSERT INTO student5(sno,NAME,icard) VALUES(1002,'猫猫','610721200509160986');
INSERT INTO student5(sno,NAME,icard) VALUES(1003,'猫猫',NULL);
INSERT INTO student5(sno,NAME) VALUES(1004,'猫猫');
修改唯一约束
方法2
修改表中的sno字段,添加唯一约束,要注意不然是空表,要不然里面的数据符合唯一约束
ALTER TABLE 表名 ADD UNIQUE(字段名);
ALTER TABLE student6 ADD UNIQUE(sno);
-- 给sno添加唯一约束
CREATE TABLE student6(id INT PRIMARY KEY AUTO_INCREMENT,sno INT,NAME VARCHAR(10),icard CHAR(18) UNIQUE
);
-- 修改表中的sno字段,添加唯一约束,要注意不然是空表,要不然里面的数据符合唯一约束
ALTER TABLE student6 ADD UNIQUE(sno);
DESC student6;SELECT * FROM student6;
-- 检查唯一约束信息
INSERT INTO student6(sno,NAME,icard) VALUES(1001,'袁继峰','610721200506090145');
-- 第二条数据不能添加,sno也是唯一约束
INSERT INTO student6(sno,NAME,icard) VALUES(1001,'袁继峰','610721200506090146');
删除唯一约束
ALTER TABLE 表名 DROP INDEX 字段名;
-- 删除唯一的约束
ALTER TABLE student6 DROP INDEX sno;
SQL-非空约束NOT NULL
特点:添加非空约束后,列不能为空
非空约束在一张表中可以有多列
创建非空约束
方式1:创建时声明非空约束
字段名 数据类型 NOT NULL
CREATE TABLE student7(id INT PRIMARY KEY,sno INT,NAME VARCHAR(10) NOT NULL
);
方式2:修改表的结构为非空
注意:修改时不能存在不符合数据的情况
ALTER TABLE 表名 MODIFY 字段名 数据类型 NOT NULL;
ALTER TABLE student7 MODIFY NAME VARCHAR(10) NOT NULL;
删除非空约束
-- 删除非空约束,将name字段的约束修改为NULL
ALTER TABLE student7 MODIFY NAME VARCHAR(10) NULL;
SQL-外键约束FOREIGN KEY
1.特点
(1)在“从表”中指定外键约束,并且一个表可以建立多个外键约束
(2)创建(create)表时就指定外键约束的话,先创建主表,再创建从表
(3)删表时,先删从表(或先删除外键约束),再删除主表
(4)从表的外键列,必须引用/参考主表的键列(主键或唯一键)
为什么?因为被依赖/被参考的值必须是唯一的
(5)从表的外键列的数据类型,要与主表被参考/被引用的列的数据类型一致,并且逻辑意义一致。
例如:都是表示部门编号,都是int类型。
(6)外键列也会自动建立索引(根据外键查询效率很高,很多)
(7)外键约束的删除,所以不会自动删除,如果要删除对应的索引,必须手动删除
2.作用
限定某个表的某个字段的引用完整性,
比如:员工表的员工所在部门的选择,必须在部门表能找到对应的部分。
3.关键字:foreign key
4.主表和从表/父表和子表
主表(父表):被引用的表,被参考的表
从表(子表):引用别人的表,参考别人的表
例如:员工表的员工所在部门这个字段的值要参考部门表,
部门表是主表,员工表是从表。
例如:学生表、课程表、选课表
选课表的学生和课程要分别参考学生表和课程表,学生表和课程表是主表,选课表是从表。
如何指定外键约束
(1)建表时
create table 主表名称(字段1 数据类型 primary key,字段2 数据类型
);create table 从表名称(字段1 数据类型 primary key,字段2 数据类型,foreign key (从表的某个字段) references 主表名(被参考字段)
);
#(从表的某个字段)的数据类型必须与主表名(被参考字段)的数据类型一致,逻辑意义也一样
#(从表的某个字段)的字段名可以与主表名(被参考字段)的字段名一样,也可以不一样
(2)建表后(了解)
alter table 从表名称 add foreign key (从表的字段) references 主表(被引用字段) 【on update xx】【on delete xx】;
5.如何查看外键约束名
desc 从表名称; #可以看到外键约束,但看不到外键约束名show create table 从表名称; #可以看到外键约束名SELECT * FROM information_schema.table_constraints WHERE table_name = '表名称';
#information_schema数据库名(系统库)
#table_constraints表名称(专门存储各个表的约束)
#WHERE条件
#table_name = '表名称'条件是指定表名称
6.如何查看外键字段索引
show index from 表名称; #查看某个表的索引名
7.如何删除外键约束(了解)
删除外键约束,不会自动删除外键约束列的索引,需要单独删除。
(1)第一步先查看约束名和删除外键约束
SELECT * FROM information_schema.table_constraints WHERE table_name = '表名称';#查看某个表的约束名alter table 从表名 drop foreign key 外键约束名;(2)第二步查看索引名和删除索引
show index from 表名称; #查看某个表的索引名alter table 从表名 drop index 索引名;
8.SQL演示
#演示外键约束
/*
1、作用:
用来限定子表和父表的引用关系。
子表的外键字段值必须在父类被引用字段值的范围内。2、外键约束一定是有两个表的角色。
其中一个作为子表(又称为从表),另一个作为父表(又称为主表)。子表外键字段的值要从父表被引用字段的值中选择。例如:员工表中记录员工信息,其中有一个字段是“部门编号”,表示这个员工所属的部门。部门的信息是记录在部门表中,但是员工表的“部门编号”字段的值不能随意填写,需要参考/引用“部门表”的部门记录。员工表是子表,引用别人,受部门表的牵制/范围管辖。部门表示父表,被别人引用。例如:商品表、用户表、订单表、订单明细表商品表里面记录了商品信息(商品名称、价格、库存量...)用户表里面记录了用户信息(用户名、密码、账户余额...)订单表里面记录了订单信息(订单编号、订单总价格、订单商品总数量、收获地址...)订单明细表记录了订单中每一个明细(商品编号、订单编号、价格、数量....)中午下了一个订单,买了“米粉”、“袜子”、“手机贴膜”、“高压锅”订单编号:112122123222222商品表:1 “米粉” 52.5 ....2 “袜子” 23.0 ....3 “手机贴膜” 15.0 ....4 “高压锅” 125.0 ....订单明细中:订单编号 商品编号 数量 价格 ....112122123222222 1 2(盒米粉) 52.5 112122123222222 2 1(打) 23.0 112122123222222 3 1(张) 15.0 112122123222222 4 1(个) 125.0订单明细表是子表,商品表和订单表是父表。 3、外键约束要求
(1)外键约束必须在子表中定义
(2)子表中要定义外键约束的字段的数据类型和逻辑意义,必须和父表中被引用字段的数据类型、逻辑意义一样。
例如:员工表中部门编号字段 引用 部门表中的部门编号字段订单明细表中的订单编号 引用 订单表中订单编号订单明细表中的商品编号 引用 商品表中商品编号(3)子表中要定义外键约束的字段的名字和父表中被引用字段名称可以不同。
(4)父表中被引用的字段必须是键约束字段,一般都是主键或唯一键约束。
此时子表和父表的关系可以是一对一或一对多的关系。
父表是一,子表是一或多。例如:父表(部门表)一条记录,对应子表(员工表)多条记录。父表(订单表)一条记录,对应子表(订单明细表)一条或多条记录。(5)父表必须先存在,然后才能正确建立子表的外键约束4、关键字 foreign key 和 references 5、外键约束约束了什么?
(1)建表的顺序,先建父表,再建子表
(2)删表的顺序,先删子表,再删父表,
或者先删掉外键约束,然后分别删除两个表(顺序就随意了)
(3)给子表添加数据、修改外键字段的数据,受影响,因为要从父表的被引用字段范围内选值。
(4)删除和修改父表的被引用字段的数据,受影响,因为要考虑这个值是否被子表引用了。
(5)是否建立外键,和查询无关。比喻:孩子的消费行为要受约束,在爸爸的挣钱范围内。爸爸的行为也要受约束,被依赖,不是那么自由。双方都会受影响。
*/#建表时,指定外键约束
create table dept(did int primary key auto_increment,dname varchar(50) unique key not null
);create table emp(id int primary key auto_increment,name varchar(20) not null,departmentid int, #子表中外键约束的字段名和父表的被引用字段名不要求一致,但是数据类型和逻辑意义要一样#外键约束只能在字段列表下面单独定义,不能在字段后面直接定义foreign key (departmentid) references dept(did)
);#查看表结构
desc dept;
desc emp;mysql> desc dept;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| did | int | NO | PRI | NULL | auto_increment |
| dname | varchar(50) | NO | UNI | NULL | |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.01 sec)mysql> desc emp;
+--------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+-------------+------+-----+---------+----------------+
| id | int | NO | PRI | NULL | auto_increment |
| name | varchar(20) | NO | | NULL | |
| departmentid | int | YES | MUL | NULL | |
+--------------+-------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)#查看表的定义
show create table dept;
show create table emp;mysql> show create table dept\G
*************************** 1. row ***************************Table: dept
Create Table: CREATE TABLE `dept` (`did` int NOT NULL AUTO_INCREMENT,`dname` varchar(50) NOT NULL,PRIMARY KEY (`did`),UNIQUE KEY `dname` (`dname`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)mysql> show create table emp\G
*************************** 1. row ***************************Table: emp
Create Table: CREATE TABLE `emp` (`id` int NOT NULL AUTO_INCREMENT,`name` varchar(20) NOT NULL,`departmentid` int DEFAULT NULL,PRIMARY KEY (`id`),KEY `departmentid` (`departmentid`),CONSTRAINT `emp_ibfk_1` FOREIGN KEY (`departmentid`) REFERENCES `dept` (`did`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)#查看系统库的约束表
SELECT * FROM information_schema.table_constraints WHERE table_name = '表名称';SELECT * FROM information_schema.table_constraints WHERE table_name = 'emp';mysql> SELECT * FROM information_schema.table_constraints WHERE table_name = 'emp';
+--------------------+-------------------+-----------------+--------------+------------+-----------------+----------+
| CONSTRAINT_CATALOG | CONSTRAINT_SCHEMA | CONSTRAINT_NAME | TABLE_SCHEMA | TABLE_NAME | CONSTRAINT_TYPE | ENFORCED |
+--------------------+-------------------+-----------------+--------------+------------+-----------------+----------+
| def | atguigu | PRIMARY | atguigu | emp | PRIMARY KEY | YES |
| def | atguigu | emp_ibfk_1 | atguigu | emp | FOREIGN KEY | YES |
+--------------------+-------------------+-----------------+--------------+------------+-----------------+----------+
2 rows in set (0.00 sec)#添加记录
#添加父表信息,没有影响
insert into dept values(null,'财务'),(null,'教学'),(null,'咨询'),(null,'后勤');mysql> select * from dept;
+-----+-------+
| did | dname |
+-----+-------+
| 4 | 后勤 |
| 3 | 咨询 |
| 2 | 教学 |
| 1 | 财务 |
+-----+-------+
4 rows in set (0.00 sec)#添加子表信息,有影响,受到约束
insert into emp values(null,'张三',1);#成功
insert into emp values(null,'李四',1);#成功
insert into emp values(null,'王五',2);#成功
insert into emp values(null,'赵六',6); #失败
#因为departmentid=1或2,在父表dept中可以找到对应记录
#因为departmentid=6,在父表dept中找不到对应记录mysql> insert into emp values(null,'赵六',6);
ERROR 1452 (23000): Cannot add(添加) or update(修改) a child(子表) row(记录/行):a foreign key constraint fails (`atguigu`.`emp`, CONSTRAINT `emp_ibfk_1` FOREIGN KEY (`departmentid`) REFERENCES `dept` (`did`))mysql> select * from emp;
+----+------+--------------+
| id | name | departmentid |
+----+------+--------------+
| 1 | 张三 | 1 |
| 2 | 李四 | 1 |
| 3 | 王五 | 2 |
+----+------+--------------+
3 rows in set (0.00 sec)#修改子表的外键字段的信息,有影响,受到约束
update emp set departmentid = 3 where id = 1;#成功
#因为departmentid = 3在父表dept中可以找到对应部门update emp set departmentid = 6 where id = 3; #失败
#因为departmentid = 6在父表dept中找不到对应部门mysql> update emp set departmentid = 6 where id = 3;
ERROR 1452 (23000): Cannot add or update a child row:
a foreign key constraint fails (`atguigu`.`emp`, CONSTRAINT `emp_ibfk_1` FOREIGN KEY (`departmentid`) REFERENCES `dept` (`did`))mysql> select * from emp;
+----+------+--------------+
| id | name | departmentid |
+----+------+--------------+
| 1 | 张三 | 3 |
| 2 | 李四 | 1 |
| 3 | 王五 | 2 |
+----+------+--------------+
3 rows in set (0.00 sec)mysql> select * from dept;
+-----+-------+
| did | dname |
+-----+-------+
| 4 | 后勤 |
| 3 | 咨询 |
| 2 | 教学 |
| 1 | 财务 |
+-----+-------+
4 rows in set (0.00 sec)#修改父表的被引用字段的值,受约束
update dept set did = 6 where did = 1;#失败
#因为did=1的部门被子表引用
update dept set did = 6 where did = 4;#成功
#因为 did=4的部门没有被子表引用mysql> update dept set did = 6 where did = 1;
ERROR 1451 (23000): Cannot delete(删除) or update(修改) a parent(父表) row(记录/行):a foreign key constraint fails (`atguigu`.`emp`, CONSTRAINT `emp_ibfk_1` FOREIGN KEY (`departmentid`) REFERENCES `dept` (`did`))mysql> select * from dept;
+-----+-------+
| did | dname |
+-----+-------+
| 6 | 后勤 |
| 3 | 咨询 |
| 2 | 教学 |
| 1 | 财务 |
+-----+-------+
4 rows in set (0.00 sec)#删除父表的记录,受约束
delete from dept where did = 6; #成功
#因为 did=6的部门没有被子表引用mysql> select * from dept;
+-----+-------+
| did | dname |
+-----+-------+
| 3 | 咨询 |
| 2 | 教学 |
| 1 | 财务 |
+-----+-------+
3 rows in set (0.00 sec)#删除父表的记录,受约束
delete from dept where did = 1; #失败
#因为 did=1的部门被子表引用mysql> delete from dept where did = 1;
ERROR 1451 (23000): Cannot delete or update a parent row:a foreign key constraint fails (`atguigu`.`emp`, CONSTRAINT `emp_ibfk_1` FOREIGN KEY (`departmentid`) REFERENCES `dept` (`did`))#删除子表的数据,不受约束
delete from emp where name ='王五'; #可以mysql> select * from emp;
+----+------+--------------+
| id | name | departmentid |
+----+------+--------------+
| 1 | 张三 | 3 |
| 2 | 李四 | 1 |
+----+------+--------------+
2 rows in set (0.00 sec)#删除父表,受约束
drop table dept; #失败mysql> drop table dept; #失败
ERROR 3730 (HY000): Cannot drop table 'dept' referenced by a foreign key constraint 'emp_ibfk_1' on table 'emp'.#删除子表,不受约束
drop table emp;#能不能在建表后添加外键约束,如何可以,怎么写?
alter table 子表名 add foreign key(子表的外键字段名) references 父表名(被引用字段);create table emp(id int primary key auto_increment,name varchar(20) not null,departmentid int
);#给emp表(子表)增加外键约束
alter table emp add foreign key(departmentid) references dept(did);#查看emp的约束信息
SELECT * FROM information_schema.table_constraints WHERE table_name = 'emp';
mysql> SELECT * FROM information_schema.table_constraints WHERE table_name = 'emp';
+--------------------+-------------------+-----------------+--------------+------------+-----------------+----------+
| CONSTRAINT_CATALOG | CONSTRAINT_SCHEMA | CONSTRAINT_NAME | TABLE_SCHEMA | TABLE_NAME | CONSTRAINT_TYPE | ENFORCED |
+--------------------+-------------------+-----------------+--------------+------------+-----------------+----------+
| def | atguigu | PRIMARY | atguigu | emp | PRIMARY KEY | YES |
| def | atguigu | emp_ibfk_1 | atguigu | emp | FOREIGN KEY | YES |
+--------------------+-------------------+-----------------+--------------+------------+-----------------+----------+
2 rows in set (0.01 sec)#只有是键约束(主键、唯一键、外键),都会自动创建索引。
#查看emp表的索引
show index from emp;mysql> show index from emp;
+-------+------------+--------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+-------+------------+--------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| emp | 0 | PRIMARY | 1 | id | A | 0 | NULL | NULL | | BTREE | | | YES | NULL |
| emp | 1 | departmentid | 1 | departmentid | A | 0 | NULL | NULL | YES | BTREE | | | YES | NULL |
+-------+------------+--------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
2 rows in set (0.01 sec)
#主键字段索引名是PRIMARY,删除主键时,会自动删除对应索引
#唯一键字段索引名是字段名,删除唯一键时,就是通过删除对应的索引方式来删除唯一键约束
#外键字段索引名是字段名,删除外键时,#删除外键约束
alter table 表名称 drop foreign key 外键约束名;#删除emp表的departmentid字段的外键约束
alter table emp drop foreign key emp_ibfk_1;mysql> show index from emp;
+-------+------------+--------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+-------+------------+--------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| emp | 0 | PRIMARY | 1 | id | A | 0 | NULL | NULL | | BTREE | | | YES | NULL |
| emp | 1 | departmentid | 1 | departmentid | A | 0 | NULL | NULL | YES | BTREE | | | YES | NULL |
+-------+------------+--------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
2 rows in set (0.00 sec)#说明,删除外键约束时,不会自动删除外键字段的索引,因为它们的命名不一样,
#如果需要删除对应的索引,需要单独删
alter table 表名称 drop index 索引名;#删除emp表的departmentid字段的索引
alter table emp drop index departmentid;mysql> show index from emp;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| emp | 0 | PRIMARY | 1 | id | A | 0 | NULL | NULL | | BTREE | | | YES | NULL |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
1 row in set (0.00 sec)
9.演示问题
(1)失败:不是键列
create table dept(did int , #部门编号dname varchar(50) #部门名称
);create table emp(eid int primary key, #员工编号ename varchar(5), #员工姓名deptid int, #员工所在的部门foreign key (deptid) references dept(did)
);ERROR 1215 (HY000): Cannot add foreign key constraint 原因是dept的did不是键列
(2)失败:数据类型不一致
create table dept(did int primary key, #部门编号dname varchar(50) #部门名称
);create table emp(eid int primary key, #员工编号ename varchar(5), #员工姓名deptid char, #员工所在的部门foreign key (deptid) references dept(did)
);ERROR 1215 (HY000): Cannot add foreign key constraint 原因是从表的deptid字段和主表的did字段的数据类型不一致,并且要它俩的逻辑意义一致
(3)成功,两个表字段名一样
create table dept(did int primary key, #部门编号dname varchar(50) #部门名称
);create table emp(eid int primary key, #员工编号ename varchar(5), #员工姓名did int, #员工所在的部门foreign key (did) references dept(did) #emp表的deptid和和dept表的did的数据类型一致,意义都是表示部门的编号#是否重名没问题,因为两个did在不同的表中
);
10.设置外键约束等级
- Cascade方式:在父表上update/delete记录时,同步update/delete掉子表的匹配记录
- Set null方式:在父表上update/delete记录时,将子表上匹配记录的列设为null,但是要注意子表的外键列不能为not null
- No action方式:如果子表中有匹配的记录,则不允许对父表对应候选键进行update/delete操作
- Restrict方式:同no action, 都是立即检查外键约束
- Set default方式(在可视化工具SQLyog中可能显示空白):父表有变更时,子表将外键列设置成一个默认的值,但Innodb不能识别
如果没有指定等级,就相当于Restrict方式。
/*
外键约束可以设置约束等级:
(1)No action方式:如果子表中有匹配的记录,则不允许对父表对应候选键进行update/delete操作
(2)Restrict方式:同no action, 都是立即检查外键约束
(3)Set null方式:在父表上update/delete记录时,将子表上匹配记录的列设为null,但是要注意子表的外键列不能为not null
(4)Cascade方式:在父表上update/delete记录时,同步update/delete掉子表的匹配记录
(5)Set default方式(在可视化工具SQLyog中可能显示空白):父表有变更时,子表将外键列设置成一个默认的值,但Innodb不能识别on update No action | Restrict | Set null | Cascade
on delete No action | Restrict | Set null | Cascade
如果没有写on update 或on delete字句,表示选择了No action | Restrict。*/
#父表
create table dept(did int primary key auto_increment,dname varchar(50) unique key not null
);insert into dept values(null,'财务'),(null,'教学'),(null,'咨询'),(null,'后勤');mysql> select * from dept;
+-----+-------+
| did | dname |
+-----+-------+
| 4 | 后勤 |
| 3 | 咨询 |
| 2 | 教学 |
| 1 | 财务 |
+-----+-------+
4 rows in set (0.00 sec)#子表
create table emp(id int primary key auto_increment,name varchar(20) not null,departmentid int,foreign key (departmentid) references dept(did) on update cascade on delete set null#on delete set null要求departmentid字段没有not null约束
);#添加子表时记录和 定义on update cascade on delete set null无关。还是要在主表能找到对应的记录。
insert into emp values(null,'张三',1);
insert into emp values(null,'李四',2);
insert into emp values(null,'王五',1);mysql> select * from emp;
+----+------+--------------+
| id | name | departmentid |
+----+------+--------------+
| 1 | 张三 | 1 |
| 2 | 李四 | 2 |
| 3 | 王五 | 1 |
+----+------+--------------+
3 rows in set (0.00 sec)#修改子表, 和 定义on update cascade on delete set null无关。还是要在主表能找到对应的记录。
update emp set departmentid = 6 where name = '王五';
#失败,因为departmentid = 6在父表dept中找不到对应部门#修改父表被引用的did值, 和 定义on update cascade on delete set null有关。
update dept set did = 6 where did = 1;
#此时did=1的记录被子表引用了,可以修改,并且会同时修改子表的departmentid=1的字段值为6,级联修改mysql> select * from dept;
+-----+-------+
| did | dname |
+-----+-------+
| 4 | 后勤 |
| 3 | 咨询 |
| 2 | 教学 |
| 6 | 财务 |
+-----+-------+
4 rows in set (0.00 sec)mysql> select * from emp;
+----+------+--------------+
| id | name | departmentid |
+----+------+--------------+
| 1 | 张三 | 6 |
| 2 | 李四 | 2 |
| 3 | 王五 | 6 |
+----+------+--------------+
3 rows in set (0.00 sec)#删除父表dept被引用的did的记录, 和 定义on update cascade on delete set null有关。
delete from dept where did = 6; #did=6的部门在子表中有引用mysql> select * from dept;
+-----+-------+
| did | dname |
+-----+-------+
| 4 | 后勤 |
| 3 | 咨询 |
| 2 | 教学 |
+-----+-------+
3 rows in set (0.00 sec)mysql> select * from emp;
+----+------+--------------+
| id | name | departmentid |
+----+------+--------------+
| 1 | 张三 | NULL |
| 2 | 李四 | 2 |
| 3 | 王五 | NULL |
+----+------+--------------+
3 rows in set (0.00 sec)
SQL-检查性约束CHECK
检查性约束的特点:
检查为非NULL的插入的记录的范围是否符合check后面的条件
添加检查性约束
字段 数据类型 CHECK (条件)
CREATE TABLE student(sid INT PRIMARY KEY,sno INT,sname VARCHAR(10),sex CHAR(2) CHECK (sex='男' OR sex='女'),age INT CHECK (age>=15 AND age<=30)
)
添加时检查记录是否符合约束条件
-- 添加成功
INSERT INTO student VALUES(1,1001,'爪爪','男',16);
INSERT INTO student VALUES(2,1002,'猫猫','女',16);
INSERT INTO student VALUES(3,1003,'阿鱼',NULL,NULL);
-- 不符合的情况
-- 超出年龄范围
INSERT INTO student VALUES(5,1003,'阿鱼','男',31);
-- 超出性别范围
INSERT INTO student VALUES(3,1003,'阿鱼','未知',16);
-- '' 检查性约束,只有为NULL时,才不会检查约束
INSERT INTO student VALUES(3,1003,'阿鱼','',NULL);
删除检查性约束
ALTER TABLE 表名 DROP CHECK 检查性约束名;
-- 显示表的创建规则
SHOW CREATE TABLE student;
-- 删除表的检查性约束2
ALTER TABLE student DROP CHECK student_chk_2;
SQL-默认值约束Default
添加默认值约束
CREATE TABLE student1(sid INT PRIMARY KEY,sno INT,sname VARCHAR(10),sex CHAR(2),age INT,nation VARCHAR(10) DEFAULT '中国'
);
如果给传值,以传的值为准,否则是默认值
-- 如果给传值,以传的值为准,否则是默认值
-- 国籍为NULL
INSERT INTO student1 VALUES(1,1001,'猫猫','女',16,NULL);
-- 国籍为默认值
INSERT INTO student1(sid,sno,sname) VALUES(2,1002,'爪爪');
-- 国籍为俄罗斯
INSERT INTO student1 VALUES(3,1003,'田欣怡','女',21,'俄罗斯');
SQL-零填充约束zerofill
CREATE TABLE student2(id INT ZEROFILL,NAME VARCHAR(20)
);
INSERT INTO student2 VALUES(1111,'猫猫');
INSERT INTO student2 VALUES(1,'爪爪');
删除零填充约束
-- 删除零填充约束
ALTER TABLE student2 MODIFY id INT;
INSERT INTO student2 VALUES(2,'困困');
SQL-DQL
DQL(Data Query Language 数据查询语言)。用途是查询数据库数据,如SELECT语句。是SQL语句中最核心、最重要的语句,也是使用频率最高的语句。其中,可以根据表的结构和关系分为单表查询和多表联查。
单表查询:针对数据库中的一张数据表进行查询,可以通过各种查询条件和方式去做相关的优化。
多表联查:针对数据库中两张或者两张以上的表同时进行查询, 依赖的手段有复杂查询和嵌套查询。
SELECT [DISTINCT]{*|表1.*|[ 表1.字段1 [as 字段别名1][, 表1.字段2[as 字段别名2]][, …]]}FROM 表1 [as 表别名 ][ left|right|inner join 表2 on 表之间
的关系 ][ WHERE][ GROUP BY ]
[ HAVING][ ORDER BY][ LIMIT {[ 位置偏移量,]行数}]; 其中:
“[ ]”包含的内容可以省略;
“{ }”包含的内容必须存在;必须按照该顺序使用
关键字:
DISTINCT:设定DISTINCT可以去掉重复记录。
AS:表名或者字段名过长时,可以用AS关键字起别名,方便操作。
GROUP BY:按组分类显示查询出的数据。
HAVING:GROUP BY分组时依赖的分组条件。
ORDER BY:将查询出来的结果集按照一定顺序排序完成。
LIMIT:限制显示查询结果的条数。
重要地位
数据库管理系统⼀个重要功能就是数据查询,SQL语句中最核⼼、最重要的语句,也是使⽤频率最 ⾼的语句.数据查询不应只是简单返回数据库中存储的数据,还应该根据需要对数据进⾏筛选以及确定数 据以什么样的格式显示。 MySQL提供了功能强⼤、灵活的语句来实现这些操作。 MySQL数据库使⽤select语句来查询数据。 应⽤
简单查询
通过简单查询,可以查询表中的所有列或者指定列,通过算术运算符、列别名以及消除重复⾏可以 改变查询结果的显示⽅式。
select简单查询
⭐查全表
select * from 表名
⭐查询指定的字段
select 字段名12....n from 表名
语法:
select [all|distinct]<目标列的表达式1> AS [别名],<目标列的表达式2> AS [别名]...
from <表名1或视图名1> [别名],<表名2或视图名2> [别名]...
[where <条件表达式>]
[group by <列名>]
[having <条件表达式>]
[order by <列名> [asc(从小到大排序)|desc(从大到小排序)]]
[limit <数字或列表>];简化版查询所有列语法
select * | 列名 from 表 where 条件
说明:
1、 "*" 号代表指定表中的所有列名;
2、 "|" 代表"或者" 的含义,所以SELECT⼦句后⾯可以既可以写"*" ,也可以写所有的列名,不能两种⽅式都写;
3、如果使⽤第⼆种⽅式,每个列名之间必须要⽤逗号分隔,最后⼀个列名后⾯不写逗号;
4、FROM⼦句后⾯写要查询的表名;
5、SQL语句的最后需要加分号;
数据准备
创建数据库和表:
CREATE DATABASE db3;
USE db3;
-- 简单查询
-- 创建商品表:
create table product(pid int primary key auto_increment, -- 商品编号pname varchar(20) not null , -- 商品名字price double, -- 商品价格category_id varchar(20) -- 商品所属分类
);
添加数据:
--c001家用电器
insert into product values(null,'海尔洗衣机',5000,'c001');
insert into product values(null,'美的冰箱',3000,'c001');
insert into product values(null,'格力空调',5000,'c001');
insert into product values(null,'九阳电饭煲',200,'c001');
--服饰
insert into product values(null,'啄木鸟衬衣',300,'c002');
insert into product values(null,'恒源祥西裤',800,'c002');
insert into product values(null,'花花公子夹克',440,'c002');
insert into product values(null,'劲霸休闲裤',266,'c002');
insert into product values(null,'海澜之家卫衣',180,'c002');
insert into product values(null,'杰克琼斯运动裤',430,'c002');--护肤品
insert into product values(null,'兰蔻面霜',300,'c003');
insert into product values(null,'雅诗兰黛精华水',200,'c003');
insert into product values(null,'香奈儿香水',350,'c003');
insert into product values(null,'SK-II神仙水',350,'c003');
insert into product values(null,'资生堂粉底液',180,'c003');--零食
insert into product values(null,'老北京方便面',56,'c004');
insert into product values(null,'良品铺子海带丝',17,'c004');
insert into product values(null,'三只松鼠坚果',88,null);
insert into product values(NULL,'老北京方便面',66,'c004');
INSERT INTO product VALUES(NULL,'老北京方便面',66,'c004');
简单的查询
-- 1.查询所有的商品
select pid,pname,price,category_id from product;
-- 推荐使⽤,可读性⾼
select * from product;
-- 2.查询商铺名和商品价格
select pname,price from product
⭐别名(AS)
select empno as '员工号',sal 工资 from emp
select p.empno,p.sal from emp p
列别名⽤来给查询语句中的列或者表达式重新命名,使语句的可读性更强。
语法:
SELECT 列名1 | 表达式1 [as] [列别名1],列名2 | 表达式2 [as] [列别名2], ...,列名n | 表达式n [as] [列别名n]
FROM table;
1.列别名可以直接写在列名或者表达式的后面,也可以添加as关键字,加不加as没有区别,通常不加。
2.如果select子句中有计算表达式,通常使用列别名。
-- 别名 字段名/表名 as(可省略) 别名
SELECT pname AS '商品名',price '价格' FROM product;
注意:
1.列别名中包含有空格
2.列别名中包含有特殊字符,如%,-等。
⭐消除重复行(DISTINCT去重)
重复⾏是查询结果中有完全相同的数据⾏,消除重复⾏是消除相同的查询结果,只保留⼀⾏重复数据,使⽤关键字 DISTINCT 来完成。
语法:
SELECT DISTINCT 列名1,列名2,......列名n FROM 表名;
例如:
-- 去重
-- 去重一列的数据
SELECT DISTINCT category_id FROM product;
-- 去重多列的数据
SELECT DISTINCT price,pname FROM product WHERE pname='老北京方便面' AND price=66;
也可以使⽤DISTINCT关键字消除多列重复数据(同时考虑所有列的数据重复)
算数运算符
简介
数据库中的表结构确⽴后,表中的数据代表的意义就已经确定。通过MySQL运算符进⾏运算,就可 以获取到表结构以外的另⼀种数据。 例如,学⽣表中存在⼀个birth字段,这个字段表示学⽣的出⽣年份。⽽运⽤MySQL的算术运算符⽤ 当前的年份减学⽣出⽣的年份,那么得到的就是这个学⽣的实际年龄数据。
MySQL支持四种运算符:
算数运算符、⽐较运算符、逻辑运算符、位运算符。
运算符优先级
优先级 | 运算分类 | 运算符举例 |
---|---|---|
1 | 算术运算符 | +,-,*,/,% |
2 | 比较运算符 | =,<>,<,>,<,<=,>= |
3 | 特殊比较运算符 | BETWEEN…AND…,IN,LIKE,IS NULL |
4 | 逻辑非 | NOT |
5 | 逻辑与 | AND |
6 | 逻辑或 | OR |
0.优先级
乘除优先于加减相同优先级按照从左至右的顺序依次计算可以使用括号提升优先级
⭐1.算数运算符
可以在SELECT⼦句中使⽤算术运算符(只对数值型数据起作⽤),来改变查询结果的显示⽅式。其中⽀持的5种运算符包括:
运算符 | 作用 |
---|---|
+ | 加 |
- | 减 |
* | 乘 |
/ 或 DIV | 除法运算,返回商 |
% 或 MOD | 求余运算,返回余数 |
-- 基本运算
-- 将所有的商品加价10元,只是在显示的时候进行操作,表中的数据不进行修改
SELECT pname,price,5*(price+10) AS new FROM product;SELECT 3+5;
SELECT 3-5;
SELECT 3*5;
SELECT 3/5;
SELECT 3%5;
⭐2.比较运算符
比较运算符 | 说明 |
---|---|
= | 等于 |
< 和 <= | 小于和小于等于 |
> 和 >= | 大于和大于等于 |
<=> | 安全的等于,两个操作码均为NULL时,其所得值为1;而当一个操作码为NULL时,其所得值为0 |
<> 或 != | 不等于 |
IS NULL 或 ISNULL | 判断一个只是否为NULL |
IS NOT NULL | 判断一个值是否不为NULL |
LEAST | 当有两个或多个参数时,返回最小值 |
GREATEST | 当有两个或多个参数时,返回最大值 |
BETWEEN…AND… | 判断一个值是否落在两值之间,包含边界值 |
IN | 判断一个值是IN列表中的任意一个值 |
NOT IN | 判断一个值不是IN列表中的任意一个值 |
LIKE | 通配符匹配 |
REGEXP | 正则表达式匹配 |
⭐3.逻辑运算符
(1)按位与运算&:如果左右两边是数字,进行位运算,同为1,则结果为1。(应用:a&1==0可判断是否为偶数,a&1==1可判断是否为奇数)
(2)按位或运算|:有一个为1,则结果为1.
(3)按位异或^:不同则为1,相同则为0.(应用:异或可进行两数快速交换)
(4)左移<<:每移动一次,该数值扩大为之前的两倍。
(5)右移>>:每移动一次,该数值缩小为之前的一半。
逻辑运算符 | 说明 |
---|---|
NOT 或者 ! | 逻辑非 |
AND 或者 && | 逻辑与 |
OR 或者 | |
XOR | 逻辑异或 |
⭐4.位运算符
位运算符是在⼆进制数上进⾏计算的运算符。位运算会先将操作数变成⼆进制数,进⾏位运算。然后再 将计算结果从⼆进制数变回⼗进制数。
位运算符 | 说明 |
---|---|
& | 按位与 |
^ | 按位异或 |
<< | 按位左移 |
>> | 按位右移 |
~ | 按位取反,反转所有比特 |
SELECT 3&5;--与
SELECT 3|5;
SELECT 3^5;-- 异或 不同为1,相同为0
SELECT 3>>5;
SELECT 3<<5;
空值NULL
空值⽤NULL表示,表示⼀种⽆效的、未知的值,空值不是零,也不是空格。
SELECT * FROM product WHERE pid>15;
查询结果如下:
空值参与运算
空值参与算术运算,运算后的结果仍为NULL —上述为字符串类型,可以单独修改查看
-- 如果某个值为NULL,NULL不参与运算,结果仍旧为NULL
INSERT INTO product VALUES(NULL,'三只松鼠坚果礼盒',NULL,'c004');
SELECT pname,price,price+10 AS '爪爪' FROM product;
insert into product values(NULL,' 三只松⿏坚果礼盒 ',NULL,'c004');
SELECT pid,pname,price+10,category_id FROM product WHERE pname = ' 三只松⿏坚果礼盒 ';
查询结果如下,可以看到空值参与算术运算后,结果仍为空值
条件查询
通过简单查询,可以查询表中的所有数据⾏。但⼤多数情况下,我们只需要查询符合某些特定条件的数据⾏,这就需要⽤到SQL语⾔中的条件查询来过滤。
普通条件查询
语法:
SELECT [DISTINCT] * | 列名 | 表达式 [别名] [,....]
FROM 表名
[WHERE 条件表达式1 逻辑运算符 条件表达式2];
说明:
1.条件查询使用WHERE子句来完成,WHERE子句必须紧跟在FROM子句后面。
2.条件表达式的格式为:列名 比较运算符 要比较的值,例如:deptno=10
3.常见的比较运算符包括:=、>、>=、<、<=、<>
含义为等于, 大于,大于等于,小于,小于等于,不等于
4.数值型、字符型、日期型数据都可以使用比较运算符进行比较。
5.字符型和日期型数据作为查询条件时,条件表达式右边的值,必须添加单引号
-- 条件查询
-- 1.查询商品价格为200的商品(数值型条件)
SELECT * FROM product WHERE price = 200;-- 2.查询商品名为老北京方便面(字符型条件)
SELECT * FROM product WHERE pname='老北京方便面';-- 3.查询商品价格不为200的商品信息(其他条件运算符)
SELECT * FROM product WHERE price != 200;SELECT * FROM product WHERE price <> 200;SELECT * FROM product WHERE NOT (price = 200);
-- 4.查询商品价格大于500的商品
SELECT * FROM product WHERE price > 500;-- 5.查询商品价格在200~3000之间的商品
SELECT * FROM product WHERE price >=200 AND price<= 3000;
SELECT * FROM product WHERE price >=200 && price<= 3000;
特殊比较运算符
BETWEEN…AND…
使⽤BETWEEN … AND… 可以查询出某列的值在某个范围内(包括边界值)的数据⾏。
语法:WHERE 列名 BETWEEN 值1 AND 值2;
-- BETWEEN 200 AND 3000;包含边界值
SELECT * FROM product WHERE price BETWEEN 200 AND 3000;
IN
使⽤IN运算符可以查询出某列的值是否和给定集合内的任意⼀个值相等。
语法:WHERE 列名 IN (值1, 值2, …值n)
-- 6.查询商品价格是200或800的所有商品 in(,,,)
SELECT * FROM product WHERE price = 200 OR price = 800;
SELECT * FROM product WHERE price = 200 || price = 800;
SELECT * FROM product WHERE price IN (200,800);
LIKE
使⽤LIKE运算符可以查询出某列的值是否满⾜关键字匹配,也叫模糊查询。
先介绍两个通配符:
% :⽤来匹配任何字符,进⾏模糊匹配
_ : 下划线_⽤来匹配单个字符
-- 7.like %占位符 占多位 _占位符 占一位
-- 以老字开头的
SELECT * FROM product WHERE pname LIKE '老%';
-- 以水字结尾的
SELECT * FROM product WHERE pname LIKE '%水';
-- 商品中第三个字为京字的商品
SELECT * FROM product WHERE pname LIKE '__京%';
-- 商品中包含老字的商品
SELECT * FROM product WHERE pname LIKE '%老%';
IS NULL
如果要查询某列的值是否为NULL,
使⽤ “ 列名 = NULL” 是查询不出来的。只能使⽤ “ 列名 IS NULL”
-- 8.is null 运算符
-- null和null不等,不能拿来判断
SELECT * FROM product WHERE category_id = NULL;
SELECT * FROM product WHERE category_id IS NULL;
SELECT * FROM product WHERE category_id IS NOT NULL;
least,greatest运算符
-- 9.最大值,最小值
SELECT LEAST(10,20,30);
-- 如果求最小值时有null,则直接返回null
SELECT LEAST(10,20,NULL) MIN;SELECT GREATEST(10,20,30);
-- 如果求最大值时有null,则直接返回null
SELECT GREATEST(10,20,NULL);SELECT LEAST(pid,price),pname FROM product;
⭐对查询结果排序【排序查询】
简介:
前⾯讲解的简单查询和条件查询SQL,结果集的显示顺序都不是我们指定的。在⼤多数情况下,我 们希望查询出来的结果集按照⼀定的顺序显示,⽐如按价格降序显示、在价格基础上分类排序等等。这就需要⽤到SQL语⾔中的对结果集排序的⼦句ORDER BY。
语法:
SELECT 字段名 1 ,字段名 2 , …… FROM 表名 [WHERE 条件 ] [ORDER BY 字段名 1 [ASC|DESC], 字段名 2[ASC|DESC]];
排序规则说明
1、ORDER BY⽤于⼦句中可以⽀持单个字段,多个字段,表达式,函数,别名等
2、可以按照3种⽅式进⾏排序:分别是按列名排序、按列别名排序、按列序号排序。
3、ASC表示按升序排序(默认值), DESC表示按降序排序。
-- OrderBy子句
USE db3;
-- ASC升序
SELECT *
FROM product
ORDER BY price ASC;
-- DESC降序
SELECT *
FROM product
ORDER BY price DESC;
4、可以同时按照多个列名进⾏排序
5、ORDER BY ⼦句必须写在SELECT语句的最后,LIMIT⼦句除外
6、数字升序排列⼩值在前,⼤值在后。即按照数字⼤⼩顺序由⼩到⼤排列。
7、⽇期升序排列相对较早的⽇期在前,较晚的⽇期在后。
8、字符升序排列按照字⺟由⼩到⼤的顺序排列,即由A-Z排列。
9、空值在升序排列中排在最前⾯,在降序排列中排在最后。
按列名排序
-- 进行排序,按照商品的价格来排序
-- 降序
SELECT *
FROM product
WHERE category_id IN('c001','c002')
ORDER BY price DESC;-- 默认升序ASC 可写可不写
SELECT *,price*100 AS `猫猫`
FROM product
ORDER BY 5 ASC;
按别名排序
-- 按照别名排序
SELECT *,price*100 AS 猫猫
FROM product
ORDER BY 猫猫 DESC;
按列序号排序
-- 按照列的序号来进行排序,表的第几列
SELECT *
FROM product
ORDER BY 4;
按多列排序
-- 先按照价格降序排序,价格相同按照pid排序
SELECT *
FROM product
ORDER BY price DESC,pid ASC;
⭐聚合函数
MIN,MAX,SUM,AVG,COUNT
MIN和MAX函数会忽略掉NULL值后,再进行计算
-- 字符型最大值
SELECT MAX(pname) FROM product; -- 数值型最小值
SELECT MIN(price) FROM product;-- 数值型最大值+10
SELECT MAX(price+10) FROM product;
SUM求和,AVG求平均值,它俩也会忽略掉NULL值,再进行计算
-- SUM 求和
SELECT SUM(price) FROM product WHERE category_id='c001';
-- AVG 平均值
SELECT AVG(price) FROM product WHERE category_id='c001';
COUNT 求符合条件的记录[元组]数
-- COUNT(*)包含null值
SELECT COUNT(*) FROM product;-- 21
-- COUNT(category_id)不包含null值
SELECT COUNT(category_id) FROM product;-- 20-- 去重后有多少行
SELECT COUNT(DISTINCT category_id) sumID FROM product;
聚合函数处理NULL值的小测试
CREATE TABLE test_null(c1 VARCHAR(20),c2 int
)INSERT INTO test_null VALUES('a',3);
INSERT INTO test_null VALUES('b',3);
INSERT INTO test_null VALUES('c',NULL);
INSERT INTO test_null VALUES('d',6);-- 12 3.0 6 3
SELECT SUM(c2),AVG(IFNULL(c2,0)),MAX(c2),MIN(c2)
FROM test_null;-- 4 3
SELECT COUNT(*),COUNT(c2)
FROM test_null;
⭐Group By 分组
- group by 分组,select后面只能跟分组的列以及聚合函数
SELECT category_id,COUNT(*) 总和
FROM product
WHERE category_id IN('c001','c002')
GROUP BY category_id
ORDER BY 总和 DESC;
-- 多列分组:根据商品的分类和商品的价格进行分组
SELECT category_id,price,COUNT(*)
FROM product
WHERE category_id IN('c001','c003')
GROUP BY category_id,price
Having是分组后进行筛选
-- 分组后进行筛选
-- 展示商品数>=2的商品分类和价格
SELECT category_id,price,COUNT(*) SUM
FROM product
WHERE category_id IN('c001','c003')
GROUP BY category_id,price
HAVING SUM>=2;
⭐分页查询LIMIT
语法:
SELECT * FROM product LIMIT page;
page 展示多少条数据,索引从0开始
-- 分页查询
-- 展示5条数据,索引从0开始
SELECT * FROM product LIMIT 5;
SELECT * FROM product LIMIT start,page
start 表示开始的下标:
计算公式:(第几页-1)*每页最多显示几条
page:每页最多显示几条
-- 第一个参数表示开始的元组数,第二个表示最多显示多少条
SELECT * FROM product LIMIT 5,5
⭐SQL语法顺序最终版
select [all|distinct]
<目标列的表达式1> AS [别名],
<目标列的表达式2> AS [别名]...
from <表名1或视图名1> [别名],<表名2或视图名2> [别名]...
[where <条件表达式>]
[group by <列名>]
[having <条件表达式>]
[order by <列名> [asc(从小到大排序)|desc(从大到小排序)]]
[limit <数字或列表>];
⭐执行顺序
通过FROM子句中找到需要查询的表,
通过WHERE子句进行非聚合函数筛选判断:
通过GROUP BY子句完成分组操作;
通过聚合函数完成计算操作
通过HAVING子句完成组函数筛选判断,
通过SELECT子句选择显示的列或表达式及组函数,
通过ORDER BY子句进行排序操作。
通过LIMIT子句进行分页操作
多表查询
数据准备
CREATE DATABASE db4;
USE db4;
-- 创建部门表
create table if not exists dept(deptno varchar(20) primary key , -- 部门号name varchar(20) -- 部门名字
);-- 创建员工表
create table if not exists emp(eid varchar(20) primary key , -- 员工编号ename varchar(20), -- 员工名字age int, -- 员工年龄dept_id varchar(20) -- 员工所属部门
);-- 给dept表添加数据
insert into dept values('1001','研发部');
insert into dept values('1002','销售部');
insert into dept values('1003','财务部');
insert into dept values('1004','人事部');-- 给emp表添加数据
insert into emp values('1','乔峰',20, '1001');
insert into emp values('2','段誉',21, '1001');
insert into emp values('3','虚竹',23, '1001');
insert into emp values('4','阿紫',18, '1001');
insert into emp values('5','扫地僧',85, '1002');
insert into emp values('6','李秋水',33, '1002');
insert into emp values('7','鸠摩智',50, '1002');
insert into emp values('8','天山童姥',60, '1003');
insert into emp values('9','慕容博',58, '1003');
--注意,此处部门表中没有存在1005对应的部门,当然部门1004在此出没有员工,
--只是为了演示效果
insert into emp values('10','丁春秋',71, '1005');SELECT * FROM emp;
SELECT * FROM dept;
一对一 一般需要表合并
一对多
多对多 需要从表
内连接
多张表联查时符合关系条件相等的数据叫内连接,返回符合条件的公有数据。
笛卡尔积
-- 笛卡尔积 A表m条,B表n条,查询结果为m*n条
SELECT *
FROM emp,dept;
隐藏内连接
-- 隐式内连接(SQL92标准)
-- SELECT 字段列表 FROM 表1,表2... WHERE 条件;
-- 隐式内连接
SELECT *
FROM emp,dept
WHERE dept.deptno=emp.dept_id;
显式内连接
-- 显式内连接(SQL99标准) []包括的可以省略不写
-- SELECT 字段列表 FROM 表1 [INNER] JOIN 表2 ON 条件;
-- 显式内连接
SELECT *
FROM emp INNER join dept
ON dept.deptno=emp.dept_id;
内连接应用:
-- 查询在研发部的员工信息
SELECT *
FROM emp,dept
WHERE dept.deptno=emp.dept_id AND dept.NAME='研发部';SELECT *
FROM emp INNER join dept
ON dept.deptno=emp.dept_id AND dept.NAME='研发部';-- 查询每个部门的员工数,并升序
SELECT NAME,COUNT(*) AS con
FROM emp,dept
WHERE emp.dept_id=dept.deptno
GROUP BY deptno,NAME
ORDER BY con;-- 查询人数大于3的部门,并按照人数进行降序
SELECT NAME,COUNT(*) AS con
FROM emp,dept
WHERE emp.dept_id=dept.deptno
GROUP BY deptno,NAME
HAVING con>=3
ORDER BY con DESC;
外连接
外连接,返回主查表中的所有数据,从表返回符合条件的数据。
主查表在join的左边,left
主查表在join的右边,right
数据准备
DROP DATABASE IF EXISTS TEST;
CREATE DATABASE IF NOT EXISTS TEST;
USE TEST;
DROP TABLE EMP;
DROP TABLE DEPT;
DROP TABLE SALGRADE;
CREATE TABLE DEPT
(deptno integer(4) not null,dname varchar(14),loc varchar(13)
);ALTER TABLE dept ADD CONSTRAINT pk_dept PRIMARY KEY(deptno);
CREATE TABLE EMP
(empno integer(4) not null,ename varchar(10),job varchar(9),mgr integer(4),hiredate date,sal decimal(7,2),comm decimal(7,2),deptno integer(2)
);
ALTER TABLE EMP ADD CONSTRAINT pk_emp PRIMARY KEY (empno);
ALTER TABLE EMP ADD CONSTRAINT fk_deptno FOREIGN KEY (deptno) REFERENCES DEPT (deptno);
CREATE TABLE SALGRADE
(grade integer(1),losal decimal(7,2),hisal decimal(7,2)
);
INSERT INTO DEPT (DEPTNO, DNAME, LOC) VALUES (10, 'ACCOUNTING', 'NEW YORK');
INSERT INTO DEPT (DEPTNO, DNAME, LOC) VALUES (20, 'RESEARCH', 'DALLAS');
INSERT INTO DEPT (DEPTNO, DNAME, LOC) VALUES (30, 'SALES', 'CHICAGO');
INSERT INTO DEPT (DEPTNO, DNAME, LOC) VALUES (40, 'OPERATIONS', 'BOSTON');
INSERT INTO SALGRADE (GRADE, LOSAL, HISAL) VALUES (1, 700, 1200);
INSERT INTO SALGRADE (GRADE, LOSAL, HISAL) VALUES (2, 1201, 1400);
INSERT INTO SALGRADE (GRADE, LOSAL, HISAL) VALUES (3, 1401, 2000);
INSERT INTO SALGRADE (GRADE, LOSAL, HISAL) VALUES (4, 2001, 3000);
INSERT INTO SALGRADE (GRADE, LOSAL, HISAL) VALUES (5, 3001, 9999);
/* 下面要特别注意,%d-%m-%Y 里的d和m一定要小写 */
INSERT INTO EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
VALUES (7369, 'SMITH', 'CLERK', 7902, STR_TO_DATE('17-12-1980', '%d-%m-%Y'), 800.00, null, 20);
INSERT INTO EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
VALUES (7499, 'ALLEN', 'SALESMAN', 7698, STR_TO_DATE('20-02-1981', '%d-%m-%Y'), 1600.00, 300.00, 30);
INSERT INTO EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
VALUES (7521, 'WARD', 'SALESMAN', 7698, STR_TO_DATE('22-02-1981', '%d-%m-%Y'), 1250.00, 500.00, 30);
INSERT INTO EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
VALUES (7566, 'JONES', 'MANAGER', 7839, STR_TO_DATE('02-04-1981', '%d-%m-%Y'), 2975.00, null, 20);
INSERT INTO EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
VALUES (7654, 'MARTIN', 'SALESMAN', 7698, STR_TO_DATE('28-09-1981', '%d-%m-%Y'), 1250.00, 1400.00, 30);
INSERT INTO EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
VALUES (7698, 'BLAKE', 'MANAGER', 7839, STR_TO_DATE('01-05-1981', '%d-%m-%Y'), 2850.00, null, 30);
INSERT INTO EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
VALUES (7782, 'CLARK', 'MANAGER', 7839, STR_TO_DATE('09-06-1981', '%d-%m-%Y'), 2450.00, null, 10);
INSERT INTO EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
VALUES (7788, 'SCOTT', 'ANALYST', 7566, STR_TO_DATE('19-04-1987', '%d-%m-%Y'), 3000.00, null, 20);
INSERT INTO EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
VALUES (7839, 'KING', 'PRESIDENT', null, STR_TO_DATE('17-11-1981', '%d-%m-%Y'), 5000.00, null, 10);
INSERT INTO EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
VALUES (7844, 'TURNER', 'SALESMAN', 7698, STR_TO_DATE('08-09-1981', '%d-%m-%Y'), 1500.00, 0.00, 30);
INSERT INTO EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
VALUES (7876, 'ADAMS', 'CLERK', 7788, STR_TO_DATE('23-05-1987', '%d-%m-%Y'), 1100.00, null, 20);
INSERT INTO EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
VALUES (7900, 'JAMES', 'CLERK', 7698, STR_TO_DATE('03-12-1981', '%d-%m-%Y'), 950.00, null, 30);
INSERT INTO EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
VALUES (7902, 'FORD', 'ANALYST', 7566, STR_TO_DATE('03-12-1981', '%d-%m-%Y'), 3000.00, null, 20);
INSERT INTO EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
VALUES (7934, 'MILLER', 'CLERK', 7782, STR_TO_DATE('23-01-1982', '%d-%m-%Y'), 1300.00, null, 10);
左外连接
左外连接
SELECT 字段 FROM 表1 LEFT [OUTER] JOIN 表2 ON 连接条件
SELECT *
FROM emp e RIGHT OUTER JOIN dept d ON
e.dept_id = d.deptno;SELECT e.eid,e.ename,e.age,e.dept_id,d.deptno,d.name
FROM dept d RIGHT JOIN emp e ON e.dept_id=d.deptno;
右外连接
右外连接
SELECT 字段 FROM 表1 RIGHT [OUTER] JOIN 表2 ON 连接条件
SELECT *
FROM emp e RIGHT OUTER JOIN dept d ON
e.dept_id = d.deptno;SELECT e.eid,e.ename,e.age,e.dept_id,d.deptno,d.name
FROM dept d RIGHT JOIN emp e ON e.dept_id=d.deptno;
满外连接
全外连接,满外连接
UNION ALL 不去重
UNION 去重的是交集部分
SELECT e.eid,e.ename,e.age,e.dept_id,d.deptno,d.name
FROM dept d LEFT JOIN emp e ON
e.dept_id=d.deptno
UNION ALL
SELECT e.eid,e.ename,e.age,e.dept_id,d.deptno,d.name
FROM dept d RIGHT JOIN emp e ON
e.dept_id=d.deptno;SELECT e.eid,e.ename,e.age,e.dept_id,d.deptno,d.name
FROM dept d LEFT JOIN emp e ON
e.dept_id=d.deptno
UNION
SELECT e.eid,e.ename,e.age,e.dept_id,d.deptno,d.name
FROM dept d RIGHT JOIN emp e ON
e.dept_id=d.deptno;
嵌套查询(子查询)
基本概念:一个查询语句(外部查询)中包含了另一个查询语句(子查询)
select 列名1,列名2....
from 表1,表2
where 条件 条件表达式(子查询语句)
子查询被当作一个单元来执行,它先于外部查询执行,其结果可以被外部查询使用。
子查询的结果通常被看作是一个临时表,外部查询可以像操作任何其他表一样操作这个临时结果集。
1.单行单列
1.查询年龄最大的员工信息,显示信息包含员工号、员工名字、员工年龄
-- step1.找出年龄最大的年龄
USE db4;
SELECT MAX(age) FROM emp;
-- step2.和emp表进行再次匹配,从这张表找年龄为
SELECT * FROM emp WHERE age=85;
-- 子查询合并
SELECT * FROM emp WHERE age=(SELECT MAX(age) FROM emp);
2.多行单列
2.查询研发部和销售部的员工信息,包含员工号、员工名字
-- 关联查询
SELECT * FROM emp,dept
WHERE dept.`name` IN ('研发部','销售部')
AND emp.dept_id = dept.deptno;-- 多行单列查询
SELECT * FROM emp
WHERE emp.dept_id IN(SELECT deptno FROM deptWHERE NAME IN('研发部','销售部')
);
3.多行多列
-- 3.查询研发部85岁以下的员工信息,包括员工号、员工名字、部门名字
SELECT * FROM emp,dept
WHERE emp.age AND dept.`name`='研发部' AND
emp.dept_id=dept.deptno;-- step1 找研发部的信息
-- step2 找85岁以下的员工
SELECT * FROM
(SELECT * FROM dept WHERE dept.name='研发部')t1,
(SELECT * FROM emp WHERE age <85)t2
WHERE t1.deptno=t2.dept_id;
子查询关键字
ALL
所有 相当于and
-- 子查询关键字
-- ALL 所有 相当于and
-- 查询年龄大于'1003'部门的所有年龄的员工信息-- 写法1
-- step1:查找1003部门年龄最大的
SELECT MAX(age)
FROM emp
WHERE dept_id = '1003';
-- step2:嵌套查询
SELECT *
FROM emp
WHERE age>(SELECT MAX(age)FROM empWHERE dept_id = '1003'
);-- 写法2:使用ALL关键字
SELECT *
FROM emp
WHERE age> ALL(SELECT ageFROM empWHERE dept_id = '1003'
);
ANY
任意 相当于or
-- 查询年龄大于'1003'任意一个员工年龄的员工信息
-- 写法1
-- step1:查找1003部门年龄最小的
SELECT MIN(age)
FROM emp
WHERE dept_id = '1003';
-- step2:嵌套查询
SELECT *
FROM emp
WHERE age>(SELECT MIN(age)FROM empWHERE dept_id = '1003'
);-- 写法2:使用ANY关键字
SELECT *
FROM emp
WHERE age> ANY(SELECT ageFROM empWHERE dept_id = '1003'
);
IN
在SQL语言中,IN
是一个运算符,用于测试一个值是否在一个列表(一组值)中。它通常用在WHERE
子句中,以过滤出满足特定条件的记录。
当你看到这样的查询语句:
SELECT * FROM table WHERE column IN (value1, value2, value3, ...);
这个查询会返回所有column
列的值存在于括号内列表(value1, value2, value3等)的记录。
举例来说,如果你有一个orders
表,其中有一个customer_id
列,你可以使用IN
来找出特定顾客ID列表的所有订单:
SELECT * FROM orders WHERE customer_id IN (101, 102, 103);
这个查询将返回customer_id
为101,102或103的所有订单。
在你提供的查询中:
SELECT *
FROM emp
WHERE dept_id IN (SELECT deptno FROM dept
) AND dept_id > 1001;
这里IN
用于检查emp
表中的dept_id
是否存在于由子查询返回的部门编号列表中。如果存在,并且dept_id
大于1001,那么相应的员工记录就会被选中并返回。
-- 查询所有部门信息真实存在的员工
SELECT *
FROM emp
WHERE dept_id IN(SELECT deptno FROM dept
) AND dept_id > 1001;
EXISTS
把外层sql的结果,拿到内层sql去测试,如果内层的sql成立,则该行取出。内层查询是exists后的查询。
至少存在员工的部门
-- 至少存在员工的部门
SELECT *
FROM dept
WHERE EXISTS(SELECT * FROM empWHERE dept.deptno = emp.dept_id
);SELECT *
FROM dept
WHERE deptno IN(SELECT DISTINCT dept_id FROM emp
);
自关联查询
自己和自己做内连接或者外连接,用于显示自关联的层级关系
-- 6. 显示员工KING和FORD管理的下属员工的员工姓名。
-- step1 查询KING和FORD
-- step2 根据mgr查询姓名
SELECT empno FROM emp WHERE ename IN ('KING','FORD');SELECT empno FROM emp WHERE mgr IN(7902,7839);SELECT ename FROM emp WHERE mgr IN(SELECT empno FROM emp WHERE ename IN ('KING','FORD')
);-- t1员工
SELECT *
FROM emp t1 INNER JOIN emp t2
ON t1.mgr=t2.empno AND (t2.ename IN ('KING','FORD'));
数据准备
-- db4的t_sanguo表
-- 创建表,并建立自关联约束
-- 添加自关联约束
CREATE TABLE t_sanguo(eid INT PRIMARY KEY,ename VARCHAR(20),manager_id INT,FOREIGN KEY (manager_id) REFERENCES t_sanguo(eid)
);insert into t_sanguo values(1,'刘协',NULL);
insert into t_sanguo values(2,'刘备',1);
insert into t_sanguo values(3,'关羽',2);
insert into t_sanguo values(4,'张飞',2);
insert into t_sanguo values(5,'曹操',1);
insert into t_sanguo values(6,'许褚',5);
insert into t_sanguo values(7,'典韦',5);
insert into t_sanguo values(8,'孙权',1);
insert into t_sanguo values(9,'周瑜',8);
insert into t_sanguo values(10,'鲁肃',8);
SELECT * FROM t_sanguo;
-- 查询每个三国人物的上级
SELECT *
FROM t_sanguo t1 LEFT JOIN t_sanguo t2
ON t1.manager_id = t2.eid;-- 查询每个三国人物的上级的上级
SELECT *
FROM t_sanguo t1 LEFT JOIN t_sanguo t2
ON t1.manager_id = t2.eid
LEFT JOIN t_sanguo t3
ON t2.manager_id = t3.eid;
DQL总练习题
-- DQL总练习题
-- 2024-08-10 14:30
CREATE DATABASE mydb3;
USE mydb3;
--学生表 Student(sno-学号,sname-姓名,ssex-性别,sage-年龄,sdept-所在系)
--课程表Course(con-课程号,cname-课程名称,cpno-先行课,ccredit-学分)
--选课表sc(sno-学号,cno-课程号,grade-成绩)CREATE TABLE Student
(Sno CHAR(9) PRIMARY KEY, /* 列级完整性约束条件,Sno是主码*/ Sname CHAR(20) UNIQUE, /* Sname取唯一值*/Ssex CHAR(2),Sage SMALLINT,Sdept CHAR(20)
); CREATE TABLE Course(Cno VARCHAR(4) PRIMARY KEY, /*列级完整性约束条件,Cno为主码*/Cname CHAR(40) NOT NULL, /*列级完整性约束条件,Cname不能取空值*/Cpno CHAR(4), /*Cpno的含义为先修课*/ Ccredit SMALLINT
);CREATE TABLE SC(Sno CHAR(9), Cno CHAR(4), Grade SMALLINT,PRIMARY KEY (Sno,Cno),/* 主码由两个属性构成,必须作为表级完整性进行定义*/FOREIGN KEY (Sno) REFERENCES Student(Sno),/* 表级完整性约束条件,Sno是外码,被参照表是Student */FOREIGN KEY (Cno)REFERENCES Course(Cno)/* 表级完整性约束条件, Cno是外码,被参照表是Course*/
); INSERT INTO Student VALUES('202415121','李勇','男',20,'CS'),
('202415122','刘晨','女',19,'CS'),
('202415123','王敏','女',18,'MA'),
('202415125','张立','男',19,'IS')INSERT INTO Student VALUES('202415124','猫猫','男',20,'LS');
INSERT INTO Student VALUES('202415126','欧阳疯子','女',20,'LS');
INSERT INTO Student VALUES('202415127','欧阳封','女',20,'LS');INSERT INTO Course VALUES('1','数据库','5',4),
('2','数学',NULL,2),
('3','信息系统','1',4),
('4','操作系统','6',3),
('5','数据结构','7',4),
('6','数据处理',NULL,2),
('7','java语言','6',4)INSERT INTO SC VALUES('202415121','1',92),
('202415121','2',85),
('202415121','3',88),
('202415122','2',90),
('202415122','3',80)SELECT * FROM course;
SELECT * FROM sc;
SELECT * FROM student;
-- 练习1~30 2024-08-10 14:32
-- 1. 查询全体学生的学号与姓名。
SELECT sno,sname FROM student;
-- 2.查询全体学生的姓名、学号、所在系
SELECT sname,sno,sdept FROM student;
-- 3.查询全体学生的详细记录。
SELECT * FROM student;SELECT * FROM student
LEFT JOIN sc on student.Sno =sc.Sno
LEFT JOIN course ON sc.Cno=course.Cno;
-- 4.查询全体学生的姓名及其出生年份。
SELECT sname,'2024-08-10'-sage AS 出生年份 FROM student;
-- 5.查询全体学生的姓名、出生年份和所在的院系,要求用小写字母表示系名。
SELECT sname,'2024'-sage AS 出生年份,LOWER(sdept) FROM student;
-- 6.查询选修了课程的学生学号。
SELECT sno FROM sc
GROUP BY sno;
-- 7.查询计算机科学系全体学生的名单(cs计算机系)。
SELECT * FROM student WHERE sdept='CS';SELECT * FROM student
LEFT JOIN sc on student.Sno =sc.Sno
LEFT JOIN course ON sc.Cno=course.Cno
WHERE student.Sdept='CS';
-- 8.查询所有年龄在20岁以下的学生姓名及其年龄。
SELECT sname,sage
FROM student
WHERE sage < 20;
-- 9.查询考试成绩有不及格的学生的学号。
SELECT sno FROM sc WHERE grade<90;
-- 10. 查询年龄在20~23岁(包括20岁和23岁)之间的学生的姓名、系别和年龄
SELECT sname,sdept,sage
FROM student
WHERE sage BETWEEN 20 AND 23;-- 11.查询年龄不在20~23岁之间的学生姓名、系别和年龄
SELECT sname,sdept,sage
FROM student
WHERE sage NOT BETWEEN 20 AND 23;
-- 12. 查询计算机科学系(CS)、数学系(MA)和信息系(IS)学生的姓名和性别。
SELECT sname,ssex
FROM student
WHERE sdept IN('CS','MA','IS');
-- 13查询既不是计算机科学系、数学系,也不是信息系的学生的姓名和性别。
SELECT sname,ssex
FROM student
WHERE sdept NOT IN('CS','MA','IS');
-- 14 查询所有姓刘学生的姓名、学号和性别。
SELECT sname,sno,ssex
FROM student
WHERE sname LIKE '刘%';
-- 15查询姓"欧阳"且全名为三个汉字的学生的姓名。
SELECT sname
FROM student
WHERE sname LIKE '欧阳_';
-- 16 查询名字中第2个字为"阳"字的学生的姓名和学号。
SELECT sname,sno
FROM student
WHERE sname LIKE '_阳%';
-- 17.查询所有不姓刘的学生姓名、学号和性别。
SELECT sname,sno,ssex
FROM student
WHERE sname NOT LIKE '刘%';
-- 18某些学生选修课程后没有参加考试,所以有选课记录,但没有考试成绩。
-- 查询缺少成绩的学生的学号和相应的课程号。
SELECT sno,cno
FROM sc
WHERE grade IS NULL;
-- 19.查询所有有成绩的学生学号和课程号。
SELECT sno,cno
FROM sc
WHERE grade IS NOT NULL;
-- 20 查询计算机系年龄在20岁以下的学生姓名。
SELECT sname
FROM student
WHERE sage<20 AND sdept = 'CS';-- 21 查询计算机科学系(CS)、数学系(MA)和信息系(IS)学生的姓名和性别。
SELECT sname,ssex
FROM student
WHERE sdept IN ('CS','MA','IS');
-- 22 查询选修了3号课程的学生的学号及其成绩,查询结果按分数降序排列。
SELECT sno,grade FROM sc
WHERE cno = 3
ORDER BY grade DESC;
-- 23查询全体学生情况,查询结果按所在系的系号升序排列,同一系中的学生按年龄降序排列。
SELECT * FROM student
ORDER BY sdept ASC,sage DESC;
-- 24 查询学生总人数。
SELECT COUNT(sno) FROM student;
-- 25 查询选修了课程的学生人数。
SELECT COUNT(DISTINCT sno) FROM sc;
-- 26 计算1号课程的学生平均成绩。
SELECT AVG(grade) FROM sc WHERE cno =1;
-- 27 查询选修1号课程的学生最高分数。
SELECT MAX(grade) FROM sc WHERE cno =1;
-- 28 查询学生202415121选修课程的总学分数。
SELECT SUM(ccredit)
FROM course
WHERE cno IN (SELECT cno FROM sc WHERE sno='202415121'
);SELECT SUM(ccredit)
FROM sc s LEFT JOIN course c ON
c.Cno=s.Cno AND sno='202415121';
-- 29 求各个课程号及相应的选课人数。
SELECT c.Cno, COUNT(DISTINCT s.sno) AS '选课人数'
FROM course c LEFT JOIN sc s ON
c.Cno = s.Cno
GROUP BY c.Cno;
-- 30 查询选修了3门以上课程的学生学号。
SELECT sno,COUNT(cno) AS num
FROM sc
GROUP BY sno
HAVING num >= 3;-- 32.查询每个学生及其选修课程的情况
SELECT *
FROM student LEFT JOIN sc ON
student.Sno=sc.Sno
LEFT JOIN course ON
course.Cno=sc.Cno;
-- 33.查询每个学生及其选修课程的情况,使用自然连接。[有问题,没做出来]
SELECT *
FROM student NATURAL JOIN sc ON
student.Sno = sc.Sno
NATURAL JOIN course ON
sc.Cno = course.Cno;-- 34.查询选修2号课程且成绩在90分以上的所有学生的学号和姓名。
SELECT sc.Sno,sname
FROM sc INNER JOIN student ON
sc.Sno = student.Sno
WHERE cno = 2 AND grade >=90;
-- 35.查询每一门课的间接先修课(即先修课的先修课)。
SELECT *
FROM course c1 LEFT JOIN course c2 ON c1.Cpno=c2.Cno
LEFT JOIN course c3 ON c2.Cpno=c3.Cno;
-- 36.查询每个学生及其选修课程的情况,改写(有问题,需注意)。
SELECT student.Sno, student.Sname, course.Cno, course.Cname
FROM student LEFT JOIN sc ON student.Sno = sc.Sno
LEFT JOIN course ON sc.Cno = course.Cno;-- 37 查询每个学生的学号、姓名、选修的课程名及成绩。
SELECT student.Sno, student.Sname, course.Cname,sc.Grade
FROM student LEFT JOIN sc ON student.Sno = sc.Sno
LEFT JOIN course ON sc.Cno = course.Cno;
-- 38.查询与“刘晨”在同一个系学习的学生。
SELECT *
FROM student WHERE sdept = (SELECT sdept FROM student WHERE sname='刘晨'
) AND sname !='刘晨';
-- 39.查询选修了课程名为“信息系统”的学生学号和姓名。
SELECT student.Sno,student.Sname
FROM student LEFT JOIN sc ON
student.Sno = sc.Sno
LEFT JOIN course ON
sc.Cno = course.Cno
WHERE cname = '信息系统';
-- 40.找出每个学生不小于他选修课程平均成绩的课程号。
SELECT *
FROM student LEFT JOIN sc ON student.Sno = sc.Sno
LEFT JOIN course ON sc.Cno = course.Cno
WHERE sc.grade >= (SELECT AVG(sc2.grade)FROM sc sc2WHERE sc2.Sno = student.Sno
);SELECT *
FROM sc
WHERE sc.Grade >= (SELECT AVG(sc2.Grade)FROM sc sc2WHERE sc2.Sno = sc.Sno
);-- 41.查询非计算机科学系中比计算机科学系任意一个学生年龄小的学生姓名和年龄。
SELECT s1.Sname, s1.Sage
FROM student s1
WHERE s1.Sdept != 'CS' AND s1.Sage < (SELECT MIN(sage)FROM student s2WHERE s2.Sdept = 'CS'
);-- 42. 查询非计算机科学系中比计算机科学系所有学生年龄都小的学生姓名及年龄。
SELECT s1.Sname, s1.Sage
FROM student s1
WHERE s1.Sdept != 'CS' AND s1.Sage < ALL (SELECT s2.SageFROM student s2WHERE s2.Sdept = 'CS'
);
-- 43.查询所有选修了1号课程的学生姓名。
SELECT DISTINCT student.Sname
FROM student
JOIN sc ON student.Sno = sc.Sno
WHERE sc.Cno = '1';
-- 44. 查询没有选修1号课程的学生姓名。
SELECT sname
FROM student
WHERE sno NOT IN (SELECT snoFROM scWHERE cno = '1'
);-- 45.查询与“刘晨”在同一个系学习的学生。可以用带EXISTS谓词的子查询替换。
SELECT s1.Sname
FROM student s1
WHERE s1.Sdept = (SELECT s2.SdeptFROM student s2WHERE s2.Sname = '刘晨'
) AND s1.Sname != '刘晨';
DQL难题
CREATE DATABASE mydb4;
USE mydb4;
-- 学生表:Student(编号sid,姓名sname,生日birthday,性别ssex,班级 classid)
-- 课程表:Course(课程编号cid,课程名称cname,教师编号tid)
-- 成绩表:Sc(学生编号sid,课程编号cid,成绩score)
-- 教师表:Teacher(教师编号tid,姓名tname)
-- 班级表:Class (班级编号 classid,班级名称 classname)-- 学生表 Student
create table student(Sid int primary key, Sname varchar(10), birthday datetime, Ssex varchar(10), classid int);
insert into Student values('1' , '赵雷' , '1990-01-01' , '男', '1');
insert into Student values('2' , '钱电' , '1990-12-21' , '男', '2');
insert into Student values('3' , '孙风' , '1990-05-20' , '男', '1');
insert into Student values('4' , '李云' , '1990-08-06' , '男', '2');
insert into Student values('5' , '周梅' , '1991-12-01' , '女', '1');
insert into Student values('6' , '吴兰' , '1992-03-01' , '女', '2');
insert into Student values('7' , '郑竹' , '1989-07-01' , '女', '1');
insert into Student values('8' , '王菊' , '1990-01-20' , '女', '2');-- 成绩表 SC
create table SC(Sid int, Cid int, score decimal(18,1));
insert into SC values('1' , '1' , 80);
insert into SC values('1' , '2' , 90);
insert into SC values('1' , '3' , 99);
insert into SC values('2' , '1' , 70);
insert into SC values('2' , '2' , 60);
insert into SC values('2' , '3' , 80);
insert into SC values('3' , '1' , 80);
insert into SC values('3' , '2' , 80);
insert into SC values('3' , '3' , 80);
insert into SC values('4' , '1' , 50);
insert into SC values('4' , '2' , 30);
insert into SC values('4' , '3' , 20);
insert into SC values('5' , '1' , 76);
insert into SC values('5' , '2' , 87);
insert into SC values('6' , '1' , 31);
insert into SC values('6' , '3' , 34);
insert into SC values('7' , '2' , 89);
insert into SC values('7' , '3' , 98);-- 课程表 Course
create table Course(Cid int primary key,Cname varchar(10),Tid varchar(10));
insert into Course values('1' , '语文' , '2');
insert into Course values('2' , '数学' , '1');
insert into Course values('3' , '英语' , '3');-- 教师表 Teacher
create table teacher1(Tid int primary key auto_increment,Tname varchar(10),Tsex TINYINT default 1,Tbirthday date,Taddress varchar(255),Temail varchar(255),Tmoney DECIMAL(20,2)
);
insert into Teacher values('1' , '张三',1,'1988-1-15','陕西咸阳','zhangsan@qq.com',3000.00);
insert into Teacher values('2' , '李四',0,'1992-5-9','陕西宝鸡','lisi@qq.com',4000.00);
insert into Teacher values('3' , '王五',1,'1977-7-1','山西太原','wangwu@qq.com',5000.00);-- 班级表 Class
create table Class(classid int primary key, classname varchar(20));
insert into Class values('1', '一班');
insert into Class values('2', '二班');SELECT * FROM class;
SELECT * FROM course;
SELECT * FROM sc;
SELECT * FROM student;
SELECT * FROM teacher;
-- 1. 查询” 01 “课程比” 02 “课程成绩高的学生的信息及课程分数
SELECT * FROM student
INNER JOIN
(SELECT * FROM sc WHERE cid = 1)t1 ON student.sid = t1.sid
INNER JOIN
(SELECT * FROM sc WHERE cid = 2)t2 ON student.sid = t2.sid
WHERE t1.score > t2.score;SELECT s.*,t1.score
FROM student s,(SELECT sid,cid,score FROM sc WHERE cid = 1)t1,(SELECT sid,cid,score FROM sc WHERE cid = 2)t2
WHERE s.sid=t1.sid AND t1.sid=t2.sid AND t1.score>t2.score;
-- 答案1:
SELECT s.*,t1.score,t2.score
FROM student s,(SELECT sid,cid,score FROM sc WHERE cid = 1)t1,(SELECT sid,cid,score FROM sc WHERE cid = 2)t2
WHERE s.sid=t1.sid AND t1.sid=t2.sid AND t1.score>t2.score;
-- 答案2:
SELECT s.*,t1.score,t2.score
FROM student s,sc t1,sc t2
WHERE s.Sid=t1.Sid AND t1.Sid=t2.Sid AND (t1.Cid=1 AND t2.Cid=2)AND t1.score>t2.score;
-- 2. 查询平均成绩大于等于 60 分的同学的学生编号和学生姓名和平均成绩
SELECT sc.sid,sname,AVG(score) AS num
FROM sc JOIN student ON sc.sid=student.sid
GROUP BY sc.sid
HAVING num>=60;-- 答案1
SELECT *
FROM student s,(SELECT sid,AVG(score) avg_scoreFROM sc GROUP BY sid) t2
WHERE s.sid=t2.sid AND t2.avg_score>=60; -- 答案2
SELECT student.`*`,AVG(score) avg_score
FROM student LEFT JOIN sc ON student.Sid=sc.Sid
GROUP BY student.Sid
HAVING avg_score>=60;-- 3. 查询在 SC 表存在成绩的学生信息
SELECT student.*
FROM sc LEFT JOIN student
ON sc.Sid=student.Sid
GROUP BY sc.Sid;-- 答案
SELECT *
FROM student
WHERE sid IN (SELECT DISTINCT sid from sc WHERE score IS NOT NULL);
-- 4. 查询所有同学的学生编号、学生姓名、选课总数、所有课程的总成绩(没成绩的显示为 null )
SELECT student.sid,sname,COUNT(cid),SUM(score)
FROM student LEFT JOIN sc
ON student.sid = sc.sid
GROUP BY sid;-- 答案
SELECT *
FROM student
LEFT JOIN (SELECT sid,COUNT(*),SUM(score) FROM sc GROUP BY sid) t1
ON student.sid = t1.sid;
-- 5. 查询「李」姓老师的数量
SELECT COUNT(*)
FROM teacher
WHERE tname LIKE '李%';
-- 6. 查询学过「张三」老师授课的同学的信息
-- step1:查询张三老师教的课
SELECT tid FROM teacher WHERE tname='张三';-- step2:查询课程表,老师带的哪个班
SELECT cid FROM course WHERE tid=1;-- step3:查询cid为2的sid
SELECT sid
FROM sc
WHERE cid=2;-- step4:查询sid对应的学生信息
SELECT *
FROM student,sc
WHERE student.Sid=sc.Sid AND cid=2-- 总的
SELECT *
FROM student,sc
WHERE student.Sid=sc.Sid AND
cid=(SELECT cid FROM course WHERE tid=(SELECT tid FROM teacher WHERE tname='张三')
);-- 答案
SELECT *
FROM student
WHERE EXISTS (SELECT * FROM scWHERE cid IN (SELECT cidFROM teacher t1,course t2WHERE tname = '张三' AND t1.tid = t2.tid) AND student.sid = sc.sid
)
-- 7. 查询没有学全所有课程的同学的信息
SELECT student.*,COUNT(*) course_num
FROM student
LEFT JOIN sc ON student.sid = sc.Sid
GROUP BY student.sid
HAVING course_num<(select COUNT(*) FROM course
);-- 答案
SELECT student.*
FROM student
LEFT JOIN sc ON student.sid = sc.sid
GROUP BY student.sid
HAVING count(cid) < (SELECT count(*) FROM course);-- 8. 查询至少有一门课与学号为” 01 “的同学所学相同的同学的信息
-- 赵雷学了科目内容1 2 3
-- 至少1,2,3 1,2 1,2,4 1,4
SELECT student.*
FROM student INNER JOIN sc ON student.Sid=sc.Sid
WHERE student.Sid NOT IN(SELECT sid FROM sc WHERE cid NOT IN(SELECT cid FROM sc WHERE sid=1)
)
GROUP BY student.Sid;-- 答案
SELECT DISTINCT student.*
FROM student,(SELECT *
FROM sc
WHERE cid IN(SELECT cidFROM sc WHERE sid = 7))t1
WHERE student.sid = t1.sid
-- 9. 查询和” 01 “号的同学学习的课程完全相同的其他同学的信息
-- step1:查询01号同学学习的课程
SELECT cid FROM sc WHERE sid =1
-- step2:查询学习1,2,3课程的人的名字
SELECT s.*,t1.*,t2.*
FROM student s,
(SELECT sid,cid,score FROM sc WHERE cid=1)t1,
(SELECT sid,cid,score FROM sc WHERE cid=2)t2,
(SELECT sid,cid,score FROM sc WHERE cid=3)t3
WHERE s.Sid=t1.sid AND t1.sid=t2.sid AND t2.sid = t3.sid-- 总的【错误的,查询不是动态的】
SELECT s.*
FROM student s,(SELECT sid, cid, score FROM sc WHERE cid = 1) t1,(SELECT sid, cid, score FROM sc WHERE cid = 2) t2,(SELECT sid, cid, score FROM sc WHERE cid = 3) t3
WHERE s.sid = t1.sidAND t1.sid = t2.sidAND t2.sid = t3.sid;-- 答案:
-- step1:内连接
SELECT *
FROM student
INNER JOIN sc ON student.sid = sc.sid
-- step2:查询课程在都01的学生选课的信息中的学生
SELECT student.sid,student.Sname,COUNT(*)
FROM student
INNER JOIN sc ON student.sid = sc.sid
WHERE
student.sid NOT IN(SELECT sidFROM scWHERE cid NOT IN(SELECT cid FROM sc WHERE sid='01')
)
GROUP BY student.sid,student.sname
HAVING COUNT(cid) = (SELECT COUNT(*) FROM sc WHERE sid=1);-- 10. 查询没学过”张三”老师讲授的任一门课程的学生姓名
SELECT Sname
FROM student
WHERE Sid NOT IN(SELECT DISTINCT Sid FROM sc WHERE Cid=(SELECT Cid FROM course WHERE Tid=(SELECT Tid FROM teacher WHERE Tname='张三'))
);-- 答案
SELECT *
FROM student
WHERE NOT EXISTS (SELECT * FROM scWHERE cid IN (SELECT cidFROM teacher t1,course t2WHERE tname = '张三' AND t1.tid = t2.tid) AND student.sid = sc.sid
);
-- 11. 查询两门及其以上不及格课程的同学的学号,姓名及其平均成绩
SELECT sc.Sid,sname,AVG(score)
FROM sc,student
WHERE score<60 AND sc.Sid=student.Sid
GROUP BY sc.Sid
HAVING COUNT(*)>=2;-- 12. 检索” 01 “课程分数小于 60,按分数降序排列的学生信息
SELECT student.*
FROM student
INNER JOIN sc ON student.Sid=sc.Sid
WHERE score < 60 AND cid = 1
ORDER BY score DESC;-- 答案
SELECT *
FROM student,sc
WHERE student.sid = sc.sid AND cid = '01' AND sc.score<60
ORDER BY score DESC;
-- 13. 查询各科成绩最高分、最低分和平均分,以如下形式显示:
-- 课程 ID,课程 name,最高分,最低分,平均分,
-- 及格率,中等率,优良率,优秀率(及格为>=60,中等为:70-80,优良为:80-90,优秀为:>=90)。
-- 方式1
SELECT sc.Cid AS '课程ID',cname '课程',
MAX(score) AS '最高分',
MIN(score) AS '最低分',
AVG(score) AS '平均分',
((SELECT COUNT(sid) FROM sc WHERE score>=60 AND sc.Cid=course.Cid)/(SELECT COUNT(sid) FROM sc WHERE sc.Cid=course.Cid)
)AS '及格率',
((SELECT COUNT(sid) FROM sc WHERE score>=70 AND score<80 AND sc.Cid=course.Cid)/(SELECT COUNT(sid) FROM sc WHERE sc.Cid=course.Cid)
)AS '中等率',
((SELECT COUNT(sid) FROM sc WHERE score>=80 AND score<90 AND sc.Cid=course.Cid)/(SELECT COUNT(sid) FROM sc WHERE sc.Cid=course.Cid)
)AS '优良率',
((SELECT COUNT(sid) FROM sc WHERE score>=90 AND sc.Cid=course.Cid)/(SELECT COUNT(sid) FROM sc WHERE sc.Cid=course.Cid)
)AS '优秀率'
FROM sc
INNER JOIN course ON sc.cid = course.cid
GROUP BY course.Cid;-- 方式2
SELECT sc.Cid AS '课程ID',course.cname AS '课程',MAX(sc.score) AS '最高分',MIN(sc.score) AS '最低分',AVG(sc.score) AS '平均分',SUM(CASE WHEN sc.score >= 60 THEN 1 ELSE 0 END) / COUNT(sc.sid) AS '及格率',SUM(CASE WHEN sc.score >= 70 AND sc.score < 80 THEN 1 ELSE 0 END) / COUNT(sc.sid) AS '中等率',SUM(CASE WHEN sc.score >= 80 AND sc.score < 90 THEN 1 ELSE 0 END) / COUNT(sc.sid) AS '优良率',SUM(CASE WHEN sc.score >= 90 THEN 1 ELSE 0 END) / COUNT(sc.sid) AS '优秀率'
FROM sc
INNER JOIN course ON sc.cid = course.cid
GROUP BY sc.Cid, course.cname;-- 方式3
SELECT sc.Cid AS '课程ID',course.cname AS '课程',MAX(sc.score) AS '最高分',MIN(sc.score) AS '最低分',AVG(sc.score) AS '平均分',COUNT(case when sc.score>=60 then score END)/COUNT(*) AS '及格率',COUNT(case when sc.score>=70 AND sc.score<80 then score END)/COUNT(*) AS '中等率',COUNT(case when sc.score>=80 AND sc.score<90 then score END)/COUNT(*) AS '优良率',COUNT(case when sc.score>=90 then score END )/COUNT(*) AS '优秀率'
FROM sc
INNER JOIN course ON sc.cid = course.cid
GROUP BY sc.Cid, course.cname;
SQL函数
数据准备
-- MySQL的函数
CREATE DATABASE db5;
USE db5;create table emp(emp_id int primary key auto_increment comment '编号',emp_name char(20) not null default '' comment '姓名',salary decimal(10,2) not null default 0 comment '工资',department char(20) not null default '' comment '部门'
);insert into emp(emp_name,salary,department)
values('张晶晶',5000,'财务部'),('王飞飞',5800,'财务部'),('赵刚',6200,'财务部'),('刘小贝',5700,'人事部'),
('王大鹏',6700,'人事部'),('张小斐',5200,'人事部'),('刘云云',7500,'销售部'),('刘云鹏',7200,'销售部'),
('刘云鹏',7800,'销售部');
group_concat函数
-- group_concat函数
-- 将所有员工的名字合成一行
SELECT GROUP_CONCAT(emp_name)
FROM emp;-- 指定分隔符和分组合并
SELECT department,GROUP_CONCAT(emp_name SEPARATOR ';')
FROM emp
GROUP BY department;-- 指定排序方式和分隔符
SELECT department,GROUP_CONCAT(emp_name ORDER BY salary DESC SEPARATOR ';')
FROM emp
GROUP BY department;-- 指定排序方式和分隔符并去重
SELECT department,GROUP_CONCAT(DISTINCT emp_name ORDER BY salary DESC SEPARATOR ';')
FROM emp
GROUP BY department;
数学相关函数
abs()取绝对值
-- abs() 取绝对值
SELECT ABS(5),ABS(-2.2),ABS(2.2),ABS(0);
sqrt()开平方
-- sqrt()开平方--负数开不了
SELECT SQRT(4),SQRT(5),SQRT(0),SQRT(-5);
mod(x,y)求余数
-- mod(x,y) 求余数-- 对0取余为NULL
SELECT MOD(10,3),MOD(10,-3),MOD(12.5,6),MOD(10,0);
ceil(x)和ceiling(x)向上取整
-- ceil(x)和ceiling(x)向上取整
SELECT CEIL(-2.1),CEILING(-2.9),CEIL(2.1),CEIL(2.9);
floor(x)向下取整
-- floor(x)向下取整
SELECT FLOOR(-2.1),FLOOR(-2.9),FLOOR(2.1),FLOOR(2.9);
rand()生成随机数
-- rand()生成随机数
SELECT RAND;-- RAND(x) x为随机数种子,种子相同,每次生成的随机数也相同
SELECT RAND(5);-- 0.40613597483014313
round()四舍五入函数
-- round():四舍五入函数
SELECT ROUND(-2.1),ROUND(-2.9),ROUND(2.1),ROUND(2.9);
-- 从指定位数进行四舍五入
-- 21.123
-- -1 0. 1 2 3第几位
SELECT ROUND(21.153,-2); -- 0
SELECT ROUND(25.153,-1); -- 30
SELECT ROUND(21.553,0); -- 22
SELECT ROUND(21.153,1); --21.2
SELECT ROUND(21.153,2); --21.15
SELECT ROUND(21.153,3); --21.153
SELECT ROUND(21.153,300); --21.153
sign()判断值的范围
-- sign(): 整数>0 1 整数=0 0 整数<0 -1
SELECT SIGN(100),SIGN(0),SIGN(-100);
pow(x,y),x的y次方
-- pow(x,y) power(x,y):x的y次方
SELECT POW(2,2),POW(2,-2),POW(-2,2);
sin()函数
-- sin() 角度xπ÷180°
SELECT SIN(PI()/6); -- SIN(π/6)
SELECT PI();
truncate(x,y)截断
-- truncate(x,y)截断 从x的第y位往后的截断,不进行四舍五入
-- 2.123456
-- 0.123456 第几位
SELECT TRUNCATE(2.123456,2); -- 2.12
SELECT TRUNCATE(2.123456,-2);-- 0
SELECT TRUNCATE(2.123456,800);-- 2.123456
字符串相关的函数
lower() 将大写转小写
-- lower() 将大写转小写
SELECT LOWER('LIYU'),LOWER('LiYu'),LOWER('liyu'),LOWER('李YU'),LOWER('李郁')
upper() 将小写转大写
-- upper() 将小写转大写
SELECT UPPER('LIYU'),UPPER('LiYu'),UPPER('liyu'),UPPER('李yu'),UPPER('李郁')-- 查询多列
USE test;
SELECT ename,LOWER(ename),UPPER(ename) FROM emp;
concat() 字符串连接
-- concat() 字符串连接
SELECT CONCAT('一','猫','人'),CONCAT(ename,'_','的工作',job) FROM emp;
substr()/substring()截取字符串
-- substr()/substring() 截取字符串
-- substr(x,y) x要截取的字符串,y要截取的字符串的起始位置(默认从1开始数)
-- substr(x,y,z) z截取的字符个数
SELECT SUBSTR('HelloWorld',5),SUBSTRING('HelloWorld',5);-- oWorld
SELECT SUBSTR('HelloWorld',5,3),SUBSTRING('HelloWorld',5,3);-- oWoSELECT SUBSTR('HelloWorld',-5),SUBSTRING('HelloWorld',-5);-- World
SELECT SUBSTR('HelloWorld',5,-3),SUBSTRING('HelloWorld',5,-3);-- 空
SELECT SUBSTR('HelloWorld',-5,-3),SUBSTRING('HelloWorld',-5,-3);-- 空
length() 返回字符串的长度
-- length() 返回字符串的长度
SELECT LENGTH('abcdefg'),LENGTH('一猫人');-- 7 9 (UTF8一个汉字占3个字节,GBK2个字节)
char_length() character_length() 返回一共有多少个字
-- char_length() character_length() 返回一共有多少个字
SELECT CHAR_LENGTH('abcdefg'),CHAR_LENGTH('一猫人');-- 7 3
SELECT CHARACTER_LENGTH('abcdefg'),CHARACTER_LENGTH('一猫人');-- 7 3
instr() 在字符串中找某个字符串第一次出现的位置
-- instr() 在字符串中找某个字符串第一次出现的位置
SELECT INSTR('footer','o'),INSTR('footer','oo');
LPAD()、RPAD()指定字符串输出的长度
-- 指定字符串输出的长度
-- lpad() rapd() 不够指定的位数补字符串
SELECT LPAD('毛阿敏',10,'5');-- 5555555毛阿敏
SELECT RPAD('毛阿敏',10,'5');-- 毛阿敏5555555SELECT RPAD('毛阿敏',-10,'5');-- 空
trim去除字符串的字符/空格
-- trim去除字符串的两端空格
SELECT CONCAT('a',(TRIM(' Stringzhua ')),'a');-- 去除左边空格或字符
SELECT TRIM(LEADING ' ' FROM ' Stringzhua ') AS 去除左边空格,
TRIM(LEADING 'M' FROM 'MMMStringzhuaMMM')AS 去除左边字符;-- 去除右边空格或字符
SELECT TRIM(TRAILING ' ' FROM ' Stringzhua ') AS 去除右边空格,
TRIM(TRAILING 'M' FROM 'MMMStringzhuaMMM')AS 去除右边字符;-- 去除两边空格或字符
SELECT TRIM(BOTH ' ' FROM ' Stringzhua ') AS 去除两边空格,
TRIM(BOTH 'M' FROM 'MMMStringzhuaMMM')AS 去除两边字符;
replace() 替换字符串
-- replace() 替换字符串,替换字符串中指定的旧字符串内容为新字符
-- 替换的是全部匹配的旧字符进行替换
SELECT REPLACE('www.github.com','github','gitee');-- repeat(str,count) 将str重复count次后返回
SELECT repeat('docker',3);-- reverse() 将字符串反转后返回
SELECT REVERSE('docker');
repeat() 重复字符串
-- repeat(str,count) 将str重复count次后返回
SELECT repeat('docker',3);
reverse() 反转字符串
-- reverse() 将字符串反转后返回
SELECT REVERSE('docker');
format() 格式化字符串
-- 格式化数字, 将其转换为字符串format(x,y) 整数部分每3位一个逗号
-- y为小数位数,保留几位,保留时进行四舍五入
SELECT FORMAT(1234,3);
SELECT FORMAT(1234.56782,4);-- 1,234.5678
SELECT FORMAT(1234.56789,4);-- 1,234.5679
日期时间相关函数
获取日期相关函数
-- 1.获取日期相关函数
-- NOW()返回当前的日期和时间,CURDATE()函数返回当前的日期
-- CURTIME()返回当前的时间
SELECT NOW(),CURDATE(),CURTIME();
获取日期
-- 2.获取日期 WEEK(NOW())获取的是这一年的第多少周
SELECT DATE(NOW()),YEAR(NOW()),MONTH(NOW()),DAY(NOW()),WEEK(NOW());
SELECT DATE(CURDATE()),YEAR(CURDATE()),MONTH(CURDATE()),DAY(CURDATE()),WEEK(CURDATE());
获取时间
-- 3.获取时间
SELECT TIME(NOW()),HOUR(NOW()),MINUTE(NOW()),SECOND(NOW());
SELECT TIME(CURTIME()),HOUR(CURTIME()),MINUTE(CURTIME()),SECOND(CURTIME());
获取当前天
-- 4.获取当前天 226 13 3(周日为第一天,1 3为周二)
SELECT DAYOFYEAR(NOW()),DAYOFMONTH(NOW()),DAYOFWEEK(NOW());
SELECT WEEKOFYEAR(NOW());-- 当前时间为该年的第多少周
日期相关的转换
格式化时间
-- 1.格式化时间DATE_FORMAT(date,'格式')
SELECT NOW(),DATE_FORMAT(NOW(),'%y-%m-%d %h:%i:%s');SELECT DATE_FORMAT(NOW(),'%Y-%b-%d');-- 2024-Aug-13
SELECT DATE_FORMAT(NOW(),'%Y-%M-%e');-- 2024-August-13
SELECT DATE_FORMAT(NOW(),'%Y-%m-%D');-- 2024-08-13th
SELECT DATE_FORMAT(NOW(),'%Y-%c-%j');-- 2024-8-226SELECT DATE_FORMAT(NOW(),'%a');-- Tue
SELECT DATE_FORMAT(NOW(),'%W');-- Tuesday
SELECT DATE_FORMAT(NOW(),'%H:%i:%S');-- 12:13:19
SELECT DATE_FORMAT(NOW(),'%h:%i:%f');-- 12:13:000000SELECT DATE_FORMAT(NOW(),'%T');-- 12:14:28
主题 | 格式 | 描述 |
---|---|---|
年 | %Y | 4位数的年份 |
月 | %b | 月份对应的英文缩写 |
月 | %M | 月份对应的英文全称 |
月 | %m | 01-12的月 |
月 | %c | 01-12的月份数值 |
日 | %d | 01-31的某月里面的第几天 |
日 | %e | 01-31的某月里面的第几天 |
日 | %D | 用th后缀表示某月中的第几天 |
日 | %j | 001~366的一年中的第几天 |
周 | %a | 星期几对应的英文缩写 |
周 | %W | 星期几对应的英文全称 |
时 | %H | 00~23的小时 |
时 | %h | 01~12的小时 |
分 | %i | 00~59的分钟 |
秒 | %S | 秒(00~59) |
秒 | %f | 微秒 |
时分秒 | %T | 返回当前的时分秒,24~小时(hh:mm:ss) |
extract函数
-- 2.extract函数,用于返回一个具体日期时间的单独部分
SELECT EXTRACT(YEAR FROM NOW()) AS col1,EXTRACT(MONTH FROM NOW()) AS col2,EXTRACT(DAY FROM NOW()) AS col3,EXTRACT(WEEK FROM NOW()) AS col4,EXTRACT(HOUR FROM NOW()) AS col5,EXTRACT(MINUTE FROM NOW()) AS col6,EXTRACT(SECOND FROM NOW()) AS col7;
unit | 说明 |
---|---|
year | 年 |
month | 月 |
day | 日 |
hour | 小时 |
minute | 分钟 |
second | 秒 |
week | 周数,全年第几周 |
日期相关计算问题(向后/向前偏移时间)
-- 3.日期相关计算问题
-- 向后偏移时间date_add(date,interval num unit)
SELECT NOW(),DATE_ADD(NOW(),INTERVAL 1 YEAR),DATE_ADD(NOW(),INTERVAL 1 MONTH),DATE_ADD(NOW(),INTERVAL 1 WEEK),DATE_ADD(NOW(),INTERVAL 1 DAY),DATE_ADD(NOW(),INTERVAL 1 HOUR),DATE_ADD(NOW(),INTERVAL 1 MINUTE),DATE_ADD(NOW(),INTERVAL 1 SECOND);
-- 向前偏移时间date_sub(date,interval num unit)
SELECT NOW(),DATE_SUB(NOW(),INTERVAL 1 YEAR),DATE_SUB(NOW(),INTERVAL 1 MONTH),DATE_SUB(NOW(),INTERVAL 1 WEEK),DATE_SUB(NOW(),INTERVAL 1 DAY),DATE_SUB(NOW(),INTERVAL 1 HOUR),DATE_SUB(NOW(),INTERVAL 1 MINUTE),DATE_SUB(NOW(),INTERVAL 1 SECOND);
datediff(end_date,start_date) 和指定时间差多少天
-- 4.datediff(end_date,start_date) 和指定时间差多少天
SELECT DATEDIFF(NOW(),'2022-08-12');
练习
-- 2024-08-13 12:05
-- emp
-- 查询入职时间为1981年的人
USE test;
SELECT * FROM emp;SELECT *
FROM emp
WHERE SUBSTRING(hiredate,1,4)=1981;SELECT *
from emp
WHERE EXTRACT(YEAR FROM hiredate)=1981;
流程控制函数
if()类似三目运算
-- if
-- 1.if()类似三目运算
SELECT if(10<5,'大','小'),if(1>2,1,0),if(1<2,'√','×');USE mydb4;
SELECT *,if(Tsex=1,'男','女') AS 性别,
if(INSTR(Taddress,'陕西')>0,'陕西','其他')AS 籍贯
FROM teacher;
ifnull()判断是否为空
-- 2.ifnull()判断是否为空
SELECT IFNULL(NULL,'猫头鹰'),IFNULL('猫头鹰','一猫人');
nullif(expr1,expr2) 比较两个字符串是否相等,相等为null,不相等返回expr1
-- 3.nullif(expr1,expr2) 比较两个字符串是否相等,相等为null,不相等返回expr1
SELECT NULLIF('苏轼','苏轼'),NULLIF('苏轼','苏东坡'),NULLIF(1,1);-- case
CREATE TABLE t_stu(id INT AUTO_INCREMENT COMMENT '编号',NAME VARCHAR(10) COMMENT '姓名',sex TINYINT COMMENT '性别,0:未知,1:男,2:女',PRIMARY KEY(id)
) COMMENT '学生表';INSERT INTO t_stu (NAME,sex) VALUES
('张学友',1),('刘德华',1),('郭富城',1),('蔡依林',2),('猫猫',0);SELECT * FROM t_stu;
-- 写法1
SELECT *,(case t.sexwhen 1 then '男'when 2 then '女'ELSE '未知'END
)AS '性别'
FROM t_stu AS t;-- 写法2
SELECT *,(case when t.sex=1 then '男'when t.sex=2 then '女'ELSE '未知'END
)AS '性别'
FROM t_stu AS t;
练习
-- 2024-08-13 14:35
use mydb4;
-- 创建订单表
create table orders(oid int primary key, -- 订单idprice double, -- 订单价格payType int -- 支付类型(1:微信支付 2:支付宝支付 3:银行卡支付 4:其他)
);insert into orders values(1,1200,1);
insert into orders values(2,1000,2);
insert into orders values(3,200,3);
insert into orders values(4,3000,1);
insert into orders values(5,1500,2);
SELECT * FROM orders;
-- 查询支付方式
-- 写法1
SELECT *,(case t.paytypewhen 1 then '微信支付'when 2 then '支付宝支付'when 2 then '银行卡支付'ELSE '其他'END
)AS '支付方式'
FROM orders AS t;-- 写法2
SELECT *,(case when t.paytype=1 then '微信支付'when t.paytype=2 then '支付宝支付'when t.paytype=2 then '银行卡支付'ELSE '其他'END
)AS '支付方式'
FROM orders AS t;
开窗函数MySQL8特有
数据准备
USE db5;
create table test(id int,name varchar(10),sale int);
insert into test values(1,'aaa',100);
insert into test values(1,'bbb',200);
insert into test values(1,'ccc',200);
insert into test values(1,'ddd',300);
insert into test values(2,'eee',400);
insert into test values(2,'fff',200);
排序函数
SELECT * FROM test;
-- ROW_NUMBER()计算的值表示每组内部排序后的顺序编号(组内的排序是连续且唯一的)
SELECT *,ROW_NUMBER() OVER() FROM test;
-- 根据id分组,进行排序
SELECT *,ROW_NUMBER() OVER(PARTITION BY id) FROM test;
SELECT *,ROW_NUMBER() OVER(PARTITION BY id ORDER BY sale DESC) FROM test;
-- RANK()有相同的排名时,相同排名的数据有相同的序号,排序序号不连续
SELECT *,RANK() OVER(PARTITION BY id ORDER BY sale DESC) FROM test;
-- DENSE_RANK(),有相同的排名时,相同排名的数据有相同的序号,排序序号连续
SELECT *,DENSE_RANK() OVER(PARTITION BY id ORDER BY sale DESC) FROM test;SELECT *,ROW_NUMBER() OVER(ORDER BY sale DESC),RANK() OVER(ORDER BY sale DESC),DENSE_RANK() OVER(ORDER BY sale DESC)
FROM test;
开窗聚合函数
-- 开窗聚合函数
SELECT *,SUM(salary) OVER() FROM employee;-- 开窗聚合函数求和(分组求和)
SELECT *,SUM(salary) OVER (PARTITION BY dname) FROM employee;-- 开窗聚合函数求和(分组并排序求和)
SELECT *,SUM(salary) OVER (PARTITION BY dname ORDER BY hiredate DESC) FROM employee;
滑动窗口
-- 滑动窗口
-- 算出当前行和前三行的和
SELECT *,SUM(salary) OVER (PARTITION BY dnameORDER BY hiredate DESCROWS BETWEEN 3 PRECEDING AND CURRENT ROW) sum
FROM employee;-- 找出前两行、当前行、后一行中的最大值
SELECT *,MAX(salary) OVER (PARTITION BY dnameORDER BY hiredate DESCROWS BETWEEN 2 PRECEDING AND 1 FOLLOWING) MAX
FROM employee;
-- 找出前两行、当前行、后一行中的最小值
SELECT *,MIN(salary) OVER (PARTITION BY dnameORDER BY hiredate DESCROWS BETWEEN 2 PRECEDING AND 1 FOLLOWING) MIN
FROM employee;-- 找出前两行、当前行、后一行中的平均值
SELECT *,AVG(salary) OVER (PARTITION BY dnameORDER BY hiredate DESCROWS BETWEEN 2 PRECEDING AND 1 FOLLOWING) AVG
FROM employee;-- 找出前两行、当前行、后一行中的总和
SELECT *,SUM(salary) OVER (PARTITION BY dnameORDER BY hiredate DESCROWS BETWEEN 2 PRECEDING AND 1 FOLLOWING) SUM
FROM employee;-- 找出前两行、当前行、后一行中的个数
SELECT *,COUNT(salary) OVER (PARTITION BY dnameORDER BY hiredate DESCROWS BETWEEN 2 PRECEDING AND 1 FOLLOWING) COUNT
FROM employee;
练习
数据准备
USE db5;
create table employee( dname varchar(20), -- 部门名 eid varchar(20), ename varchar(20), hiredate date, -- 入职日期 salary double -- 薪资
); insert into employee values('研发部','1001','刘备','2021-11-01',3000);
insert into employee values('研发部','1002','关羽','2021-11-02',5000);
insert into employee values('研发部','1003','张飞','2021-11-03',7000);
insert into employee values('研发部','1004','赵云','2021-11-04',7000);
insert into employee values('研发部','1005','马超','2021-11-05',4000);
insert into employee values('研发部','1006','黄忠','2021-11-06',4000);insert into employee values('销售部','1007','曹操','2021-11-01',2000);
insert into employee values('销售部','1008','许褚','2021-11-02',3000);
insert into employee values('销售部','1009','典韦','2021-11-03',5000);
insert into employee values('销售部','1010','张辽','2021-11-04',6000);
insert into employee values('销售部','1011','徐晃','2021-11-05',9000);
insert into employee values('销售部','1012','曹洪','2021-11-06',6000);
SELECT * FROM employee;
-- 1.- 对每个部门的员工按照薪资排序,并给出排名
SELECT *,ROW_NUMBER() OVER(PARTITION BY dname ORDER BY salary DESC) FROM employee;
-- 2.-- 对每个部门的员工按照薪资排序,并给出排名 rank
SELECT *,RANK() OVER(PARTITION BY dname ORDER BY salary DESC) FROM employee;
-- 3.-- 对每个部门的员工按照薪资排序,并给出排名 dense-rank
SELECT *,DENSE_RANK() OVER(PARTITION BY dname ORDER BY salary DESC) FROM employee;
-- 4-- 求出每个部门薪资排在前三名的员工-(提示,子查询)单行单列 单行多列 多行多列
SELECT *
FROM (
SELECT *,ROW_NUMBER() OVER(PARTITION BY dname ORDER BY salary DESC) AS number
FROM employee) t1
WHERE number<=3;
前后函数
-- 为每一行数据的上一行数据进行开窗,如果该行没有上一行数据,则显示为NULL
SELECT sale_date,salary,LAG(salary) OVER(ORDER BY sale_date) AS lag_salary
FROM sales;-- 设置OFFSET偏移量为2,可以查到当前行的前面第2行的数据
-- 当前行的前面第2行如果没有数据,则显示为null,否则返回并显示对应的数据内容
SELECT sale_date,salary,LAG(salary,2) OVER(ORDER BY sale_date) AS lag_salary
FROM sales;-- 设置OFFSET偏移量为1,可以查到当前行的往下数1行的数据
-- 当前行的往下数1行如果没有数据,则显示为0,即把默认显示的NULL替换为自定义的显示内容
SELECT sale_date,salary,LEAD(salary,1,0) OVER(ORDER BY sale_date) AS lag_salary
FROM sales;-- 存在下一行的数据显示为exist,不存在往下数2行的数据则显示0,这个NULL为默认的
SELECT sale_date,salary,LEAD('exist',1,0) OVER(ORDER BY sale_date) AS lag_salary
FROM sales;
首尾函数
-- 首尾函数
-- db5的employee表
USE db5;
SELECT *,FIRST_VALUE(salary)OVER(PARTITION BY dname ORDER BY hiredate ) AS FIRST,LAST_VALUE(salary)OVER(PARTITION BY dname ORDER BY hiredate ) AS LAST
FROM employee;
分箱函数
-- 分箱函数
-- test的emp表
USE test;
SELECT ename,hiredate,deptno,
NTILE(4) OVER(PARTITION BY deptno ORDER BY hiredate)
FROM emp;
MySQL事务(待补充)
概念:将一组SQL语句放在同一批次内去执行,如果一个SQL语句出错,则该批次内的所有SQL都将被取消执行
事务是一个过程,在进行中
特点:一个事务中如果有一个数据库操作失败,则整个
练习题1
create table student(
stuno int(8) primary key auto_increment,
sname varchar(12) not null,
sex char(2) default '男',
brithday date,
email varchar(20) unique,
score double(5,2) check(score>=0 and score<=100),
classno int(2) not null
);update student set score=score+2 where classno=1;select *
from student
where sname like '张%'
order by score desc;select DATE_FORMAT(birthday,'%Y年%m月%d日 %h:%i:%s')
from student;select count(*),max(score),min(score),avg(score) as pingjun
from student
group by classno
order by pingjun desc;select stuno,sname,score,classno
from student
order by score desc
LIMIT 10