MySQL_SE

MySQL

命令行客户端

数据库

1、查看所有的数据库

show databases;

2、创建自己的数据库

create database 数据库名;

#创建atguigudb数据库
create database atguigudb;

3、删除数据库

drop database 数据库名;
#删除atguigudb数据库
drop database atguigudb;

4、使用自己的数据库

use 数据库名;

#使用atguigudb数据库
use atguigudb;

说明:如果没有使用use语句,后面针对数据库的操作也没有加“数据名”的限定,那么会报“ERROR 1046 (3D000): No database selected”(没有选择数据库)

使用完use语句之后,如果接下来的SQL都是针对一个数据库操作的,那就不用重复use了,如果要针对另一个数据库操作,那么要重新use。

数据表

1、查看某个库的所有表格

show tables;  #要求前面有use语句

show tables from 数据库名;

2、创建新的表格

create table 表名称(
	字段名  数据类型,
	字段名 数据类型
);

说明:如果是最后一个字段,后面就用加逗号,因为逗号的作用是分割每个字段。

#创建学生表
create table student(
	id int,
    name varchar(20)  #说名字最长不超过20个字符
);

3、查看定义好的表结构

desc 表名称;
desc student;

4、添加一条记录

insert into 表名称 values(值列表);

#添加两条记录到student表中
insert into student values(1,'张三'),(2,'李四');

5、查看一个表的数据

select * from 表名称;

6、删除表

drop table 表名称;
#删除学生表
drop table student;

导入数据

在命令行客户端登录mysql,使用source指令导入

mysql> source d:\练习脚本.sql

注意:在使用命令行导入SQL脚本之前,请使用记事本或NotePad++等文本编辑器打开SQL脚本查看SQL脚本中是否有USE语句,如果没有,那么在命令行中需要先使用USE语句指定具体的数据库,否则会报“No database selected”的错误。

image-20220222174512444

导出数据

在命令行客户端不登录mysql,使用mysqldump命令。

mysqldump -u用戶名 -p密码 数据库名 > 脚本名.sql
mysqldump -u用戶名 -p密码 数据库名 表名 > 脚本名.sql

image-20220207094511797

SQL的分类

DDL语句:数据定义语句(Data Define Language),例如:创建(create),修改(alter),删除(drop)等

DML语句:数据操作语句(Data Manipulation Language),例如:增(insert),删(delete),改(update),查(select)

因为查询语句使用的非常的频繁,所以很多人把查询语句单拎出来一类,DQL(数据查询语言)

DCL语句:数据控制语句(Data Control Language),例如:grant,commit,rollback等

其他语句:USE语句,SHOW语句,SET语句等。这类的官方文档中一般称为命令。

SQL语法规范

(1)mysql的sql语法不区分大小写

​ A:数据库的表中的数据是默认不区分大小写

​ B:sql中的关键字,比如:create,insert等,不区分大小写。但是大家习惯上把关键字都“大写”。

(2)命名时:尽量使用26个英文字母大小写,数字0-9,下划线,不要使用其他符号

(3)建议不要使用mysql的关键字等来作为表名、字段名、数据库名等,如果不小心使用,请在SQL语句中使用`(飘号)引起来

(4)数据库和表名、字段名等对象名中间不要包含空格、如果字段由多个单词组成则中间使用下划线连接

(5)同一个mysql软件中,数据库不能同名,同一个库中,表不能重名,同一个表中,字段不能重名

SQL脚本中如何加注释

SQL脚本中如何加注释

  • 单行注释:#注释内容(mysql特有的)

  • 单行注释:--空格注释内容 其中--后面的空格必须有

  • 多行注释:/* 注释内容 */

create table tt(
	id int, #编号
    `name` varchar(20), -- 姓名
    gender enum('男','女')
    /*
    性别只能从男或女中选择一个,
    不能两个都选,或者选择男和女之外的
    */
);

mysql脚本中的标点符号

mysql脚本中标点符号的要求如下:

  • 本身成对的标点符号必须成对,例如:(),'',""。

  • 所有标点符号必须英文状态下半角输入方式下输入。

几个特殊的标点符号:

  • 小括号():在创建表、添加数据、函数使用、子查询、计算表达式等等会用()表示某个部分是一个整体结构。

  • 单引号'':字符串和日期类型的数据值使用单引号''引起来,数值类型的不需要加标点符号。

  • 双引号"":列的别名可以使用双引号"",给表名取别名不要使用双引号。

DQL

因为查询语句使用的非常的频繁,所以很多人把查询语句单拎出来一类,DQL(数据查询语言)。

SELECT语句

SELECT语句是用于查看计算结果、或者查看从数据表中筛选出的数据的。

SELECT语句的基本语法:

SELECT 常量;
SELECT 表达式;
SELECT 函数;

例如:

SELECT 1; 
SELECT 9/2;
SELECT NOW(); 

如果要从数据表中筛选数据,需要加FROM子句。FROM指定数据来源。字段列表筛选列。

SELECT 字段列表 FROM 表名称;

如果要从数据表中根据条件筛选数据,需要加FROM和WHERE子句。WHERE筛选行。

SELECT 字段列表 FROM 表名称 WHERE 条件;

完整的SELECT语句后面可以跟7个子句,后面会逐一讲解。

使用别名

在当前select语句中给某个字段或表达式计算结果,或表等取个临时名称,便于当前select语句的编写和理解。这个临时名称称为别名。

select 字段名1 as "别名1", 字段名2 as "别名2" from 表名称 as 别名;
  • 列的别名有空格时,请加双引号。列的别名中没有空格时,双引号可以加也可以不加
  • 表的别名不能加双引号,表的别名中间不能包含空格。
  • as大小写都可以,as也完全可以省略。

结果去重

mysql可以在查询结果中使用distinct关键字去重。

select distinct 字段列表 from 表名称 【where 条件】; 
select distinct did from t_employee;

运算符

算术运算符

加:+
	在MySQL +就是求和,没有字符串拼接
减:-
乘:*
除: /:会保留小数
	div:除法 不会保留小数
模:%   mod

注意
mysql中没有 +=等运算符
SELECT 3+4,7-6,8*9,9/3,9%4;
+-----+-----+-----+--------+------+
| 3+4 | 7-6 | 8*9 | 9/3    | 9%4  |
+-----+-----+-----+--------+------+
|   7 |   1 |  72 | 3.0000 |    1 |
+-----+-----+-----+--------+------+
SELECT 10/4,10 DIV 4;
+--------+----------+
| 10/4   | 10 DIV 4 |
+--------+----------+
| 2.5000 |        2 |
+--------+----------+

比较运算符

大于:>
小于:<
大于等于:>=
小于等于:<=
等于:=   不能用于null判断
不等于:!=  或 <>  不能用于null判断
-- select 字段 from 表名 [where 筛选条件];
-- 薪资高于10000元的员工信息
SELECT * FROM t_employee WHERE salary >10000;

-- 员工的名字 薪水 性别 生日  要求是 80后 '1990-01-01'
SELECT ename,salary,gender,birthday FROM t_employee WHERE birthday > '1990-01-01';

-- 薪资小于15000元的员工信息
SELECT * FROM t_employee WHERE salary<15000;

-- 2017年之前入职的
SELECT * FROM t_employee WHERE hiredate <'2015-01-01';



-- 性别为女的员工的名字 性别 薪资
SELECT ename,salary,gender FROM t_employee WHERE gender='女';


-- 查看居住在天通苑一区 的员工信息
SELECT * FROM t_employee WHERE address ='天通苑一区';

-- did 不等于2的员工信息
SELECT ename,did FROM t_employee WHERE did <>2;

逻辑运算符

逻辑与:&& 或 and
逻辑或:|| 或 or
逻辑非:! 或 not
逻辑异或: xor
-- 薪资高于15000 并且性别为女的员工的名字 薪资  性别 生日
SELECT 
  ename,
  salary,
  gender,
  birthday 
FROM
  t_employee 
WHERE salary > 15000 AND gender = '女' ;

-- 薪资高于10000 并且性别为女的员工 并且是80后 的名字 薪资  性别 生日
SELECT 
  ename,
  salary,
  gender,
  birthday 
FROM
  t_employee 
WHERE salary > 10000 
  AND gender = '女' 
  AND birthday >= '1980-01-01' ;
  
  
  
-- 或者
-- 薪资高于10000元 或者是女员工
SELECT *
FROM t_employee WHERE salary>10000 OR gender='女';


-- 异或
-- 薪资>15000的男 薪资<=15000的女
SELECT ename,salary,gender
FROM t_employee WHERE salary>15000 XOR gender='女';

区间或集合范围比较运算符(between and)

区间范围:between x  and  y  [x,y]
	    not between x  and  y
集合范围:in (x,x,x)
	    not  in(x,x,x)
#查询薪资在[10000,15000]
select * from t_employee where salary>=10000 && salary<=15000;
select * from t_employee where salary between 10000 and 15000;

#查询籍贯在这几个地方的
select * from t_employee where native_place in ('北京', '浙江', '江西');

#查询薪资不在[10000,15000]
select * from t_employee where salary not between 10000 and 15000;

#查询籍贯不在这几个地方的
select * from t_employee where native_place not in ('北京', '浙江', '江西');

模糊匹配比较运算符(Like)

%:代表任意个字符

_:代表一个字符,如果两个下划线代表两个字符

#查询名字中包含'冰'字
select * from t_employee where ename like '%冰%';

#查询名字以‘雷'结尾的
select * from t_employee where ename like '%雷';

#查询名字以’李'开头
select * from t_employee where ename like '李%';

#查询名字有冰这个字,但是冰的前面只能有1个字
select * from t_employee where ename like '_冰%';

关于null值的问题(is null or not is null)

-- 是null
   字段名 is null;
   字段名 <=> null;
-- 不是null	
   字段名 is not null;
   not 字段名 <=> null;
-- 奖金比例为null 
SELECT ename,salary,commission_pct FROM t_employee WHERE commission_pct IS NULL;

SELECT ename,salary,commission_pct FROM t_employee WHERE commission_pct <=> NULL;
-- 奖金比例为不为null 

SELECT ename,salary,commission_pct FROM t_employee WHERE commission_pct IS NOT NULL;

SELECT ename,salary,commission_pct FROM t_employee WHERE NOT commission_pct  <=> NULL;

位运算符

左移:<<
右移:>>
按位与:&
按位或:|
按位异或:^
按位取反:~

系统预定义函数

函数:代表一个独立的可复用的功能。

和Java中的方法有所不同,不同点在于:MySQL中的函数必须有返回值,参数可以有可以没有。

MySQL中函数分为:

(1)系统预定义函数:MySQL数据库管理软件给我提供好的函数,直接用就可以,任何数据库都可以用公共的函数。

  • 分组函数:或者又称为聚合函数,多行函数,表示会对表中的多行记录一起做一个“运算”,得到一个结果。
    • 求平均值的avg,求最大值的max,求最小值的min,求总和sum,求个数的count等
  • 单行函数:表示会对表中的每一行记录分别计算,有n行得到还是n行结果
    • 数学函数、字符串函数、日期时间函数、条件判断函数、窗口函数等

(2)用户自定义函数:由开发人员自己定义的,通过CREATE FUNCTION语句定义,是属于某个数据库的对象。

分组函数/多行函数/聚合函数

调用完函数后,结果的行数变少了,可能得到一行,可能得到少数几行。

分组函数有合并计算过程。

常用分组函数类型

  • AVG(x) :求平均值
  • SUM(x):求总和
  • MAX(x) :求最大值
  • MIN(x) :求最小值
  • **COUNT(x) **:统计记录数

注意:
1.多行函数会忽略null值(null值不计数)
2.统计数量 count(*)

#统计t_employee表的员工的数量
SELECT COUNT(*) FROM t_employee;
SELECT COUNT(1) FROM t_employee;
SELECT COUNT(eid) FROM t_employee;
SELECT COUNT(commission_pct) FROM t_employee;

#找出t_employee表中最高的薪资值
SELECT MAX(salary) FROM t_employee;

#找出t_employee表中最低的薪资值
SELECT MIN(salary) FROM t_employee;

#统计t_employee表中平均薪资值
SELECT AVG(salary) FROM t_employee;

#统计所有人的薪资总和,财务想看一下,一个月要准备多少钱发工资
SELECT SUM(salary) FROM t_employee; #没有考虑奖金

单行函数

1、数学函数

以下表格中也只是列出了一部分

函数 用法
ABS(x) 返回x的绝对值
CEIL(x) 返回大于x的最小整数值
FLOOR(x) 返回小于x的最大整数值
MOD(x,y) 返回x/y的模
RAND() 返回0~1的随机值
ROUND(x,y) 返回参数x的四舍五入的有y位的小数的值
TRUNCATE(x,y) 返回数字x截断为y位小数的结果
FORMAT(x,y) 强制保留小数点后y位,整数部分超过三位的时候以逗号分割,并且返回的结果是文本类型的
SQRT(x) 返回x的平方根
POW(x,y) 返回x的y次方
-- 绝对值
SELECT ABS(-10),ABS(10);

-- 四舍五入
SELECT ROUND(6.1),ROUND(7.6);

-- round(一个小数),round(小数,保留的小数位数)
SELECT ROUND(7.1234,2),ROUND(7.4567,2);

-- 向上取整
SELECT CEIL(1.1),CEIL(3.1);
-- 向下取整
SELECT FLOOR(3.9),FLOOR(8.9);
-- 保留指定位数的小数 不会四舍五入
SELECT TRUNCATE(5.6789,2),TRUNCATE(8.4567,0);
-- 数字格式化 对整数三位一个逗号 小数 会四舍五入
SELECT FORMAT(112345.19,1);

-- 开平方
SELECT SQRT(9),SQRT(16);
-- x^y
SELECT POW(3,3);

2、字符串函数

下面列出部分字符串函数:

函数 功能描述
CONCAT(S1,S2,……Sn) 连接S1,S2,……Sn为一个字符串
CONCAT_WS(s,S1,S2,……Sn) 同CONCAT(S1,S2,…)函数,但每个字符串之间要加上s
CHAR_LENGTH(s) 返回字符串s的字符数
LENGTH(s) 返回字符串s的字节数,和字符集有关
LOCATE(str1,str)或 POSITION(str1 in str)或 INSTR(str,str1) 返回子字符串str1在str中的开始位置
UPPER(s)或UCASE(s) 将字符串s的所有字母转成大写字母
LOWER(s)或LCASE(s) 将字符串s的所有字母转成小写字母
LEFT(s,n) 返回字符串s最左边的n个字符
RIGHT(s,n) 返回字符串s最右边的n个字符
LPAD(str,len,pad) 用字符串pad对str最左边进行填充直到str的长度达到len
RPAD(str,len,pad) 用字符串pad对str最右边进行填充直到str的长度达到len
LTRIM(s) 去掉字符串s左侧的空格
RTRIM(s) 去掉字符串s右侧的空格
TRIM(s) 去掉字符串s开始与结尾的空格
TRIM([BOTH] s1 FROM s) 去掉字符串s开始与结尾的s1
TRIM([LEADING] s1 FROM s) 去掉字符串s开始处的s1
TRIM([TRAILING]s1 FROM s) 去掉字符串s结尾处的s1
INSERT(str,index,len,instr) 将字符串str从index位置开始len个字符的替换为字符串instr
REPLACE(str,a,b) 用字符串b替换字符串str中所有出现的字符串a
REPEAT(str,n) 返回str重复n次的结果
REVERSE(s) 将字符串反转
STRCMP(s1,s2) 比较字符串s1,s2
SUBSTRING(s,index,len) 返回从字符串s的index位置截取len个字符
SUBSTRING_INDEX(str, 分隔符,count) 如果count是正数,那么从左往右数,第n个分隔符的左边的全部内容。例如,substring_index("www.atguigu.com",".",1)是"www"。如果count是负数,那么从右边开始数,第n个分隔符右边的所有内容。例如,substring_index("www.atguigu.com",".",-1)是"com"。
-- 拼接
SELECT CONCAT("bob","jim","marry","安琪拉");
-- 指定拼接符的拼接
SELECT CONCAT_WS('-',"安琪拉","米莱迪","妲己","吕布");

-- 字节数量
SELECT LENGTH("ABC"),LENGTH("安琪拉");
-- 字符数量
SELECT CHAR_LENGTH("ABC"),CHAR_LENGTH("安琪拉");

-- 转大小写
SELECT UPPER("helloWorld"),LOWER("helloWorld");

-- MySQL下标从1开始
SELECT POSITION("B" IN "ABC");

-- 获取左边指定数量个元素
SELECT LEFT("ABCDEF",3);
-- 将字符串从左边填充到指定数量个 
SELECT LPAD("AA",5,"哈");

-- trim ltrim  rtrim 去除空格
SELECT CONCAT("--->",TRIM("  A  B  C  "),"<----");

SELECT TRIM(BOTH '%' FROM '%%%%%ABC%%%%');
SELECT TRIM(LEADING '*' FROM '******DEF**');

-- 从指定下标开始截取元素
SELECT SUBSTRING("ABCDEFG",3);-- CDEFG
-- 从指定下标开始截取指定数量个元素
SELECT SUBSTRING("ABCDEFG",1,3);
-- 将指定元素重复指定数量次
SELECT REPEAT("ABC",13);
-- 反转
SELECT REVERSE("ABCD");

-- 获取指定分割符的数据 正数(获取的个数) 从左向右  负数(获取的个数) 从右向左
SELECT SUBSTRING_INDEX("A.B.C.D.E",'.',20);

-- 获取名字, 邮箱, 邮箱@符之前的内容 
SELECT ename,email,SUBSTRING_INDEX(email,'@',1)  FROM t_employee;

3、日期时间函数

函数 功能描述
CURDATE()或CURRENT_DATE() 返回当前系统日期
CURTIME()或CURRENT_TIME() 返回当前系统时间
NOW()/SYSDATE()/CURRENT_TIMESTAMP()/ LOCALTIME()/LOCALTIMESTAMP() 返回当前系统日期时间
UTC_DATE()/UTC_TIME() 返回当前UTC日期值/时间值UTC(协调世界时)是全球统一时间的标准。它是基于原子钟生成的时间,被认为是“最精确的时间”。
UNIX_TIMESTAMP(date) 返回一个UNIX时间戳
YEAR(date)/MONTH(date)/DAY(date)/ HOUR(time)/MINUTE(time)/SECOND(time) 返回具体的时间值
EXTRACT(type FROM date) 从日期中提取一部分值
DAYOFMONTH(date)/DAYOFYEAR(date) 返回一月/年中第几天
WEEK(date)/WEEKOFYEAR(date) 返回一年中的第几周
DAYOFWEEK() 返回周几,注意,周日是1,周一是2,…周六是7
WEEKDAY(date) 返回周几,注意,周一是0,周二是1,…周日是6
DAYNAME(date) 返回星期,MONDAY,TUESDAY,…SUNDAY
MONTHNAME(date) 返回月份,January,…
DATEDIFF(date1,date2)/TIMEDIFF(time1,time2) 返回date1-date2的日期间隔/返回time1-time2的时间间隔
DATE_ADD(date,INTERVAL expr type)或ADDDATE/DATE_SUB/SUBDATE 返回与给定日期相差INTERVAL时间段的日期
ADDTIME(time,expr)/SUBTIME(time,expr) 返回给定时间加上/减去expr的时间值
DATE_FORMAT(datetime,fmt)/ TIME_FORMAT(time,fmt) 按照字符串fmt格式化日期datetime值/时间time值
STR_TO_DATE(str,fmt) 按照字符串fmt对str进行解析,解析为一个日期
GET_FORMAT(val_type,format_type) 返回日期时间字符串的显示格式

函数中日期时间类型说明

参数类型 描述 参数类型 描述
YEAR YEAR_MONTH 年月
MONTH DAY_HOUR 日时
DAY DAY_MINUTE 日时分
HOUR DAY_SECOND 日时分秒
MINUTE HOUR_MINUTE 时分
SECOND HOUR_SECOND 时分秒
WEEK 星期 MINUTE_SECOND 分秒
QUARTER 季度信息

函数中format参数说明

格式符 说明 格式符 说明
%Y 4位数字表示年份 %y 两位数字表示年份
%M 月名表示月份(January,…) %m 两位数字表示月份(01,02,03,…)
%b 缩写的月名(Jan.,Feb.,…) %c 数字表示月份(1,2,3…)
%D 英文后缀表示月中的天数(1st,2nd,3rd,…) %d 两位数字表示表示月中的天数(01,02,…)
%e 数字形式表示月中的天数(1,2,3,…) %p AM或PM
%H 两位数字表示小数,24小时制(01,02,03,…) %h和%I 两位数字表示小时,12小时制(01,02,03,…)
%k 数字形式的小时,24小时制(1,2,3,…) %l 数字表示小时,12小时制(1,2,3,…)
%i 两位数字表示分钟(00,01,02,…) %S和%s 两位数字表示秒(00,01,02,…)
%T 时间,24小时制(hh:mm:ss) %r 时间,12小时制(hh:mm:ss)后加AM或PM
%W 一周中的星期名称(Sunday,…) %a 一周中的星期缩写(Sun.,Mon.,Tues.,…)
%w 以数字表示周中的天数(0=Sunday,1=Monday,…) %j 以3位数字表示年中的天数(001,002,…)
%U 以数字表示的的第几周(1,2,3,…) 其中Sunday为周中的第一天 %u 以数字表示年中的年份(1,2,3,…) 其中Monday为周中第一天
%V 一年中第几周(01~53),周日为每周的第一天,和%X同时使用 %X 4位数形式表示该周的年份,周日为每周第一天,和%V同时使用
%v 一年中第几周(01~53),周一为每周的第一天,和%x同时使用 %x 4位数形式表示该周的年份,周一为每周第一天,和%v同时使用
%% 表示%

GET_FORMAT函数中val_type 和format_type参数说明

值类型 格式化类型 显示格式字符串
DATE EUR %d.%m.%Y
DATE INTERVAL %Y%m%d
DATE ISO %Y-%m-%d
DATE JIS %Y-%m-%d
DATE USA %m.%d.%Y
TIME EUR %H.%i.%s
TIME INTERVAL %H%i%s
TIME ISO %H:%i:%s
TIME JIS %H:%i:%s
TIME USA %h:%i:%s %p
DATETIME EUR %Y-%m-%d %H.%i.%s
DATETIME INTERVAL %Y%m%d %H%i%s
DATETIME ISO %Y-%m-%d %H:%i:%s
DATETIME JIS %Y-%m-%d %H:%i:%s
DATETIME USA %Y-%m-%d %H.%i.%s
#日期时间函数
/*
获取系统日期时间值
获取某个日期或时间中的具体的年、月等值
获取星期、月份值,可以是当天的星期、当月的月份
获取一年中的第几个星期,一年的第几天
计算两个日期时间的间隔
获取一个日期或时间间隔一定时间后的另个日期或时间
和字符串之间的转换
*/
#(1)获取系统日期。CURDATE()和CURRENT_DATE()函数都可以获取当前系统日期。将日期值“+0”会怎么样?
SELECT CURDATE(),CURRENT_DATE();

#(2)获取系统时间。CURTIME()和CURRENT_TIME()函数都可以获取当前系统时间。将时间值“+0”会怎么样?
SELECT CURTIME(),CURRENT_TIME();

#(3)获取系统日期时间值。CURRENT_TIMESTAMP()、LOCALTIME()、SYSDATE()和NOW()
SELECT CURRENT_TIMESTAMP(),LOCALTIME(),SYSDATE(),NOW();

#(4)获取当前UTC(世界标准时间)日期或时间值。
#本地时间是根据地球上不同时区所处的位置调整 UTC 得来的,
#例如,北京时间比UTC时间晚8个小时。
#UTC_DATE(),CURDATE(),UTC_TIME(), CURTIME()
SELECT UTC_DATE(),CURDATE(),UTC_TIME(), CURTIME();


#(5)获取UNIX时间戳。
SELECT UNIX_TIMESTAMP(),UNIX_TIMESTAMP('2022-1-1');

#(6)获取具体的时间值,比如年、月、日、时、分、秒。
#分别是YEAR(date)、MONTH(date)、DAY(date)、HOUR(time)、MINUTE(time)、SECOND(time)。
SELECT YEAR(CURDATE()),MONTH(CURDATE()),DAY(CURDATE());
SELECT HOUR(CURTIME()),MINUTE(CURTIME()),SECOND(CURTIME());


#(7)获取日期时间的指定值。EXTRACT(type FROM date/time)函数
SELECT EXTRACT(YEAR_MONTH FROM CURDATE());

#(8)获取两个日期或时间之间的间隔。
#DATEDIFF(date1,date2)函数表示返回两个日期之间间隔的天数。
#TIMEDIFF(time1,time2)函数表示返回两个时间之间间隔的时分秒。

#查询今天距离员工入职的日期间隔天数
SELECT ename,DATEDIFF(CURDATE(),hiredate) FROM t_employee;

#查询现在距离中午放学还有多少时间
SELECT TIMEDIFF(CURTIME(),'12:0:0');

#(9)在“t_employee”表中查询本月生日的员工姓名、生日。
SELECT ename,birthday
FROM t_employee
WHERE MONTH(CURDATE()) = MONTH(birthday);


#(10)#查询入职时间超过5年的
SELECT ename,hiredate,DATEDIFF(CURDATE(),hiredate) 
FROM t_employee
WHERE DATEDIFF(CURDATE(),hiredate)  > 365*5;


-- 抽取需要的日期时间值
SELECT EXTRACT(QUARTER FROM NOW());
-- 返回一年中第几天
SELECT DAYOFYEAR(NOW());-- 225
-- 天数 
SELECT DATEDIFF(NOW(),'1990-01-01');
-- 对日期新增
SELECT DATE_ADD(NOW(),INTERVAL 2 YEAR);
-- 格式化时间
SELECT DATE_FORMAT(NOW(),'%Y%m');

4、加密函数

列出了部分的加密函数。

函数 用法
password(str) 返回字符串str的加密版本,41位长的字符串(mysql8不再支持)
md5(str) 返回字符串str的md5值,也是一种加密方式(Message-Digest Algorithm 5,信息摘要算法第五版)
SHA(str) 返回字符串str的sha算法加密字符串,40位十六进制值的密码字符串 (Secure Hash Algorithm)
SHA2(str,hash_length) 返回字符串str的sha算法加密字符串,密码字符串的长度是hash_length/4。hash_length可以是224、256、384、512、0,其中0等同于256。
#加密函数
/*
当用户需要对数据进行加密时,
比如做登录功能时,给用户的密码加密等。
*/
#password函数在mysql8已经移除了
SELECT PASSWORD('123456');

#使用md5加密
SELECT MD5('123456'),SHA('123456'),sha2('123456',0);

SELECT CHAR_LENGTH(MD5('123456')),SHA('123456'),sha2('123456',0);


CREATE TABLE t_user(
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(20),
PASSWORD VARCHAR(100)
);

INSERT INTO t_user VALUES(NULL,'chai',MD5('123456'));

SELECT * FROM t_user 
WHERE username='chai' AND PASSWORD =MD5('123456');


SELECT * FROM t_user 
WHERE username='chai' AND PASSWORD ='123456';

5、系统信息函数

函数 用法
database() 返回当前数据库名
version() 返回当前数据库版本
user() 返回当前登录用户名
#其他函数
SELECT USER();
SELECT VERSION();
SELECT DATABASE();

6、条件判断函数

函数 功能
IF(value,t,f) 如果value是真,返回t,否则返回f
IFNULL(value1,value2) 如果value1不为空,返回value1,否则返回value2
CASE WHEN 条件1 THEN result1 WHEN 条件2 THEN result2 … ELSE resultn END 依次判断条件,哪个条件满足了,就返回对应的result,所有条件都不满足就返回ELSE的result。如果没有单独的ELSE子句,当所有WHEN后面的条件都不满足时则返回NULL值结果。等价于Java中if...else if....
CASE expr WHEN 常量值1 THEN 值1 WHEN 常量值2 THEN 值2 … ELSE 值n END 判断表达式expr与哪个常量值匹配,找到匹配的就返回对应值,都不匹配就返回ELSE的值。如果没有单独的ELSE子句,当所有WHEN后面的常量值都不匹配时则返回NULL值结果。等价于Java中switch....case
#条件判断函数
/*
这个函数不是筛选记录的函数,
而是根据条件不同显示不同的结果的函数。
*/
#如果薪资大于20000,显示高薪,否则显示正常
SELECT ename,salary,IF(salary>20000,'高薪','正常')
FROM t_employee;

#计算实发工资
#实发工资 = 薪资 + 薪资 * 奖金比例
SELECT ename,salary,commission_pct,
salary + salary * commission_pct
FROM t_employee;
#如果commission_pct是,计算完结果是NULL

SELECT ename,salary,commission_pct,
salary + salary * IFNULL(commission_pct,0) AS 实发工资
FROM t_employee;


SELECT ename,salary,commission_pct,
ROUND(salary + salary * IFNULL(commission_pct,0),2) AS 实发工资
FROM t_employee;

#查询员工编号,姓名,薪资,等级,等级根据薪资判断,
#如果薪资大于20000,显示“羡慕级别”,
#如果薪资15000-20000,显示“努力级别”,
#如果薪资10000-15000,显示“平均级别”
#如果薪资10000以下,显示“保底级别”
/*mysql中没有if...elseif函数,有case 函数。
等价于if...elseif 
*/
SELECT eid,ename,salary,
CASE WHEN salary>20000 THEN '羡慕级别'
     WHEN salary>15000 THEN '努力级别'
     WHEN salary>10000 THEN '平均级别'
     ELSE '保底级别'
END AS "等级"
FROM t_employee;  

#在“t_employee”表中查询入职7年以上的
#员工姓名、工作地点、轮岗的工作地点数量情况。
/*
计算工作地点的数量,转换为求 work_place中逗号的数量+1。
 work_place中逗号的数量 = work_place的总字符数 -  work_place去掉,的字符数
 work_place去掉, ,使用replace函数
*/
SELECT work_place, 
CHAR_LENGTH(work_place)-CHAR_LENGTH(REPLACE(work_place,',',''))
FROM t_employee;
 
 #类似于Java中switch...case
SELECT ename,work_place,
CASE (CHAR_LENGTH(work_place)-CHAR_LENGTH(REPLACE(work_place,',',''))+1)
WHEN 1 THEN '只在一个地方工作'
WHEN 2 THEN '在两个地方来回奔波'
WHEN 3 THEN '在三个地方流动'
ELSE '频繁出差'
END AS "工作地点数量情况"
FROM t_employee
WHERE DATEDIFF(CURDATE(),hiredate)  > 365*7;

7、窗口函数

窗口函数也叫OLAP函数(Online Anallytical Processing,联机分析处理),可以对数据进行实时分析处理。窗口函数是每条记录都会分析,有几条记录执行完还是几条,因此也属于单行函数。

函数分类 函数 功能描述
序号函数 ROW_NUMBER() 顺序排序,每行按照不同的分组逐行编号,例如:1,2,3,4
RANK() 并列排序,每行按照不同的分组进行编号,同一个分组中排序字段值出现重复值时,并列排序并跳过重复序号,例如:1,1,3
DENSE_RANK() 并列排序,每行按照不同的分组进行编号,同一个分组中排序字段值出现重复值时,并列排序不跳过重复序号,例如:1,1,2

窗口函数的语法格式如下

函数名([参数列表]) OVER ()
函数名([参数列表]) OVER (子句)

over关键字用来指定窗口函数的窗口范围。如果OVER后面是空(),则表示SELECT语句筛选的所有行是一个窗口。OVER后面的()中支持以下3种语法来设置窗口范围。

  • WINDOW:给窗口指定一个别名;
  • PARTITION BY子句:一个窗口范围还可以分为多个区域。按照哪些字段进行分区/分组,窗口函数在不同的分组上分别处理分析;
  • ORDER BY子句:按照哪些字段进行排序,窗口函数将按照排序后结果进行分析处理;
序号函数:
     row_number(): 顺序编号
     rank():并列编号 会跳过 1 1 3
     dense_rank():并列编号 不会跳过 1 1 2
     
    over(): 如果子句内什么都不写 那就是对整个结果集编号
    
    over([partition(篱笆) by 字段] [order by 排序 [desc]])
     排序 默认是正序 从小到大
          倒序 字段后 desc  
#(1)在“t_employee”表中查询薪资在[8000,10000]之间的员工姓名和薪资并给每一行记录编序号
SELECT ROW_NUMBER() OVER () AS "row_num",ename,salary
FROM t_employee WHERE salary BETWEEN 8000 AND 10000;

#(2)计算每一个部门的平均薪资与全公司的平均薪资的差值。
SELECT  did,AVG(salary) OVER() AS avg_all,
AVG(salary) OVER(PARTITION BY did) AS avg_did,
ROUND(AVG(salary) OVER()-AVG(salary) OVER(PARTITION BY did),2) AS deviation
FROM  t_employee;


#(3)在“t_employee”表中查询女员工姓名,部门编号,薪资,查询结果按照部门编号分组后在按薪资升序排列,并分别使用ROW_NUMBER()、RANK()、DENSE_RANK()三个序号函数给每一行记录编序号。
SELECT ename,did,salary,gender,
ROW_NUMBER() OVER (PARTITION BY did ORDER BY salary) AS "row_num",
RANK() OVER (PARTITION BY did ORDER BY salary) AS "rank_num" ,
DENSE_RANK() OVER (PARTITION BY did ORDER BY salary) AS "ds_rank_num" 
FROM t_employee WHERE gender='女';

#或

SELECT ename,did,salary,
ROW_NUMBER() OVER w AS "row_num",
RANK() OVER w AS "rank_num" ,
DENSE_RANK() OVER w AS "ds_rank_num" 
FROM t_employee WHERE gender='女'
WINDOW w AS (PARTITION BY did ORDER BY salary);


#(4)在“t_employee”表中查询每个部门最低3个薪资值的女员工姓名,部门编号,薪资值。
SELECT ROW_NUMBER() OVER () AS "rn",temp.*
FROM(SELECT ename,did,salary,
ROW_NUMBER() OVER w AS "row_num",
RANK() OVER w AS "rank_num" ,
DENSE_RANK() OVER w AS "ds_rank_num" 
FROM t_employee WHERE gender='女'
WINDOW w AS (PARTITION BY did ORDER BY salary))temp 
WHERE temp.rank_num<=3;

#或
SELECT ROW_NUMBER() OVER () AS "rn",temp.*
FROM(SELECT ename,did,salary,
ROW_NUMBER() OVER w AS "row_num",
RANK() OVER w AS "rank_num" ,
DENSE_RANK() OVER w AS "ds_rank_num" 
FROM t_employee WHERE gender='女'
WINDOW w AS (PARTITION BY did ORDER BY salary))temp 
WHERE temp.ds_rank_num<=3;


#(5)在“t_employee”表中查询每个部门薪资排名前3的员工姓名,部门编号,薪资值。
SELECT temp.*
FROM(SELECT ename,did,salary,
DENSE_RANK() OVER w AS "ds_rank_num" 
FROM t_employee
WINDOW w AS (PARTITION BY did ORDER BY salary DESC))temp 
WHERE temp.ds_rank_num<=3;

#(6)在“t_employee”表中查询全公司薪资排名前3的员工姓名,部门编号,薪资值。
SELECT temp.*
FROM(SELECT ename,did,salary,
DENSE_RANK() OVER w AS "ds_rank_num" 
FROM t_employee
WINDOW w AS (ORDER BY salary DESC))temp 
WHERE temp.ds_rank_num<=3;

关联查询(联合查询)

什么是关联查询

关联查询:两个或更多个表一起查询。

前提条件:这些一起查询的表之间是有关系的(一对一、一对多),它们之间一定是有关联字段,这个关联字段可能建立了外键,也可能没有建立外键。

关联查询结果分为几种情况

image-20211201000449563

关联查询的SQL有几种情况

1、内连接:inner join ... on

结果:A表 ∩ B表

2、左连接:A left join B on

(2)A表全部

(3)A表- A∩B

3、右连接:A right join B on

(4)B表全部

(5)B表-A∩B

4、全外连接:full outer join ... on,但是mysql不支持这个关键字,mysql使用union(合并)结果的方式代替

(6)A表∪B表: (2) A表结果 union (4)B表的结果

(7)A∪B - A∩B (3)A表- A∩B结果 union (5)B表-A∩B结果

/*
(1)凡是联合查询的两个表,必须有“关联字段”,
关联字段是逻辑意义一样,数据类型一样,名字可以一样也可以不一样的两个字段。
比如:t_employee (A表)中did和t_department(B表)中的did。

发现关联字段其实就是“可以”建外键的字段。当然联合查询不要求一定建外键。

(2)联合查询必须写关联条件,关联条件的个数 = n - 1.
n是联合查询的表的数量。
如果2个表一起联合查询,关联条件数量是1,
如果3个表一起联合查询,关联条件数量是2,
如果4个表一起联合查询,关联条件数量是3,
。。。。
否则就会出现笛卡尔积现象,这是应该避免的。

(3)关联条件可以用on子句编写,也可以写到where中。
但是建议用on单独编写,这样呢,可读性更好。

每一个join后面都要加on子句
A inner|left|right join  B on 条件
A inner|left|right join  B on 条件 inner|left|right jon C on 条件

1、内连接

#演示内连接,结果是A∩B
SELECT ename,t_department.did,dname
FROM t_employee INNER JOIN t_department 
ON t_employee.did = t_department.did;

SELECT *
FROM t_employee INNER JOIN t_department 
ON t_employee.did = t_department.did;

#查询部门编号为1的女员工的姓名、部门编号、部门名称、薪资等情况
SELECT ename,gender,t_department.did,dname,salary
FROM t_employee INNER JOIN t_department 
ON t_employee.did = t_department.did
WHERE t_department.did = 1 AND gender = '女';

#查询部门编号为1的员工姓名、部门编号、部门名称、薪资、职位编号、职位名称等情况
SELECT ename,gender,t_department.did,dname,salary,job_id,jname
FROM t_employee INNER JOIN t_department ON t_employee.did = t_department.did
 INNER JOIN t_job ON t_employee.`job_id` = t_job.`jid`
WHERE t_department.did = 1;

2、左连接

#演示左连接
/*
(2)A
(3)A-A∩B
*/
/*
观察数据:
t_employee  看成A表
t_department 看成B表
此时t_employee (A表)中有 李红和周洲的did是NULL,没有对应部门,
    t_department(B表)中有 测试部,在员工表中找不到对应记录的。
*/
#查询所有员工,包括没有指定部门的员工,他们的姓名、薪资、部门编号、部门名称
SELECT ename,salary,t_department.did,dname
FROM t_employee LEFT JOIN t_department
ON t_employee.did = t_department.did;
#查询的是A结果  A left join B

#查询没有部门的员工信息
SELECT ename,salary,t_department.did,dname
FROM t_employee LEFT JOIN t_department
ON t_employee.did = t_department.did
WHERE t_employee.did IS NULL;
#查询的结果是A - A∩B
#此时的where条件,建议写子表的关联字段is null,这样更准确一点。
#如果要建外键,它们肯定有子表和父表的角色,写子表的关联字段is null
#因为父表中这个字段一般是主键,不会为null。

3、右连接

/*
右连接
(4)B
(5)B - A∩B
*/
#演示右连接
/*
观察数据:
t_employee  看成A表
t_department 看成B表
此时t_employee (A表)中有 李红和周洲的did是NULL,没有对应部门,
    t_department(B表)中有 测试部,在员工表中找不到对应记录的。
*/
#查询所有部门,包括没有对应员工的部门,他们的姓名、薪资、部门编号、部门名称
SELECT ename,salary,t_department.did,dname
FROM t_employee RIGHT JOIN t_department
ON t_employee.did = t_department.did;
#查询的是B结果  A RIGHT join B

#查询没有员工部门的信息
SELECT ename,salary,t_department.did,dname
FROM t_employee RIGHT JOIN t_department
ON t_employee.did = t_department.did
WHERE t_employee.did IS NULL;
#查询的结果是B - A∩B
#此时的where条件,建议写子表的关联字段is null,这样更准确一点。
#如果要建外键,它们肯定有子表和父表的角色,写子表的关联字段is null
#因为父表中这个字段一般是主键,不会为null。



#查询所有员工,包括没有指定部门的员工,他们的姓名、薪资、部门编号、部门名称
SELECT ename,salary,t_department.did,dname
FROM t_department RIGHT JOIN t_employee
ON t_employee.did = t_department.did;
#查询的是B结果  A RIGHT join B

#查询没有部门的员工信息
SELECT ename,salary,t_department.did,dname
FROM t_department RIGHT JOIN t_employee
ON t_employee.did = t_department.did
WHERE t_employee.did IS NULL;
#查询的结果是B - A∩B  A right join B
#此时的where条件,建议写子表的关联字段is null,这样更准确一点。
#如果要建外键,它们肯定有子表和父表的角色,写子表的关联字段is null
#因为父表中这个字段一般是主键,不会为null。

4、union

/*
union实现
(6)A∪B
(7)A∪B - A∩B
    A-A∩B  ∪  B-A∩B
*/
#演示用union合并两个查询结果实现A∪B 和A∪B - A∩B
/*
union合并时要注意:
(1)两个表要查询的结果字段是一样的
(2)UNION ALL表示直接合并结果,如果有重复的记录一并显示
   ALL去掉表示合并结果时,如果有重复记录,去掉。
(3)要实现A∪B的结果,那么必须是合并 查询是A表结果和查询是B表结果的select语句。
同样要实现A∪B - A∩B的结果,那么必须是合并查询是A-A∩B结果和查询是B-A∩B的select语句。
*/

#查询所有员工和所有部门,包括没有指定部门的员工和没有分配员工的部门。
SELECT *
FROM t_employee LEFT JOIN t_department
ON t_employee.did = t_department.did

UNION 

SELECT *
FROM t_employee RIGHT JOIN t_department
ON t_employee.did = t_department.did;


#以下union会报错
SELECT * FROM t_employee
UNION
SELECT * FROM t_department;
/*
错误代码: 1222
The used SELECT statements have a different number of columns
两个Select语句的列数是不同的。

column:列,表中的字段。
columns:很多的字段,即字段列表
select 字段列表 from 表名称;
*/


#联合 查询结果是A表的select 和查询结果是A∩B的select语句,是得不到A∪B
SELECT *
FROM t_employee LEFT JOIN t_department
ON t_employee.did = t_department.did

UNION 

SELECT *
FROM t_employee INNER JOIN t_department
ON t_employee.did = t_department.did;

#查询那些没有分配部门的员工和没有指定员工的部门,即A表和B表在对方那里找不到对应记录的数据。
SELECT *
FROM t_employee LEFT JOIN t_department
ON t_employee.did = t_department.did
WHERE t_employee.did IS NULL

UNION 

SELECT *
FROM t_employee RIGHT JOIN t_department
ON t_employee.did = t_department.did
WHERE t_employee.did IS NULL;

自连接

#演示特殊的联合查询/关联查询/多表查询:自连接
#物理上,是一张表
#逻辑上,是两张表
/*
分析表结构:t_employee表
mid:是表示存储员工的领导编号。即该员工归谁管。领导编号其实就是“领导”作为员工身份的员工编号
   例如:eid为3的员工邓超远,他的mid是7,表示他的领导是员工编号为7的员工。
   eid为7的员工是贾宝玉,他的eid是7,贾宝玉作为员工来说,他的编号是7,作为领导来说,他的编号也是7。

mid的取值范围受到eid字段的限制。mid的值选择必须是eid现有值范围。

可以理解为mid和eid是关联字段,如果要建外键,可以在mid字段上建外键。
foreign key(mid) references t_employee(eid)   

此时t_employee既是子表也是父表。
员工表t_employee建立了外键:
CONSTRAINT `t_employee_ibfk_3` FOREIGN KEY (`mid`) REFERENCES `t_employee` (`eid`) ON DELETE SET NULL ON UPDATE CASCADE
*/
#查询每一个员工自己的编号、名字、薪资和他的领导的编号、姓名、薪资。
SELECT emp.eid,emp.ename,emp.salary,  mgr.eid,mgr.ename,mgr.salary
FROM t_employee AS emp INNER JOIN t_employee AS mgr
ON emp.mid = mgr.eid; 
#把t_employee当成两张表,通过取别名的方式
#t_employee AS emp 把员工表 当成员工表
# t_employee AS mgr 把员工表  当成存储领导信息的领导表
#emp.mid = mgr.eid; 员工表的领导编号就是领导表的员工编号

注意:

  1. 如果两张表有同名字段 必须指定此字段来自于那张表
  2. 进行关联查询时会产生笛卡尔集 指定关联条件 去除 笛卡尔集

using(id) <===> on A.id = B.id

select的7大子句

7大子句顺序

(1)from:从哪些表中筛选

(2)inner|left|right ... on:关联多表查询时,去除笛卡尔积

(3)where:从表中筛选的条件

(4)group by:分组依据

(5)having:在分组统计结果中再次筛选(with rollup)

(6)order by:排序

(7)limit:分页

必须按照(1)-(7)的顺序编写子句。

from子句

#1、from子句
SELECT * 
FROM t_employee; #表示从某个表中筛选数据

on子句

#2、on子句
/*
(1)on必须配合join使用
(2)on后面只写关联条件
所谓关联条件是两个表的关联字段的关系
(3)有n张表关联,就有n-1个关联条件
两张表关联,就有1个关联条件
三张表关联,就有2个关联条件
*/
SELECT *
FROM t_employee INNER JOIN t_department
ON t_employee.did = t_department.did;  #1个关联条件

#查询员工的编号,姓名,职位编号,职位名称,部门编号,部门名称
#需要t_employee员工表,t_department部门表,t_job职位表
SELECT eid,ename,t_job.job_id,t_job.job_name, `t_department`.`did`,`t_department`.`dname`
FROM t_employee INNER JOIN t_department INNER JOIN t_job
ON t_employee.did = t_department.did AND t_employee.job_id = t_job.job_id;

where子句

#3、where子句,在查询结果中筛选
#查询女员工的信息,以及女员工的部门信息
SELECT *
FROM t_employee INNER JOIN t_department
ON t_employee.did = t_department.did
WHERE gender = '女';

group by子句

#4、group by分组
#查询所有员工的平均薪资
SELECT AVG(salary) FROM t_employee;

#查询每一个部门的平均薪资
SELECT did,ROUND(AVG(salary),2 )
FROM t_employee
GROUP BY did;

#查询每一个部门的平均薪资,显示部门编号,部门的名称,该部门的平均薪资
SELECT t_department.did,dname,ROUND(AVG(salary),2 )
FROM t_department LEFT JOIN t_employee
ON t_department.did = t_employee.did
GROUP BY t_department.did;

#查询每一个部门的平均薪资,显示部门编号,部门的名称,该部门的平均薪资
#要求,如果没有员工的部门,平均薪资不显示null,显示0
SELECT t_department.did,dname,IFNULL(ROUND(AVG(salary),2),0)
FROM t_department LEFT JOIN t_employee
ON t_department.did = t_employee.did
GROUP BY t_department.did;
问题1:合计,WITH ROLLUP,加在group by后面
#问题1:合计,WITH ROLLUP,加在group by后面
#按照部门统计人数
SELECT did, COUNT(*) FROM t_employee GROUP BY did;
#按照部门统计人数,并合计总数
SELECT did, COUNT(*) FROM t_employee GROUP BY did WITH ROLLUP;
SELECT IFNULL(did,'合计'), COUNT(*) FROM t_employee GROUP BY did WITH ROLLUP;
SELECT IFNULL(did,'合计') AS "部门编号" , COUNT(*)  AS "人数" FROM t_employee GROUP BY did WITH ROLLUP;

问题2:是否可以按照多个字段分组统计
#问题2:是否可以按照多个字段分组统计
#按照不同的部门,不同的职位,分别统计男和女的员工人数
SELECT did, job_id, gender, COUNT(*)
FROM t_employee 
GROUP BY did, job_id, gender;
问题3:分组统计时,select后面字段列表的问题
#问题4:分组统计时,select后面字段列表的问题
SELECT eid,ename, did, COUNT(*) FROM t_employee; 
#eid,ename, did此时和count(*),不应该出现在select后面

SELECT eid,ename, did, COUNT(*) FROM t_employee GROUP BY did; 
#eid,ename此时和count(*),不应该出现在select后面

SELECT  did, COUNT(*) FROM t_employee GROUP BY did;
#分组统计时,select后面只写和分组统计有关的字段,其他无关字段不要出现,否则会引起歧义

having子句

#5、having
/*
having子句也写条件
where的条件是针对原表中的记录的筛选。where后面不能出现分组函数。
having子句是对统计结果(分组函数计算后)的筛选。having可以加分组函数。
*/
#查询每一个部门的女员工的平均薪资,显示部门编号,部门的名称,该部门的平均薪资
#要求,如果没有员工的部门,平均薪资不显示null,显示0
#最后只显示平均薪资高于12000的部门信息
SELECT t_department.did,dname,IFNULL(ROUND(AVG(salary),2),0)
FROM t_department LEFT JOIN t_employee
ON t_department.did = t_employee.did
WHERE gender = '女'
GROUP BY t_department.did
HAVING IFNULL(ROUND(AVG(salary),2),0) >12000;


#查询每一个部门的男和女员工的人数
SELECT did,gender,COUNT(*)
FROM t_employee
GROUP BY did,gender;

#查询每一个部门的男和女员工的人数,显示部门编号,部门的名称,性别,人数
SELECT t_department.did,dname,gender,COUNT(eid)
FROM t_employee RIGHT JOIN t_department
ON t_employee.did = t_department.did
GROUP BY t_department.did,gender;

#查询每一个部门薪资超过10000的男和女员工的人数,显示部门编号,部门的名称,性别,人数
#只显示人数低于3人
SELECT t_department.did,dname,gender,COUNT(eid)
FROM t_employee RIGHT JOIN t_department
ON t_employee.did = t_department.did
WHERE salary > 10000
GROUP BY t_department.did,gender
HAVING COUNT(eid) < 3;

order by子句

#6、排序 order by
/*
升序和降序,默认是升序
asc代表升序
desc 代表降序
*/
#查询员工信息,按照薪资从高到低
SELECT * FROM t_employee
ORDER BY salary DESC;

#查询每一个部门薪资超过10000的男和女员工的人数,显示部门编号,部门的名称,性别,人数
#只显示人数低于3人,按照人数升序排列
SELECT t_department.did,dname,gender,COUNT(eid)
FROM t_employee RIGHT JOIN t_department
ON t_employee.did = t_department.did
WHERE salary > 10000
GROUP BY t_department.did,gender
HAVING COUNT(eid) < 3
ORDER BY COUNT(eid);


#查询员工的薪资,按照薪资从低到高,薪资相同按照员工编号从高到低
SELECT *
FROM t_employee
ORDER BY salary ASC , eid DESC;

limit子句

#演示limit子句
/*
limit子句是用于分页显示结果。
limit m,n
n:表示最多该页显示几行
m:表示从第几行开始取记录,第一个行的索引是0
m = (page-1)*n  page表示第几页

每页最多显示5条,n=5
第1页,page=1,m = (1-1)*5 = 0;  limit 0,5
第2页,page=2,m = (2-1)*5 = 5;  limit 5,5
第3页,page=3,m = (3-1)*5 = 10;  limit 10,5
*/
#查询员工表的数据,分页显示,每页显示5条记录
#第1页
SELECT * FROM t_employee LIMIT 0,5;
#第2页
SELECT * FROM t_employee LIMIT 5,5;
#第3页
SELECT * FROM t_employee LIMIT 10,5;
#第4页
SELECT * FROM t_employee LIMIT 15,5;
#第5页
SELECT * FROM t_employee LIMIT 20,5;
#第6页
SELECT * FROM t_employee LIMIT 25,5;


#查询所有的男员工信息,分页显示,每页显示3条,第2页
#limit m,n  n=3,page=2,m=(page-1)*n=3
SELECT *
FROM t_employee
WHERE gender ='男'
LIMIT 3,3

#查询每一个编号为偶数的部门,显示部门编号,名称,员工数量,
#只显示员工数量>=2的结果,按照员工数量升序排列,
#每页显示2条,显示第1页
SELECT t_department.did,dname,COUNT(eid)
FROM t_employee RIGHT JOIN t_department
ON t_employee.did = t_department.did
WHERE t_department.did%2=0
GROUP BY t_department.did
HAVING COUNT(eid)>=2
ORDER BY COUNT(eid)
LIMIT 0,2;

子查询

SELECT的SELECT中嵌套子查询

/*
子查询:嵌套在另一个SQL语句中的查询。
SELECT语句可以嵌套在另一个SELECT中,UPDATE,DELETE,INSERT,CREATE语句等。

(1)SELECT的SELECT中嵌套子查询
*/

#(1)在“t_employee”表中查询每个人薪资和公司平均薪资的差值,
#并显示员工薪资和公司平均薪资相差5000元以上的记录。
SELECT ename AS "姓名",
	salary AS "薪资",
 ROUND((SELECT AVG(salary) FROM t_employee),2) AS "全公司平均薪资",
 ROUND(salary-(SELECT AVG(salary) FROM t_employee),2) AS "差值"
FROM t_employee
WHERE ABS(ROUND(salary-(SELECT AVG(salary) FROM t_employee),2))>5000;

#(2)在“t_employee”表中查询每个部门平均薪资和公司平均薪资的差值。
SELECT did,AVG(salary),
AVG(salary)-(SELECT AVG(salary) FROM t_employee)
FROM t_employee
GROUP BY did;

SELECT的WHERE或HAVING中嵌套子查询

当子查询结果作为外层另一个SQL的过滤条件,通常把子查询嵌入到WHERE或HAVING中。根据子查询结果的情况,分为如下三种情况。

  • 当子查询的结果是单列单个值,那么可以直接使用比较运算符,如“<”、“<=”、“>”、“>=”、“=”、“!=”等与子查询结果进行比较。
  • 当子查询的结果是单列多个值,那么可以使用比较运算符IN或NOT IN进行比较。
  • 当子查询的结果是单列多个值,还可以使用比较运算符, 如“<”、“<=”、“>”、“>=”、“=”、“!=”等搭配ANY、ALL等关键字与查询结果进行比较。
/*
子查询嵌套在where后面。
在where或having后面的子查询结果是:
(1)单个值,那么可以用=,>,<,>=,<=,!=这样的运算符和子查询的结果做比较
(2)多个值,那么需要用in,not in, >all,>any....形式做比较
 如“<”、“<=”、“>”、“>=”、“=”、“!=”等搭配ANY、SOME、ALL等关键字与查询结果进行比较

*/
#(1)在“t_employee”表中查询薪资最高的员工姓名(ename)和薪资(salary)。
#SELECT ename,MAX(salary) FROM t_employee;#错误
#取表中第一行员工的姓名和全公司最高的薪资值一起显示。

SELECT ename,salary
FROM t_employee
WHERE salary = (SELECT MAX(salary) FROM t_employee);

#(2)在“t_employee”表中查询比全公司平均薪资高的男员工姓名和薪资。
SELECT ename,salary
FROM t_employee
WHERE salary > (SELECT AVG(salary) FROM t_employee) AND gender = '男';

#(3)在“t_employee”表中查询和“白露”,“谢吉娜”同一部门的员工姓名和电话。
SELECT ename,tel,did
FROM t_employee
WHERE did IN(SELECT did FROM t_employee WHERE ename='白露' || ename='谢吉娜');

SELECT ename,tel,did
FROM t_employee
WHERE did =ANY(SELECT did FROM t_employee WHERE ename='白露' || ename='谢吉娜');


#(4)在“t_employee”表中查询薪资比“白露”,“李诗雨”,“黄冰茹”三个人的薪资都要高的员工姓名和薪资。
SELECT ename,salary
FROM t_employee
WHERE salary >ALL(SELECT salary FROM t_employee WHERE ename IN('白露','李诗雨','黄冰茹'));


#(5)查询“t_employee”和“t_department”表,按部门统计平均工资,
#显示部门平均工资比全公司的总平均工资高的部门编号、部门名称、部门平均薪资,
#并按照部门平均薪资升序排列。
SELECT t_department.did,dname,AVG(salary)
FROM t_employee RIGHT JOIN t_department
ON t_employee.did = t_department.did
GROUP BY t_department.did
HAVING AVG(salary) >(SELECT AVG(salary) FROM t_employee)
ORDER BY AVG(salary);

SELECT中的EXISTS型子查询

EXISTS型子查询也是存在外层SELECT的WHERE子句中,不过它和上面的WHERE型子查询的工作模式不相同,所以这里单独讨论它。

如果EXISTS关键字后面的参数是一个任意的子查询,系统将对子查询进行运算以判断它是否返回行,如果至少返回一行,那么EXISTS的结果为true,此时外层查询语句将进行查询;如果子查询没有返回任何行,那么EXISTS的结果为false,此时外层查询语句不进行查询。EXISTS和NOT EXISTS的结果只取决于是否返回行,而不取决于这些行的内容,所以这个子查询输入列表通常是无关紧要的。

如果EXISTS关键字后面的参数是一个关联子查询,即子查询的WHERE条件中包含与外层查询表的关联条件,那么此时将对外层查询表做循环,即在筛选外层查询表的每一条记录时,都看这条记录是否满足子查询的条件,如果满足就再用外层查询的其他WHERE条件对该记录进行筛选,否则就丢弃这行记录。

#exist型子查询
/*
(1)exists()中的子查询和外面的查询没有联合的情况下,
如果exists()中的子查询没有返回任何行,那么外面的子查询就不查了。
(2)exists()中的子查询与外面的查询有联合工作的情况下,
循环进行把外面查询表的每一行记录的值,代入()中子查询,如果可以查到结果,
就留下外面查询的这条记录,否则就舍去。
*/

#(1)查询“t_employee”表中是否存在部门编号为NULL的员工,
#如果存在,查询“t_department”表的部门编号、部门名称。
SELECT * FROM t_department 
WHERE EXISTS(SELECT * FROM t_employee  WHERE did IS NULL);

#(2)查询“t_department”表是否存在与“t_employee”表相同部门编号的记录,
#如果存在,查询这些部门的编号和名称。
SELECT * FROM t_department
WHERE EXISTS(SELECT * FROM t_employee WHERE t_employee.did = t_department.did);

#查询结果等价于下面的sql
SELECT DISTINCT t_department.*
FROM t_department INNER JOIN t_employee
ON t_department.did = t_employee.did;

SELECT的FROM中嵌套子查询

当子查询结果是多列的结果时,通常将子查询放到FROM后面,然后采用给子查询结果取别名的方式,把子查询结果当成一张“动态生成的临时表”使用。

#子查询嵌套在from后面
/*
当一个查询要基于另一个查询结果来筛选的时候,
另一个查询还是多行多列的结果,那么就可以把这个查询结果当成一张临时表,
放在from后面进行再次筛选。

*/

#(1)在“t_employee”表中,查询每个部门的平均薪资,
#然后与“t_department”表联合查询
#所有部门的部门编号、部门名称、部门平均薪资。

SELECT did,AVG(salary) FROM t_employee GROUP BY did;

+------+-------------+
| did  | AVG(salary) |
+------+-------------+
|    1 |  11479.3125 |
|    2 |       13978 |
|    3 |    37858.25 |
|    4 |       12332 |
|    5 |       11725 |
+------+-------------+
5 ROWS IN SET (0.00 sec)

#用上面的查询结果,当成一张临时表,与t_department部门表做联合查询
#要给这样的子查询取别名的方式来当临时表用,不取别名是不可以的。
#而且此时的别名不能加""
#字段的别名可以加"",表的别名不能加""

SELECT t_department.did ,dname,AVG(salary)
FROM t_department LEFT JOIN (SELECT did,AVG(salary) FROM t_employee GROUP BY did) temp
ON t_department.did = temp.did;
#错误,from后面的t_department和temp表都没有salary字段,
#SELECT t_department.did ,dname,AVG(salary)出现AVG(salary)是错误的

SELECT t_department.did ,dname,pingjun
FROM t_department LEFT JOIN (SELECT did,AVG(salary) AS pingjun FROM t_employee GROUP BY did) temp
ON t_department.did = temp.did;


#(2)在“t_employee”表中查询每个部门中薪资排名前2的员工姓名、部门编号和薪资。
SELECT * FROM (
SELECT ename,did,salary,
DENSE_RANK() over (PARTITION BY did ORDER BY salary DESC) AS paiming
FROM t_employee) temp
WHERE temp.paiming <=2;

行列转置

单表

DROP TABLE IF EXISTS `temp`;
CREATE TABLE `temp`  (
  `sid` int NOT NULL,
  `sname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `course` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `score` int NULL DEFAULT NULL,
  PRIMARY KEY (`sid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of temp
-- ----------------------------
INSERT INTO `temp` VALUES (1, '张三', '语文', 89);
INSERT INTO `temp` VALUES (2, '张三', '数学', 96);
INSERT INTO `temp` VALUES (3, '张三', '英语', 75);
INSERT INTO `temp` VALUES (4, '李四', '语文', 88);
INSERT INTO `temp` VALUES (5, '李四', '数学', 99);
select * from temp;
1722826867022
/*
不够完美
select sid,sname,
   case when course='语文' then score end as "语文",
   CASE WHEN course='数学' THEN score END AS "数学",
   CASE WHEN course='英语' THEN score END AS "英语"
from temp;*/

SELECT sid,sname,
   SUM(CASE WHEN course='语文' THEN score END) AS "语文",
   SUM(CASE WHEN course='数学' THEN score END) AS "数学",
   SUM(CASE WHEN course='英语' THEN score END) AS "英语"
FROM temp
GROUP BY sname;

1722826940411

多表(把联表查询的结果当作虚拟表再转置)

#学生表
drop table if exists t_stu;
create table t_stu(
	sid int primary key,
	sname varchar(20)
);

#添加学生信息
insert into t_stu values(1,'张三');
insert into t_stu values(2,'李四');

#课程表
drop table if exists t_course;
create table t_course(
	cid int primary key,
	cname varchar(50)
);

#添加课程信息
insert into t_course values(1,'java');
insert into t_course values(2,'mysql');
    
#成绩表
drop table if exists t_score;
create table t_score(
    id int primary key auto_increment,
	sid int ,
	cid int ,
	score int
);

insert into t_score values(null,1,1,89);
insert into t_score values(null,2,1,100);
insert into t_score values(null,1,2,79);
#查询每个人的每门课的成绩
SELECT sid,sname,
	SUM(CASE WHEN cname='java' THEN score END) AS "java",
	SUM(CASE WHEN cname='mysql' THEN score END) AS "mysql"
FROM 	
(SELECT t_stu.sid,sname,t_course.cid,cname,score
FROM t_stu INNER JOIN t_score ON t_stu.sid = t_score.sid
   INNER JOIN t_course ON t_course.cid = t_score.cid
ORDER BY t_stu.sid) temp
GROUP BY sid
ORDER BY sid;

MySQL支持的数据类型

数值类型:包括整数和小数

数值类型主要用来存储数字,不同的数值类型提供不同的取值范围,可以存储的值范围越大,所需要的存储空间也越大。MySQL支持所有标准SQL中的数值类型,其中包括严格数据类型(INTEGER、SMALLINT、DECIMAL、NUMERIC)和近似数值类型(FLOAT、REAL、DOUBLE PRECISION)。MySQL还扩展了TINYINT、MEDIUMINT和BIGINT等3种不同长度的整数类型,并增加了BIT类型,用来存储位数据。

对于MySQL中的数值类型,还要做如下说明:

  • 关键字INT是INTEGER的同义词。

  • 关键字DEC和FIXED是DECIMAL的同义词。

  • NUMERIC和DECIMAL类型被视为相同的数据类型。

  • DOUBLE视为DOUBLE PRECISION的同义词,并在REAL_AS_FLOAT SQL模式未启用的情况下,将REAL也视为DOUBLE PRECISION的同义词。

整数类型

image-20211127205706588

说明:

int(M)这个M在字段的属性中指定了unsigned(无符号)和zerofill(零填充)的情况下才有意义。表示当整数值不够M位时,用0填充。

create table t_int(
	i1 int,
	i2 int(2)  #没有unsigned zerofill,(2)没有意义
);

bit类型

bit类型,如果没有指定(M),默认是1位。这个1位,那么表示只能存1位的二进制值。这里(M)是表示二进制的位数。M范围从1到64。

对于位类型字段,之前版本直接使用SELECT语句将不会看到结果,而在MySQL8版本中默认以“0X”开头的十六进制形式显示,可以通过BIN()函数显示为二进制格式。

#创建一个表格
create table t_bit(
	b1 bit,  #没有指定(M),默认是1位二进制
	b2 bit(4) #能够存储4位二进制0000~1111
);

小数类型

MySQL中使用浮点数和定点数来表示小数。浮点数有两种类型:单精度浮点数(FLOAT)和双精度浮点数(DOUBLE),定点数只有DECIMAL。浮点数和定点数都可以用(M,D)来表示。

  • M是精度,表示该值总共显示M位,包括整数位和小数位,对于FLOAT和DOUBLE类型来说,M取值范围为0255,而对于DECIMAL来说,M取值范围为065。
  • D是标度,表示小数的位数,取值范围为0~30,同时必须<=M。

FLOAT和DOUBLE类型在不指定(M,D)时,默认会按照实际的精度来显示。DECIMAL类型在不指定(M,D)时,默认为(10,0),即只保留整数部分。例如,定义DECIMAL(5,2)的类型,表示该列取值范围是-999.99~999.99。如果用户插入数据的小数部分位数超过D位,MySQL会四舍五入处理,但是如果用户插入数据的整数部分位数超过“M-D”位,则会报“Out of range”的错误。

DECIMAL实际是以字符串形式存放的,在对精度要求比较高的时候(如货币、科学数据等)使用DECIMAL类型会比较好。浮点数相对于定点数的优点是在长度一定的情况下,浮点数能够表示更大的数据范围,它的缺点是会引起精度问题。

/*
double 没有写宽度 能存多少存多少
   double(5,2) 一共最多有5位 2位小数 3位整数
   
   decimal: 指定小数位数最多30位
   decimal:没有写宽度 只保留整数 对小数进行四舍五入
           写了宽度 数据不满足宽度 会补0   
 */

#创建表格
create table t_double(
	d1 double,
	d2 double(5,2)  #-999.99~999.99 两位小数,三位整数
);

#创建表格
create table t_decimal(
	d1 decimal,  #没有指定(M,D)默认是(10,0)
	d2 decimal(5,2)
);
#添加数据  decimal没有指定范围会四舍五入展示整数
insert into t_decimal values(2.5,2.5);  # 3  2.50

字符串类型

MySQL的字符串类型有CHAR、VARCHAR、BINARY、VARBINARY、BLOB、TEXT、ENUM、SET等。MySQL的字符串类型可以用来存储文本字符串数据,还可以存储二进制字符串。

文本字符串类型:

image-20211127210547156

二进制字符串类型:

image-20211127210608685

char和varchar

CHAR(M)为固定长度的字符串, M表示最多能存储的字符数,取值范围是0~255个字符,如果未指定(M)表示只能存储1个字符。例如CHAR(4)定义了一个固定长度的字符串列,其包含的字符个数最大为4,如果存储的值少于4个字符,右侧将用空格填充以达到指定的长度,当查询显示CHAR值时,尾部的空格将被删掉。

create table temp(
	c1 char,   #默认是一个字符
    c2 char(3) 
);

VARCHAR(M)为可变长度的字符串,M表示最多能存储的字符数,M的范围由最长的行的大小(通常是65535)和使用的字符集确定。

create table temp(
	name varchar  #错误
	name varchar(3)  #最多不超过3个字符
);

image-20211127210826463

Enum和Set类型

无论是数值类型、日期类型、普通的文本类型,可取值的范围都非常大,但是有时候我们指定在固定的几个值范围内选择一个或多个,那么就需要使用ENUM枚举类型和SET集合类型了。比如性别只有“男”或“女”;上下班交通方式可以有“地铁”、“公交”、“出租车”、“自行车”、“步行”等。枚举和集合类型字段声明的语法格式如下:

字段名ENUM(‘值1’,‘值2’,…‘值n’)

字段名 SET(‘值1’,‘值2’,…‘值n’)

ENUM类型的字段在赋值时,只能在指定的枚举列表中取值,而且一次只能取一个。枚举列表最多可以有65535个成员。ENUM值在内部用整数表示,每个枚举值均有一个索引值, MySQL存储的就是这个索引编号。例如,定义ENUM类型的列(‘first’, ‘second’, ‘third’)。

image-20211127211743925

SET类型的字段在赋值时,可从定义的值列表中选择1个或多个值的组合。SET列最多可以有64个成员。SET值在内部也用整数表示,分别是1,2,4,8……,都是2的n次方值,因为这些整数值对应的二进制都是只有1位是1,其余是0。

image-20211127211806889

演示枚举类型:

create table temp(
	gender enum('男','女'),
    hobby set('睡觉','打游戏','泡妞','写代码')
);
insert into temp values('男','睡觉,打游戏'); #成功

insert into temp values('男,女','睡觉,打游戏'); #失败
#ERROR 1265 (01000): Data truncated for column 'gender' at row 1

insert into temp values('妖','睡觉,打游戏');#失败
ERROR 1265 (01000): Data truncated for column 'gender' at row 1

insert into temp values('男','睡觉,打游戏,吃饭');
ERROR 1265 (01000): Data truncated for column 'hobby' at row 1


insert into t_enum_set
values(2, 5); # 女 , 睡觉,打代码
#5 可以看出 1和4的组合,00001 和 0100,0101

BINARY和VARBINARY类型

BINARY和VARBINARY类似于CHAR和VARCHAR,只是它们存储的是二进制字符串。

BINARY (M)为固定长度的二进制字符串,M表示最多能存储的字节数,取值范围是0~255个字节,如果未指定(M)表示只能存储1个字节。例如BINARY (8),表示最多能存储8个字节,如果字段值不足(M)个字节,将在右边填充'\0'以补齐指定长度。

VARBINARY (M)为可变长度的二进制字符串,M表示最多能存储的字节数,总字节数不能超过行的字节长度限制65535,另外还要考虑额外字节开销,VARBINARY类型的数据除了存储数据本身外,还需要1或2个字节来存储数据的字节数。VARBINARY类型和VARCHAR类型一样必须指定(M),否则报错。

create table t_binary(
	b1 binary, #没有指定(M),默认是(1)
	b2 varbinary #没有指定(M),报错,必须指定(M)
);

create table t_binary(
	b1 binary, #默认(1),最多能存储一个字节
	b2 binary(6), #最多能存储6个字节,不够6个用\u0000补全
	b3 varbinary(6) #(6),最多能存储6个字节
)

BLOB和TEXT类型

BLOB是一个二进制大对象,用来存储可变数量的二进制字符串,分为TINYBLOB、BLOB、MEDIUMBLOB、LONGBLOB四种类型。TINYTEXT、TEXT、MEDIUMTEXT和LONGTEXT四种文本类型,它们分别对应于以上四种BLOB类型,具有相同的最大长度和存储要求。

BLOB类型与TEXT类型的区别如下:

(1)BLOB类型存储的是二进制字符串,TEXT类型存储的是文本字符串。BLOB类型还可以存储图片和声音等二进制数据。

(2)BLOB类型没有字符集,并且排序和比较基于列值字节的数值,TEXT类型有一个字符集,并且根据字符集对值进行排序和比较。

#演示blob和text
#blob系列是大的二进制数据类型
#text系列是大的文本字符串类型
#创建表格
create table t_blob_text(
	b blob,
	t text
);

日期时间类型

image-20211127212047635

/*year 年
  date 年月日
  time 时分秒
  datetime 年月日时分秒
  timestamp: 年月日时分秒 随着时区的变化而变化*/
CREATE TABLE t_date(
  y1 YEAR,
  d1 DATE,
  t1 TIME,
  dt DATETIME,
  tt TIMESTAMP
);

-- 更改时区
SET time_zone='+8:00';

其他类型

1、JSON类型

在MySQL5.7之前,如果需要在数据库中存储JSON数据只能使用VARCHAR或TEXT字符串类型。从5.7.8版本之后开始支持JSON数据类型。

2、空间类型

MySQL 空间类型扩展支持地理特征的生成、存储和分析。这里的地理特征表示世界上具有位置的任何东西,可以是一个实体,例如一座山;可以是空间,例如一座办公楼;也可以是一个可定义的位置,例如一个十字路口等等。现在的应用程序开发中空间数据的存储越来越多了,例如,钉钉的打卡位置是否在办公区域范围内,滴滴打车的位置、路线等。MySQL提供了非常丰富的空间函数以支持各种空间数据的查询和处理。

DDL

虽说图形界面工具可以直接创建库、创建表、修改库和表的结构,但是如果批量修改,还是编写DDL语句的脚本,执行脚本更快更方便。

和数据库相关的

创建数据库

create database 数据库名;

查看所有的数据库

show databases; #有一个s,代表多个数据库

查看某个数据库的详细定义语句

show create database 数据库名;
show create database 数据库名\G

修改数据库编码

#修改数据库字符集和校对规则
ALTER DATABASE 数据库名称 CHARACTER SET 字符集名称 COLLATE 字符集对应校对规则;
ALTER DATABASE atguigu_chapter3_two CHARACTER SET utf8 COLLATE utf8_general_ci;

注意,修改数据库编码只会影响之后创建的表的默认编码,之前创建的表不会受影响。

删除数据库

drop database 数据库名;

使用数据库

use 数据库名;

SQL示例演示

#演示和数据库相关的DDL语句
#查看当前登录用户(有权限)查看的所有的数据库
show databases;

#如何创建数据库
create database 数据库名称;

#例如,创建数据库bookstore
create database bookstore;

#查看数据库的定义
show create database 数据库名;

#例如,查看bookstore数据库的定义信息
show create database bookstore;
show create database bookstore\G

*************************** 1. row ***************************
       Database: bookstore
Create Database: CREATE DATABASE `bookstore` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */ /*!80016 DEFAULT ENCRYPTION='N' */
1 row in set (0.00 sec)

#修改数据库的编码字符集和校对规则
alter database 数据库名称 CHARACTER SET 字符集名称 COLLATE 校对规则

#修改bookstore数据库的字符集和校对规则
alter database bookstore CHARACTER SET Latin1 COLLATE Latin1_general_ci;
#Latin1字符集不支持中文

mysql> show create database bookstore;
+-----------+--------------------------------------------+
| Database  | Create Database                           |
+-----------+----------------------------------------------------+
| bookstore | CREATE DATABASE `bookstore` /*!40100 DEFAULT CHARACTER SET latin1 COLLATE latin1_general_ci */ /*!80016 DEFAULT ENCRYPTION='N' */ |
+-----------+---------------------------------+
1 row in set (0.00 sec)


#使用数据库
use 数据库名;

#例如:使用bookstore数据库
use bookstore;

#创建表格
create table temp(
	s char
);

#添加数据
insert into temp values('a');

#查询数据
mysql> select * from temp;
+------+
| s    |
+------+
| a    |
+------+
1 row in set (0.00 sec)

#添加数据
insert into temp values('尚');

mysql> insert into temp values('尚');
ERROR 1366 (HY000): Incorrect string value: '\xC9\xD0' for column 's' at row 1

#删除数据库
drop database 数据库名称;

#例如:删除bookstore数据库
drop database bookstore;

和数据表相关的

查看某个数据库的所有表格

use 数据库名;

show tables;
show tables from 数据库名;

创建表格

create table 数据表名称(
	字段名  数据类型,
    字段名  数据类型
);
create table teacher(
	tid int,
    tname varchar(5),
    salary double,
    weight double(5,2),
    birthday date,
    gender enum('男','女'),
    blood enum('A','B','AB','O'),
	tel char(11)
);

查看表的详细定义信息

show create table 表名称;
show create table 表名称\G
mysql> show create table teacher\G
*************************** 1. row ***************************
       Table: teacher
Create Table: CREATE TABLE `teacher` (
  `tid` int(11) DEFAULT NULL,
  `tname` varchar(5) DEFAULT NULL,
  `salary` double DEFAULT NULL,
  `weight` double(5,2) DEFAULT NULL,
  `birthday` date DEFAULT NULL,
  `gender` enum('男','女') DEFAULT NULL,
  `blood` enum('A','B','AB','O') DEFAULT NULL,
  `tel` char(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

修改数据表编码

ALTER TABLE 表名称 CHARSET=新字符集 COLLATE=新校对规则;

查看表结构

desc 表名称;
mysql> desc teacher;
+----------+------------------------+------+-----+---------+-------+
| Field    | Type                   | Null | Key | Default | Extra |
+----------+------------------------+------+-----+---------+-------+
| tid      | int(11)                | YES  |     | NULL    |       |
| tname    | varchar(5)             | YES  |     | NULL    |       |
| salary   | double                 | YES  |     | NULL    |       |
| weight   | double(5,2)            | YES  |     | NULL    |       |
| birthday | date                   | YES  |     | NULL    |       |
| gender   | enum('男','女')         | YES  |     | NULL    |       |
| blood    | enum('A','B','AB','O') | YES  |     | NULL    |       |
| tel      | char(11)               | YES  |     | NULL    |       |
+----------+------------------------+------+-----+---------+-------+
8 rows in set (0.00 sec)

删除表格,包括表结构和里面的数据

drop table 表名称;

修改表结构:删除字段

alter table 表名称 drop 【column】 字段名称; 
alter table teacher drop column weight;

修改表结构:增加字段

alter table 表名称 add 【column】 字段名称 数据类型; 
alter table 表名称 add 【column】 字段名称 数据类型 first;
alter table 表名称 add 【column】 字段名称 数据类型 after 另一个字段;
alter table teacher add weight double(5,2);
alter table teacher drop column weight;

alter table teacher add weight double(5,2) first;
alter table teacher drop column weight;

alter table teacher add weight double(5,2) after salary;
alter table teacher drop column weight;

修改表结构:修改字段的数据类型

alter table 表名称 modify 【column】 字段名称 新的数据类型; 
mysql> desc teacher;
+----------+------------------------+------+-----+---------+-------+
| Field    | Type                   | Null | Key | Default | Extra |
+----------+------------------------+------+-----+---------+-------+
| tid      | int(11)                | YES  |     | NULL    |       |
| tname    | varchar(5)             | YES  |     | NULL    |       |
| salary   | double                 | YES  |     | NULL    |       |
| weight   | double(5,2)            | YES  |     | NULL    |       |
| birthday | date                   | YES  |     | NULL    |       |
| gender   | enum('男','女')        | YES  |     | NULL    |       |
| blood    | enum('A','B','AB','O') | YES  |     | NULL    |       |
| tel      | char(11)               | YES  |     | NULL    |       |
+----------+------------------------+------+-----+---------+-------+
8 rows in set (0.00 sec)

mysql> alter table teacher modify column weight double;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> desc teacher;
+----------+------------------------+------+-----+---------+-------+
| Field    | Type                   | Null | Key | Default | Extra |
+----------+------------------------+------+-----+---------+-------+
| tid      | int(11)                | YES  |     | NULL    |       |
| tname    | varchar(5)             | YES  |     | NULL    |       |
| salary   | double                 | YES  |     | NULL    |       |
| weight   | double                 | YES  |     | NULL    |       |
| birthday | date                   | YES  |     | NULL    |       |
| gender   | enum('男','女')        | YES  |     | NULL    |       |
| blood    | enum('A','B','AB','O') | YES  |     | NULL    |       |
| tel      | char(11)               | YES  |     | NULL    |       |
+----------+------------------------+------+-----+---------+-------+
8 rows in set (0.00 sec)

修改表结构:修改字段的名称

alter table 表名称 change 【column】 旧字段名称 新的字段名称 新的数据类型; 
mysql> desc teacher;
+----------+------------------------+------+-----+---------+-------+
| Field    | Type                   | Null | Key | Default | Extra |
+----------+------------------------+------+-----+---------+-------+
| tid      | int(11)                | YES  |     | NULL    |       |
| tname    | varchar(5)             | YES  |     | NULL    |       |
| salary   | double                 | YES  |     | NULL    |       |
| weight   | double                 | YES  |     | NULL    |       |
| birthday | date                   | YES  |     | NULL    |       |
| gender   | enum('男','女')        | YES  |     | NULL    |       |
| blood    | enum('A','B','AB','O') | YES  |     | NULL    |       |
| tel      | char(11)               | YES  |     | NULL    |       |
+----------+------------------------+------+-----+---------+-------+
8 rows in set (0.00 sec)

mysql> alter table teacher change tel phone char(11);
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> desc teacher;
+----------+------------------------+------+-----+---------+-------+
| Field    | Type                   | Null | Key | Default | Extra |
+----------+------------------------+------+-----+---------+-------+
| tid      | int(11)                | YES  |     | NULL    |       |
| tname    | varchar(5)             | YES  |     | NULL    |       |
| salary   | double                 | YES  |     | NULL    |       |
| weight   | double                 | YES  |     | NULL    |       |
| birthday | date                   | YES  |     | NULL    |       |
| gender   | enum('男','女')        | YES  |     | NULL    |       |
| blood    | enum('A','B','AB','O') | YES  |     | NULL    |       |
| phone    | char(11)               | YES  |     | NULL    |       |
+----------+------------------------+------+-----+---------+-------+
8 rows in set (0.00 sec)

修改表结构:修改字段位置

alter table 表名称 modify 【column】 字段名称 数据类型 first;
alter table 表名称 modify 【column】 字段名称 数据类型 after 另一个字段;

修改表名称(重命名表)

alter table 旧表名 rename 【to】 新表名;
rename table 旧表名称 to 新表名称;

例如:

alter table teacher rename to t_tea;
rename table t_tea to teacher;

DML

添加语句

添加一条记录到某个表中

insert into 表名称 values(值列表); #值列表中的值的顺序、类型、个数必须与表结构一一对应
mysql> desc teacher;
+----------+------------------------+------+-----+---------+-------+
| Field    | Type                   | Null | Key | Default | Extra |
+----------+------------------------+------+-----+---------+-------+
| tid      | int(11)                | YES  |     | NULL    |       |
| tname    | varchar(5)             | YES  |     | NULL    |       |
| salary   | double                 | YES  |     | NULL    |       |
| weight   | double                 | YES  |     | NULL    |       |
| birthday | date                   | YES  |     | NULL    |       |
| gender   | enum('男','女')        | YES  |     | NULL    |       |
| blood    | enum('A','B','AB','O') | YES  |     | NULL    |       |
| phone    | char(11)               | YES  |     | NULL    |       |
+----------+------------------------+------+-----+---------+-------+
8 rows in set (0.00 sec)
insert into teacher values(1,'张三',15000,120.5,'1990-5-1','男','O','13789586859');
insert into teacher values(2,'李四',15000,'1990-5-1','男','O','13789586859'); #缺体重weight的值

ERROR 1136 (21S01): Column(列) count(数量) doesn't match(不匹配) value(值) count(数量) at row 1

添加一条记录到某个表中

insert into 表名称 (字段列表) values(值列表); #值列表中的值的顺序、类型、个数必须与(字段列表)一一对应
insert into teacher(tid,tname,salary,phone) values(3,'王五',16000,'15789546586');

添加多条记录到某个表中

insert into 表名称 values(值列表),(值列表),(值列表); #值列表中的值的顺序、类型、个数必须与表结构一一对应
insert into 表名称 (字段列表) values(值列表),(值列表),(值列表); #值列表中的值的顺序、类型、个数必须与(字段列表)一一对应
insert into teacher (tid,tname,salary,phone) 
values(4,'赵六',16000,'15789546586'),
(5,'汪飞',18000,'15789548886'),
(6,'天琪',19000,'15909546586');

修改语句

修改所有行

update 表名称 set 字段名 = 值, 字段名 = 值; #给所有行修改
#修改所有人的薪资,都涨了1000
update teacher set salary = salary + 1000 ; 

修改部分行

update 表名称 set 字段名 = 值, 字段名 = 值 where 条件; #给满足条件的行修改
#修改天琪的薪资降低5000
update teacher set salary = salary-5000 where tname = '天琪';

删除

删除部分行的数据

delete from 表名称 where 条件;
delete from teacher where tname = '天琪';

删除整张表的数据,但表结构留下

delete from 表名称;
delete from teacher;

截断表,清空表中的数据,只有表结构

truncate 表名称;
truncate teacher;

truncate表和delete表的区别:

delete是一条一条删除记录的。如果在事务中,事务提交之前支持回滚。(后面会讲事务)

truncate是把整个表drop,新建一张,效率更高。就算在事务中,也无法回滚。

#同学问:是否可以删除salary字段的值,字段留着,值删掉
#可以实现,但是不是用delete,用update

#同学问:是否可以删除salary字段,连同字段和这个字段的数据都是删除
#可以实现,但是不是用delete,用alter table 表名称 drop column 字段名;

#同学问:只删除某个单元格的值
#可以实现,但是不是用delete,用update

UPDATE中嵌套子查询

#子查询也可以嵌套在update语句中
#(1)修改“t_employee”表中部门编号(did)和
#“测试部”部门编号(did)相同的员工薪资为原来薪资的1.5倍。
UPDATE t_employee
SET salary = salary * 1.5
WHERE did = (SELECT did FROM t_department WHERE dname = '测试部');

#(2)修改“t_employee”表中did为NULL的员工信息,
#将他们的did值修改为“测试部”的部门编号。
#子查询select did from t_department where dname = '测试部'
#这种子查询必须是单个值,否则无法赋值

UPDATE t_employee 
SET did = (SELECT did FROM t_department WHERE dname = '测试部')
WHERE did IS NULL;

#(3)修改“t_employee”表中“李冰冰”的薪资值等于“孙红梅”的薪资值。
#这里使用子查询先在“t_employee”表中查询出“孙红梅”的薪资。
#select salary from t_employee where ename = '孙红梅';

UPDATE t_employee
SET salary = (SELECT salary FROM t_employee WHERE ename = '孙红梅')
WHERE ename = '李冰冰';
#You can't specify target table 't_employee' for update in FROM clause'

UPDATE t_employee
SET salary = (SELECT salary FROM(SELECT salary FROM t_employee WHERE ename = '孙红梅')temp)
WHERE ename = '李冰冰';
#当update的表和子查询的表是同一个表时,需要将子查询的结果用临时表的方式表示
#即再套一层子查询,使得update和最外层的子查询不是同一张表

#(4)修改“t_employee”表“李冰冰”的薪资与她所在部门的平均薪资一样。
#子查询,查询李冰冰的部门编号 
#select did from t_employee where ename = '李冰冰';

#子查询第二层,查询李冰冰所在部门的平均薪资
#select avg(salary) from t_employee where did = (select did from t_employee where ename = '李冰冰');

#子查询第三层,把第二层的子查询结果当成临时表再查一下结果
#目的使得和外层的update不是同一张表
SELECT pingjun FROM (SELECT AVG(salary) pingjun FROM t_employee WHERE did = (SELECT did FROM t_employee WHERE ename = '李冰冰') temp)

#update更新
UPDATE t_employee
SET salary = 
(SELECT pingjun FROM 
	(SELECT AVG(salary) pingjun FROM t_employee WHERE did = 
		(SELECT did FROM t_employee WHERE ename = '李冰冰') ) temp)
WHERE ename = '李冰冰';

DELETE中嵌套子查询

#delete语句中也可以嵌套子查询
#(1)从“t_employee”表中删除“测试部”的员工记录。
DELETE FROM t_employee 
WHERE did = (SELECT did FROM t_department WHERE dname = '测试部');


#(2)从“t_employee”表中删除和“李冰冰”同一个部门的员工记录。
#子查询 “李冰冰”的部门编号
#select did from t_employee where ename = '李冰冰';

DELETE FROM t_employee WHERE did = (SELECT did FROM t_employee WHERE ename = '李冰冰');
#You can't specify target table 't_employee' for update in FROM clause'
#删除和子查询是同一张表

DELETE FROM t_employee WHERE did = (SELECT did FROM (SELECT did FROM t_employee WHERE ename = '李冰冰')temp);

使用子查询复制表结构和数据

#演示通过子查询复制表,
#(1)复制表结构
#(2)复制一条或多条记录
#(3)同时复制表结构和记录
#仅仅是复制表结构,可以用create语句
CREATE TABLE department LIKE t_department;

#使用INSERT语句+子查询,复制数据,此时INSERT不用写values
INSERT INTO department (SELECT * FROM t_department WHERE did<=3);

#同时复制表结构+数据
CREATE TABLE d_department AS (SELECT * FROM t_department);
#如果select后面是部分字段,复制的新表就只有这一部分字段

约束

约束的作用

约束是为了保证数据的完整性,数据完整性(Data Integrity)是指数据的精确性(Accuracy)和可靠性(Reliability)。它是应防止数据库中存在不符合语义规定的数据和防止因错误信息的输入输出造成无效操作或错误信息而提出的。

数据的完整性要从以下四个方面考虑:

  • 实体完整性(Entity Integrity):例如,同一个表中,不能存在两条完全相同无法区分的记录
  • 域完整性(Domain Integrity):例如:年龄范围0-120,性别范围“男/女”
  • 引用完整性(Referential Integrity):例如:员工所在部门,在部门表中要能找到这个部门
  • 用户自定义完整性(User-defined Integrity):例如:用户名唯一、密码不能为空等,本部门经理的工资不得高于本部门职工的平均工资的5倍。

约束的类型

约束类型

  • 主键约束:唯一标识数据库表中的每一行/记录。
  • 外键约束:确保两个表之间的引用完整性。
  • 唯一键:确保列中的所有值都是唯一的
  • 检查约束:用于限制列中的值范围。例如,确保年龄列的值在1到100之间。
  • 非空约束:确保列不包含NULL值。
  • 默认值约束:为列提供默认值

自增是键约束字段的一个额外的属性。

表级约束和列级约束

其中键约束和检查约束是表级约束,不仅要看约束字段当前单元格的数据,还要看其他单元格的数据。

非空约束和默认值约束都是列级约束,即约束字段只看当前单元格的数据即可,和其他单元格无关。

所有的表级约束都可以在“information_schema.table_constraints”表中查看。

SELECT * FROM information_schema.table_constraints WHERE table_name = '表名称';

约束和索引

在MySQL中键约束会自动创建索引,提高查询效率。索引的详细讲解在高级部分。

MySQL高级会给大家讲解索引、存储引擎等,因为高级要给大家分析SQL性能。而基础阶段先不管效率,只要能查出来就行。

约束和索引不同:

约束是一个逻辑概念,它不会单独占用物理空间,

索引是一个物理概念,它是会占用物理空间。

例如:字典

字典里面有要求,不能有重复的字(字一样,读音也一样),这是约束。

字典里面有“目录”,它可以快速的查找某个字,目录需要占用单独的页。

《非空约束》

作用:

限定某个字段/某列的值不允许为空

关键字:not nul

特点

(1)只能某个列单独限定非空,不能组合非空

(2)一个表可以有很多列都分别限定了非空

#建表时
create table 表名称(
	字段名 数据类型 not null,
	字段名 数据类型 not null,
	字段名 数据类型
);
#建表后
alter table 表名称 modify 【column】 字段名 数据类型 not null;

#删除非空约束
alter table 表名称 modify 【column】 字段名 数据类型;

《唯一键约束》

作用:

单列唯一:用来限制某个字段/某列的值不能重复。

组合唯一:用来限定几个字段的值组合不能重复。

关键字:unique key

特点:

(1)一个表可以有很多个唯一键约束,
(2)每一个唯一键约束字段都会自动创建索引。
(3)唯一键约束允许为空
(4)唯一键约束也可以是复合唯一
(5)删除唯一键约束的索引来删除唯一键约束
索引名默认是字段名,复合唯一默认是第一个字段名。

单列唯一

#在建表时,可以指定唯一键约束
create table 表名称(
	字段名 数据类型 unique key,
	字段名 数据类型 unique key,
	字段名 数据类型
);

create table 表名称(
	字段名 数据类型,
	字段名 数据类型,
	字段名 数据类型,
	unique key(字段名),
	unique key(字段名)
);


#如何在建表后添加唯一键约束
alter table 表名称 add unique 【key】(字段名);



#如何查看唯一键约束?
desc 表名称;

show create table 表名称;

show index from 表名称; #查看表的索引信息

SELECT * FROM information_schema.table_constraints WHERE table_name = '表名称';

# 删除唯一键约束
alter table 表名称 drop index 索引名;
#删除唯一键约束需要手动删除对应的索引

复合唯一

create table 表名称(
	字段名  数据类型,
    字段名  数据类型,  
    字段名  数据类型,
    unique key(字段列表) # 字段列表中写的是多个字段名,多个字段名用逗号分隔,表示那么是复合唯一,即多个字段的组合是唯一的
);

# 复合唯一 所有的字段后, unique key(字段名1,字段名2)

《主键约束》(重要)

作用:

用来唯一的确定一条记录

关键字:primary key

特点:

(1)唯一并且非空

(2)一个表最多只能有一个主键约束

(3)如果主键是由多列组成,可以使用复合主键

(4)主键列会自动创建索引(能够根据主键查询的,就根据主键查询,效率更高)

(5)如果删除主键约束了,主键约束对应的索引就自动删除了。

/* 唯一键约束和主键约束的区别
(1)唯一键约束一个表可以有好几个,
但是主键约束只有一个
(2)唯一键约束本身不带非空限制,如果需要非空,需要单独定义。
主键约束不用再定义NOT NULL,自身就带非空限制。*/

#建表时指定主键约束
create table 表名称(
	字段名  数据类型  primary key,
    字段名  数据类型,  
    字段名  数据类型  
);
create table 表名称(
	字段名  数据类型,
    字段名  数据类型,  
    字段名  数据类型,
    primary key(字段名)
);


#建表后增加主键约束
alter table 表名称 add primary key(字段列表); #字段列表可以是一个字段,也可以是多个字段,如果是多个字段的话,是复合主键
# 复合主键
create table 表名称(
	字段名  数据类型,
    字段名  数据类型,  
    字段名  数据类型,
    primary key(字段名1,字段名2)  #表示字段1和字段2的组合是唯一的,也可以有更多个字段
);


# 删除主键约束?
alter table 表名称 drop primary key;

《默认值约束》

作用:

如何给字段加默认值

关键字 :default 默认值

特点:

​ 1.不给字段赋值时 使用默认值
​ 2.即使添加了默认值约束也可以赋值为null

# 建表时
CREATE TABLE t_person(
 pid INT,
 pname VARCHAR(20),
 gender CHAR DEFAULT '男'
);
create table 表名称(
	字段名  数据类型 default 默认值 ,
    字段名  数据类型 not null default 默认值,  
    字段名  数据类型 not null default 默认值,,
    primary key(字段名),
    unique key(字段名)
);

# 建表后(了解)
alter table 表名称 modify 字段名 数据类型 default 默认值;


#删除默认值约束
alter table 表名称 modify 字段名 数据类型 ;#删除默认值约束,也不保留非空约束

《自增属性》

作用

作用:给某个字段自动赋值,这个值是一直往上增加,如果没有特意干扰的,每次自增1.

关键字:auto_increment

特点:

(1)一个表只能有一个自增字段,因为一个表只有一个AUTO_INCREMENT属性记录自增字段值
(2)并且自增字段"只能是key字段",即定义了主键、唯一键等键约束的字段。
一般都是给主键和唯一键加自增。
(3)自增字段应该是数值类型,一般都是整数类型。
(4)如果自增列指定了 0 和 null,会在当前最大值的基础上自增,
如果自增列手动指定了具体值,直接赋值为具体值。
(5)如果手动修改AUTO_INCREMENT属性值, 必须 > 当前自增字段的最大值
#建表时
create table 表名称(
	字段名  数据类型  primary key auto_increment,
    字段名  数据类型   
    字段名  数据类型  
    字段名  数据类型 
);

#建表后
alter table 表名称 modify 字段名 数据类型 auto_increment;

# 删除自增属性
alter table 表名称 modify 字段名 数据类型; #去掉auto_increment相当于删除

《检查约束》

作用:

检查(CHECK) 约束用于限制字段中的值的范围。

如果对单个字段定义 CHECK 约束,那么该字段只允许特定范围的值。

如果对一个表定义 CHECK 约束,那么此约束会基于行中其他字段的值在特定的字段中对值进行限制

关键字:check

特点:

例如MySQL8.0之前,就算给表定义了检查约束,也不起作用。在MySQL8.0.16版本之后,CREATE TABLE语句既支持给单个字段定义列级CHECK约束的语法,还支持定义表级CHECK约束的语法。

#在建表时,可以指定检查约束
create table 表名称(
	字段名1 数据类型 check(条件),  #在字段后面直接加检查约束
	字段名2 数据类型,
	字段名3 数据类型,
	check (条件)  #可以限定两个字段之间的取值条件
);


#如何在建表后添加检查约束,使用add check
alter table 表名称 add check(条件);

#如何查看一个表的约束
SELECT * FROM information_schema.table_constraints WHERE table_name = '表名称';
#information_schema数据库名(系统库)
#table_constraints表名称(专门存储各个表的约束)
#WHERE条件
#table_name = '表名称'条件是指定表名称

# 删除检查约束
alter table 表名称 drop check 检查约束名;

《外键约束》

作用:

限定某个表的某个字段的引用完整性,

关键字:foreign key

主表和从表/父表和子表

主表(父表):被引用的表,被参考的表

从表(子表):引用别人的表,参考别人的表

特点

(1)在“从表”中指定外键约束,并且一个表可以建立多个外键约束

(2)创建(create)表时就指定外键约束的话,先创建主表,再创建从表

(3)删表时,先删从表(或先删除外键约束),再删除主表。或者先解除关系,再各自删除。

(4)从表的外键列,必须引用/参考主表的键列(主键或唯一键)

为什么?因为被依赖/被参考的值必须是唯一的

(5)从表的外键列的数据类型,要与主表被参考/被引用的列的数据类型一致,并且逻辑意义一致。

例如:都是表示部门编号,都是int类型。

(6)外键列也会自动建立索引(根据外键查询效率很高,很多)

(7)外键约束的删除,所以不会自动删除,如果要删除对应的索引,必须手动删除

 #建表时 
create table 主表名称(
	字段1  数据类型  primary key,
    字段2  数据类型
);

create table 从表名称(
	字段1  数据类型  primary key,
    字段2  数据类型,
    foreign key (从表的某个字段) references 主表名(被参考字段)
);
#建表后(了解)
alter table 从表名称 add foreign key (从表的字段) references 主表(被引用字段) 【on update xx】【on delete xx】;


#如何查看外键约束名
desc 从表名称; #可以看到外键约束,但看不到外键约束名
show create table 从表名称; #可以看到外键约束名
SELECT * FROM information_schema.table_constraints WHERE table_name = '表名称';
#information_schema数据库名(系统库)
#table_constraints表名称(专门存储各个表的约束)
#WHERE条件
#table_name = '表名称'条件是指定表名称

#如何查看外键字段索引
show index from 表名称; #查看某个表的索引名

#删除外键约束
#删除外键约束,不会自动删除外键约束列的索引,需要单独删除。

#(1)第一步先查看约束名和删除外键约束
SELECT * FROM information_schema.table_constraints WHERE table_name = '表名称';#查看某个表的约束名
alter table 从表名 drop foreign key 外键约束名;
#(2)第二步查看索引名和删除索引
show index from 表名称; #查看某个表的索引名
alter table 从表名 drop index 索引名;

事务

事务的特点

1.事务:在数据库系统中,事务是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。

2、事务的ACID属性:

(1)原子性(Automicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。

(2)一致性(Consistency)
事务必须使数据库从一个一致性状态变换到另外一个一致性状态。

(3)隔离性(Isolation)
事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。

(4)持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响

/*
原子性:
例如:
张三给李四转账500
张三转账之前余额是1000
李四转账之前余额是1000
成功:张三账号变为500,李四变为1500,
失败:张三账号是1000,李四还是1000.


#一致性
例如:
张三给李四转账500
张三转账之前余额是1000
李四转账之前余额是1000

要么他俩的余额不变, 还是1000,总和就是2000
要么他俩的余额有变化,张三500,李四1500,总和仍然是2000
错误:
张三500,李四1000,总和是1500,结果不对
张三1000,李四1500,总和是2500,结果不对

#隔离性
例如:张三要给李四转500,
      王五要给李四转500,
      张三转账是否成功,和王五是否转账成功无关。
      
      
#持久性:
例如:张三要给李四转500,一旦成功提交,就转账成功,撤不回来了。
      */

事务的开启、提交、回滚

MySQL默认情况下是自动提交事务。
每一条语句都是一个独立的事务,一旦成功就提交了。一条语句失败,单独一条语句不生效,其他语句是生效的。

手动提交模式下开启事务(本次连接一直有效)

#开启手动提交事务模式
set autocommit = false;  或  set autocommit = 0;

commit;   # 提交  
rollback; # 回滚

# 上面语句执行之后,它之后的所有sql,都需要手动提交才能生效,直到恢复自动提交模式。

#恢复自动提交模式
set autocommit = true; 或 set autocommit = 1;

#例
SET autocommit = FALSE;#设置当前连接为手动提交模式
UPDATE t_employee SET salary = 15000 
WHERE ename = '孙红雷';
#后面没有提交,直接关了连接,那么这句修改没有生效

自动提交模式下开启事务(一次有效,commit/rollback后失效)

START TRANSACTION; #开始事务
UPDATE t_employee SET salary = 0 
WHERE ename = '李冰冰'; 
#下面没有写commit;那么上面这句update语句没有正式生效。
commit;#提交


START TRANSACTION;
DELETE FROM t_employee;
ROLLBACK; #回滚

DDL语句不支持事务

#说明:DDL不支持事务
#DDL:create,drop,alter等创建库、创建表、删除库、删除表、修改库、修改表结构等这些语句不支持事务。
#换句话只对insert,update,delete语句支持事务。
TRUNCATE 表名称; 清空整个表的数据,不支持事务。 把表drop掉,新建一张表。

START TRANSACTION;
TRUNCATE t_employee;
ROLLBACK; #回滚  无效

事务的隔离级别

数据库事务的隔离性:数据库系统必须具有隔离并发运行各个事务的能力, 使它们不会相互影响, 避免各种并发问题。一个事务与其他事务隔离的程度称为隔离级别。数据库规定了多种事务隔离级别, 不同隔离级别对应不同的干扰程度, 隔离级别越高, 数据一致性就越好, 但并发性越弱。

  • 脏读:一个事务读取了另一个事务未提交数据;
  • 不可重复读:同一个事务中前后两次读取同一条记录不一样。因为被其他事务修改了并且提交了。
  • 幻读:一个事务读取了另一个事务新增、删除的记录情况,记录数不一样,像是出现幻觉。

不可重复度和幻读区别:

不可重复读的重点是修改,幻读的重点在于新增或者删除。

/*
mysql支持四个隔离级别:
read-uncommitted:会出现脏读、不可重复读、幻读
read-committed:可以避免脏读,会出现不可重复读、幻读
repeatable-read:可以避免脏读、不可重复读、幻读。但是两个事务不能操作(写update,delete)同一个行。
serializable:可以避免脏读、不可重复读、幻读。但是两个事务不能操作(写update,delete)同一个表。

修改隔离级别:
set transaction_isolation='隔离级别';  
#mysql8之前 transaction_isolation变量名是 tx_isolation

查看隔离级别:
select @@transaction_isolation;

先更改事务隔离级别 再开启事务
*/

数据库提供的 4 种事务隔离级别:

隔离级别 描述
read-uncommitted 允许A事务读取其他事务未提交和已提交的数据。会出现脏读、不可重复读、幻读问题
read-committed 只允许A事务读取其他事务已提交的数据。可以避免脏读,但仍然会出现不可重复读、幻读问题
repeatable-read 确保事务可以多次从一个字段中读取相同的值。在这个事务持续期间,禁止其他事务对这个字段进行更新。可以避免脏读和不可重复读。但是幻读问题仍然存在。注意:mysql中使用了MVCC多版本控制技术,在这个级别也可以避免幻读。
serializable 确保事务可以从一个表中读取相同的行,相同的记录。在这个事务持续期间,禁止其他事务对该表执行插入、更新、删除操作。所有并发问题都可以避免,但性能十分低下。
posted @ 2024-08-18 14:09  CH_song  阅读(4)  评论(0编辑  收藏  举报