2022-5-26
2022-5-26
DDL(DataDefinitionLanguages、数据定义语言),这些语句定义了不同的数据库、表、视图、索引等数据库对象,还可以用来创建、删除、修改数据库和数据表的结构。
DML(DataManipulationLanguage、数据操作语言),用于添加、删除、更新和查询数据库记录,并检查数据完整性。
DCL(DataControlLanguage、数据控制语言),用于定义数据库、表、字段、用户的访问权限和
安全级别。
主要的语句关键字包括GRANT、REVOKE、COMMIT、ROLLBACK、SAVEPOINT等。
因为查询语句使用的非常的频繁,所以很多人把查询语句单拎出来一类:DQL(数据查询语言)。
还有单独将COMMIT、ROLLBACK取出来称为TCL(TransactionControlLanguage,事务控制语言)。
举例子:
#以下两句是一样的,不区分大小写
show databases;
SHOW DATABASES;
#创建表格
#createtablestudentinfo(...);#表名错误,因为表名有空格
create table student_info(...);
#其中order使用``飘号,因为order和系统关键字或系统函数名等预定义标识符重名了
CREATE TABLE`order`(
id INT,
lname VARCHAR(20)
);
select idas"编号",`name`as"姓名"from t_stu;#起别名时,as都可以省略
select idas编号,`name`as姓名 from t_stu;#如果字段别名中没有空格,那么可以省略""
select idas编号,`name`as姓名 from t_stu;#错误,如果字段别名中有空格,那么不能省略""
在命令行客户端登陆MySQL,使用source指令导入
mysql>sourced:\mysqldb.sql
#1、-----查看数据库
show databases;
#2、-----删除数据库
drop database 库名;
#3、-----选择数据库
use 库名;
#4、-----创建数据库
create database zzj; #创建一个数据库名zzj的数据库,字符集为默认字符集
>create database IF
>NOT EXISTS RUNOOB
>default character set utf8
>collare utf8_general_ci;
#如果数据库不存在则创建,存在则不创建。(IF NOT EXISTS)
#创建RUNOOB数据库,并设定编码集为:utf8,校对规则为:utf8_general_ci;
#5、----查看数据库的字符集
show create database 库名;
#6、-----查看数据库的字符编码
show create database 库名 \G;
#1、创建表(必须带字段)
cerate table 表名 (id int,sname char(20),age int,sex char(20));
#2、查看表
show tables;
#3、查看表的基本结构
desc 表名
#4、查看表的数据结构
show create table 表名 \G;
#5、查看表的数量
show tables;
#6、删除表
drop table 表名
#7、修改表名
alter table 表名1 rename to(可省) 表名2;
#8、增加字段
alter table 表名 add 字段(列名) 数据类型 first;→→【加字段在表头
alter table 表名 add 字段1(列名) 数据类型 after 字2→→【在字段2的后面加一个字段1)
alter table 表名 add 字段(列名) 数据类型→→→【默认加在末尾
#9、删除字段
alter table 表名 drop 列名(字段);
#10、修改字段中的数据类型
alter table 表名 modify 列表(字段)新的数据类型;
#11、修改字段的顺序
alter table 表名 modify 字段名 数据类型 first 或 after 列名(字段);
2022-5-27
SELECT 9;
SELECT 9/2;
SELECT 9,9/2 FROM DUAL; #DUAL是伪表
SELECT employee_id,first_name,last_name #查找指定字段的数据
FROM employees;
SELECT * FROM employees; #查询表的全部数据
SELECT employee_id AS 学号,last_name AS 姓,first_name AS 名
FROM employees;
SELECT employee_id AS id,last_name "姓",first_name AS 名
FROM employees; //英文和中文都可以作为别名
例题1、查询员工表中一共有哪些部门
null–>空值
null不等同于 0 和’ ’
null参与运算结果为null
查询员工年工资多少
#查询学号小于105的
SELECT employee_id AS 学号,last_name AS 姓,first_name AS 名
FROM employees
WHERE employee_id < 105;
#查询90号部门的员工信息
SELECT *
FROM employees
WHERE department_id = 90;
#查询员工姓为“King”的员工
SELECT *
FROM employees
WHERE last_name = 'King';
#【题目】
#1.查询员工12个月的工资总和,并起别名为ANNUALSALARY
SELECT
salary * (1 + IFNULL(commission_pct,0)) * 12 "ANNUALSALARY"
FROM employees;
#2.查询employees表中去除重复的job_id以后的数据
SELECT DISTINCT job_id
FROM employees;
#3.查询工资大于12000的员工姓名和工资
SELECT first_name,last_name,salary
FROM employees
WHERE salary > 12000;
#4.查询员工号为176的员工的姓名和部门号
SELECT first_name,last_name,department_id
FROM employees
WHERE employee_id = 176;
#5.显示表departments的结构,并查询其中的全部数据
DESC departments;
SELECT * FROM departments;
2022-5-28
加减法
SELECT 100,100+0,100-0,100+50,100+50-30,100+35.5,100-35.5
FROM DUAL;
SELECT 100 + '1'; #结果是101,在java中是做链接的作用,但是在MySQL中是将字符串转化为数字来进行计算
SELECT 100 + 'A'; #结果是100,字符串转换不成功,可以用0代替
SELECT 100 + NULL; #结果是null
乘除法
1、概念:
2、等号运算符
等号运算符(=)判断等号两边的值、字符串或表达式是否相等,如果相等则返回1,不相等则返回0。
在使用等号运算符时,遵循如下规则:
对比:SQL中赋值符号使用:=
例子:
安全等于:
3、不等于运算符:
4、符号比较运算符:
例子:
#IS NULL,IS NOT NULL,ISNULL-------------------------------------
SELECT *
FROM employees
WHERE commission_pct IS NULL; #筛选commission_pct 为null 的员工信息
SELECT *
FROM employees
WHERE commission_pct IS NOT NULL; #筛选commission_pct 不为null 的员工信息
SELECT *
FROM employees
WHERE ISNULL(commission_pct); #筛选commission_pct 为null 的员工信息
#LEAST,GREATEST,--------------------------------------------------
SELECT LEAST(12,21); #12---返回最小值
SELECT GREATEST(12,21); #21---返回最大值
#BETWEEN。。。AND--------------------------------------------------
SELECT *
FROM employees
WHERE commission_pct BETWEEN 0.2 AND 0.25; #筛选commission_pct值在0.2-0.25之间的数据
#IN,NOT IN-------------------------------------------------------
SELECT *
FROM employees
WHERE commission_pct IN (0.2,0.25); #筛选commission_pct值--在0.2-0.25之间的数据
SELECT *
FROM employees
WHERE commission_pct NOT IN (0.2,0.25); #筛选commission_pct值--不在0.2-0.25之间的数据
LIKE—模糊查询
#LIKE(模糊查询 %,_)-------------------------------------------
SELECT *
FROM employees
WHERE last_name LIKE 'J%'; #查询last_name以 J开头 的员工信息
SELECT *
FROM employees
WHERE last_name LIKE '%J%'; #查询last_name中 含有J 的员工信息
SELECT *
FROM employees
WHERE last_name LIKE '_o%';#查询last_name中 第二个字符是o 的员工信息。
ESCAPE:
回避特殊符号的:使用转义符。例如:将[%]转为[ %]、[]转为[ ],然后再加上[ESCAPE‘$’]即可。
SELECT job_id
FROM jobs
WHERE job_id LIKE 'IT\_%'; #---IT_PROG
如果使用\表示转义,要省略ESCAPE。如果不是\,则要加上ESCAPE。
SELECT job_id
FROM jobs
WHERE job_id LIKE 'IT$_%' escape '$'; #---IT_PROG
REGEXP运算符
REGEXP运算符用来匹配字符串,语法格式为:exprREGEXP匹配条件。如果expr满足匹配条件,返回1;如果不满足,则返回0。若expr或匹配条件任意一个为NULL,则结果为NULL
1、‘^’匹配以该字符后面的字符开头的字符串。
2、‘$’匹配以该字符前面的字符结尾的字符串。
3、‘.’匹配任何一个单字符。
4、“[…]”匹配在方括号内的任何字符。例如,“[abc]”匹配“a”或“b”或“c”。为了命名字符的范围,使用一个‘-’。“[a-z]”匹配任何字母,而“[0-9]”匹配任何数字。
5、‘ * ’匹配零个或多个在它前面的字符。例如,“x*”匹配任何数量的‘x’字符,“[0-9]”匹配任何数量的数字,而“”匹配任何数量的任何字符。
SQL语句示例:
SELECT 'shkstart'REGEXP'^s','shkstart'REGEXP't$','shkstart'REGEXP'hk'; #判断字段是否与 s 开头,以 t 结尾,是否含有hk字符
SELECT'atguigu'REGEXP'gu.gu','atguigu'REGEXP'[ab]';
#1、判断。。。
#2、判断字段中是否 含有 a 或者 b
2022-5-28
逻辑运算符主要用来判断表达式的真假,在MySQL中,逻辑运算符的返回结果为1、0或者NULL。
逻辑非(NOT或!)运算符表示当给定的值为0时返回1;当给定的值为非0值时返回0;当给定的值为NULL时,返回NULL。
SELECT NOT 1,NOT 0,NOT(1-1),NOT !1;
#非0数时返回1,为0时返回0,-->NOT !1---!1 = 0,NOT 0 = 1
SELECT last_name,job_id
FROM employees
WHERE job_id NOT IN('IT_PROG','ST_CLERK','SA_REP');
#筛选不在这三个部门的员工信息
逻辑与(AND或&&)运算符是当给定的所有值均为非0值,并且都不为NULL时,返回
1;当给定的一个值或者多个值为0时则返回0;否则返回NULL。
SELECT 1 AND -1,0 AND NULL,1 AND NULL,0 AND 1;#当逻辑与中的条件有一处结果为0时整体为1
SELECT *
FROM employees
WHERE salary > 5000
AND last_name LIKE 'J%'; #筛选姓中以 J 开头,且工资大于5000的公司员工
逻辑或(OR或||)运算符是当给定的值都不为NULL,并且任何一个值为非0值时,则返回1,否则返回0;当一个值为NULL,并且另一个值为非0值时,返回1,否则返回NULL;当两个值都为NULL时,返回NULL。
SELECT *
FROM employees
WHERE department_id = 10
or department_id = 20; #筛选 10号部门 或者 20号部门 的公司员工
#查询基本薪资不在9000-12000之间的员工编号和基本薪资
SELECT employee_id,salary
FROM employees
WHERE NOT(salary>=9000 AND salary<=12000);
SELECT employee_id,salary
FROM employees
WHERE salary<9000 OR salary>12000;
SELECT employee_id,salary
FROM employees
WHERE salary NOT BETWEEN 9000 AND 12000;
逻辑异或(XOR)运算符是当给定的值中任意一个值为NULL时,则返回NULL;如果两个非NULL的值都是0或者都不等于0时,则返回0;如果一个值为0,另一个值不为0时,则返回1。
2022-5-30
例子:
可以使用不在SELECT列表中的列排序。
在对多列进行排序的时候,首先排序的第一列必须有相同的列值,才会对第二列进行排序。如果第
一列数据中所有值都是唯一的,将不再对第二列进行排序。
例子:
1、背景:
2、实现规则
分页原理—所谓分页显示,就是将数据库中的结果集,一段一段显示出来需要的条件
MySQL中使用limit实现分页
格式:
limit[位置偏移量,] 行数
第一个“位置偏移量”参数指示MySQL从哪一行开始显示,是一个可选参数,如果不指定“位置偏移量”,将会从表中的第一条记录开始(第一条记录的位置偏移量是0,第二条记录的位置偏移量是1,以此类推);第二个参数“行数”指示返回的记录条数。
#----查询,展示前十条数据
SELECT *
FROM employees
-- LIMIT 0,10 ; #左闭右开
LIMIT 10 ;
#----查询根据工资从小到大排序后的 前十条
SELECT *
FROM employees
-- LIMIT 0,10 ;
ORDER BY salary ASC
LIMIT 10 ; #前十条记录
#----每页显示20条数据,此时显示第三页
SELECT *
FROM employees
LIMIT 40,20;
#----每页显示20条数据,此时显示第二页
SELECT *
FROM employees
LIMIT 20,20;
ySQL8.0中可以使用“LIMIT3OFFSET4”,意思是获取从第5条记录开始后面的3条记录,和“LIMIT4,3;”返回的结果相同。
分页显式公式:(当前页数-1)*每页条数,每页条数
SELECT*FROMtable
LIMIT(PageNo-1)*PageSize,PageSize;
注意:LIMIT子句必须放在整个SELECT语句的最后!
使用LIMIT的好处
2022-5-31
案例:
SELECT *
FROM employees,departments; #查询到了2889条数据
为什么会出现以上结果:
SELECT COUNT(department_id)
FROM departments; #一共有27条数据
SELECT COUNT(employee_id)
FROM employees; #一共有107条数据
#------2889 = 107 * 27
原因是上述结果发生了笛卡尔积。
笛卡尔乘积:是一个数学运算。假设我有两个集合X和Y,那么X和Y的笛卡尔积就是X和Y的所有可能组合,也就是第一个对象来自于X,第二个对象来自于Y的所有可能。组合的个数即为两个集合中元素个数的乘积数。
SQL92中,笛卡尔积也称为交叉连接,英文是CROSSJOIN。在SQL99中也是使用CROSSJOIN表示交叉连接。它的作用就是可以把任意表进行连接,即使这两张表不相关。在MySQL中如下情况会出现笛卡尔积:
为什么会出现笛卡尔积呢:
笛卡尔积的四种书写方式
SELECT last_name,department_name
FROM employees,departments; #2889
SELECT last_name,department_name
FROM employees CROSS JOIN departments; #2889
SELECT last_name,department_name
FROM employees INNER JOIN departments; #2889
SELECT last_name,department_name
FROM employees JOIN departments; #2889
笛卡尔积的错误会在下面条件下产生:
为了避免笛卡尔积可以加入where进行条件判断
加入条件后的,查询语法:
#----查询每个员工对应的部门名字
SELECT last_name,department_name
FROM employees,departments
WHERE employees.department_id = departments.department_id;
#----一共106条数据-->有一个员工的department_id = null
在表中有相同的字段名时,要在字段名前加上表的名字来表明来自哪张表的字段
当查询语句中出现了多个表中都存在的字段,则必须指明此字段所在的表
#----查询每个员工对应的部门名字
SELECT
employees.last_name,employees.first_name,departments.department_id
FROM employees,departments
WHERE employees.department_id = departments.department_id;
给表起别名—因为重复使用了多次表名
可以简化查询
列名前使用表名前缀可以提高查询效率
注意:如果我们使用了表的别名,在查询字段中,过滤条件的中就只能使用别名进行代替,并不能使用原表名,使用后会报错。
SELECT e.employee_id,d.department_id,d.department_name
FROM employees e,departments d
WHERE e.department_id = d.department_id;
阿里巴巴的规范:
扩展:
#----查询employees表中的工资应在job_grades表中的最高工资与最低工资之间,并按从高到低排序
SELECT e.last_name,e.first_name,e.salary,j.grade_level
FROM employees e,job_grades j
WHERE e.salary BETWEEN j.lowest_sal AND j.highest_sal
ORDER BY e.salary;
自己和自己连接—用别名使两张相同的表代表不同的意义,然后进行查询
#------查询各个员工的经理编号
SELECT e1.last_name,e2.last_name,e1.manager_id,e2.employee_id
FROM employees e1,employees e2
WHERE e1.manager_id = e2.employee_id;
#查询出last_name为‘Chen’的员工的manager的信息。
SELECT d.*
FROM employees e,departments d
WHERE e.manager_id = d.manager_id AND e.last_name = 'Chen';
#查询员工id,员工姓名及其管理者的id姓名
SELECT e.employee_id,e.last_name,e1.last_name
FROM employees e,employees e1
WHERE e.manager_id = e1.employee_id;
SQL92:使用(+)创建连接
在SQL92中采用(+)代表从表所在的位置。即左或右外连接中,(+)表示哪个是从表。
Oracle对SQL92支持较好,而MySQL则不支持SQL92的外连接。
#左外连接
SELECTlast_name,department_name
FROMemployees,departments
WHEREemployees.department_id=departments.department_id(+);
#右外连接
SELECTlast_name,department_name
FROMemployees,departments
WHEREemployees.department_id(+)=departments.department_id;
语法:
SELECT 字段列表
FROM A表 INNER JOIN B表 #inner这个关键字可以省略
ON 关联条件
WHERE 等其他子句;
语法结构:
使用**(join。。on)**创建连接
SELECT table1.columnt,able2.column,table3.column
FROM table1
JOIN table2 ON table1 和 table2的连接条件
JOIN table3 ON table2 和 table3的连接条件
SQL99采用的这种嵌套结构非常清爽、层次性更强、可读性更强,即使再多的表进行连接也都清晰可见。如果你采用SQL92,可读性就会大打折扣。
语法说明:
例子说明:
#---获取员工id>150的员工所在部门的名字
SELECT e.employee_id,d.department_name
FROM employees e JOIN departments d
ON (e.department_id = d.department_id)
WHERE e.employee_id > 150
ORDER BY employee_id ASC;
2022-6.1
左外连接:
语法:
SELECT 字段列表
FROM A表 LEFT JOIN B表
ON 关联条件
WHERE 等其他子句;
语法例子:
#----查询员工对应的部门id及名称
SELECT e.last_name, e.department_id, d.department_name
FROM employees e
LEFT OUTER JOIN departments d
ON (e.department_id = d.department_id) ;
右外连接:
需要注意的是,LEFTJOIN和RIGHTJOIN只存在于SQL99及以后的标准中,在SQL92中不存在,只能用(+)表示。
语法:
#实现查询结果是B
SELECT 字段列表
FROM A表 RIGHT JOIN B表
ON 关联条件
WHERE 等其他子句;
语法例子
#----查询员工对应的部门id及名称
SELECT e.last_name, e.department_id, d.department_name
FROM employees e
LEFT OUTER JOIN departments d
ON (e.department_id = d.department_id) ;.3.3满外连接(FULLOUTERJOIN)
满外连接的结果=左右表匹配的数据+左表没有匹配到的数据+右表没有匹配到的数据。
SQL99是支持满外连接的。使用FULLJOIN或FULLOUTERJOIN来实现。
需要注意的是,MySQL不支持FULLJOIN,但是可以用LEFTJOINUNIONRIGHTjoin代替。
满外连接(FULL OUTER JOIN)
**合并查询结果:**利用UNION关键字,可以给出多条SELECT语句,并将它们的结果组合成单个结果集。合并时,两个表对应的列数和数据类型必须相同,并且相互对应。各个SELECT语句之间使用UNION 或 UNION ALL关键字分隔。
语法格式:
SELECTcolumnFROMtable1
UNION[ALL]
SELECTcolumnFROMtable2
UNION操作符
UNION ALL操作符
UNIONALL操作符返回两个查询的结果集的并集。对于两个结果集的重复部分,不去重。
注意:执行UNIONALL语句时所需要的资源比UNION语句少。如果明确知道合并数据后的结果数据不存在重复数据,或者不需要去除重复的数据,则尽量使用UNIONALL语句,以提高数据查询的效率。
例子:
#----举例:查询部门编号>90或邮箱包含a的员工信息
SELECT *
FROM employees
WHERE department_id > 90
OR email LIKE '%a%';
SELECT *
FROM employees
WHERE department_id > 90
UNION #返回结果集,去除重复
SELECT *
FROM employees
WHERE email LIKE '%a%';
#-----举例:查询中国用户中男性的信息以及美国用户中年男性的用户信息
SELECT id,cname
FROM t_chinamale
WHERE csex='男'
UNION ALL #返回结果集,不去重
SELECT id,tname
FROM t_usmale
WHERE tGender='male';
代码实现:
#中--->内连接 A 交 B----------------------------
SELECT e.employee_id,e.last_name,d.department_name
FROM employees e JOIN departments d
ON e.department_id = d.department_id;
#左上--->左外连接-------------------------------
SELECT e.employee_id,e.last_name,d.department_name
FROM employees e LEFT JOIN departments d
ON e.department_id = d.department_id;
#右上--->右外连接-------------------------------
SELECT e.employee_id,e.last_name,d.department_name
FROM employees e RIGHT JOIN departments d
ON e.department_id = d.department_id;
#左中图--->A - A 交 B------------------------------
SELECT e.employee_id,e.last_name,d.department_name
FROM employees e LEFT JOIN departments d
ON e.department_id = d.department_id
WHERE d.department_id IS NULL;
#右中图--->B - A 交 B-------------------------
SELECT e.employee_id,e.last_name,d.department_name
FROM employees e RIGHT JOIN departments d
ON e.department_id = d.department_id
WHERE d.department_id IS NULL;
#左下图--->A 并 B--------------------------------
SELECT e.employee_id,e.last_name,d.department_name
FROM employees e LEFT JOIN departments d
ON e.department_id = d.department_id
UNION ALL
SELECT e.employee_id,e.last_name,d.department_name
FROM employees e RIGHT JOIN departments d
ON e.department_id = d.department_id;
#右下图--->A 并 B - A 交 B-------------------------
SELECT e.employee_id,e.last_name,d.department_name
FROM employees e LEFT JOIN departments d
ON e.department_id = d.department_id
WHERE d.department_id IS NULL
UNION ALL
SELECT e.employee_id,e.last_name,d.department_name
FROM employees e RIGHT JOIN departments d
ON e.department_id = d.department_id
WHERE d.department_id IS NULL;
SQL99语法新特性
在SQL92标准中:
#--->将两张表所有相同的字段连接----------------
SELECT employee_id,last_name,department_name
FROM employees e JOIN departments d
ON e.department_id = d.department_id
AND e.manager_id = d.manager_id;
在SQL99标准中:
SELECT e.employee_id,e.last_name,d.department_name
FROM employees e NATURAL JOIN departments d
当我们进行连接的时候,SQL99还支持使用 USING 指定数据表里的 同名字段 进行等值连接。但是只能配合JOIN一起使用。
例子展示:
SELECT e.employee_id,e.last_name,d.department_name
FROM employees e
JOIN departments d
ON e.department_id = d.department_id;
SELECT e.employee_id,e.last_name,d.department_name
FROM employees e
JOIN departments d
USING (department_id);
#上面两个SQL语句的含义是一样的
注意:
表连接的约束条件可以有三种方式:WHERE,ON,USING
2022-6-2
我们在使用 SQL 语言的时候,不是直接和这门语言打交道,而是通过它使用不同的数据库软件,即DBMS。DBMS 之间的差异性很大,远大于同一个语言不同版本之间的差异。实际上,只有很少的函数是被 DBMS 同时支持的。比如,大多数 DBMS 使用(||)或者(+)来做拼接符,而在 MySQL 中的字符串拼接函数为concat()。大部分 DBMS 会有自己特定的函数,这就意味着采用 SQL 函数的代码可移植性是很差的,因此在使用函数的时候需要特别注意。
MySQL提供了丰富的内置函数,这些函数使得数据的维护与管理更加方便,能够更好地提供数据的分析与统计功能,在一定程度上提高了开发人员进行数据分析与统计的效率。
MySQL提供的内置函数从 实现的功能角度 可以分为数值函数、字符串函数、日期和时间函数、流程控制函数、加密与解密函数、获取MySQL信息函数、聚合函数等。这里,我将这些丰富的内置函数再分为两类: 单行函数 、 聚合函数(或分组函数) 。
两种函数:
单行函数:
2022-6-4
函数 | 用法 |
---|---|
ABS(X) | 返回x的绝对值 |
SIGN(X) | 返回X的符号。正数返回1,负数返回-1,0返回0 |
PI() | 返回圆周率的值 |
CEIL(X),CEILING(x) | 返回大于或等于某个值的最小整数 |
FLOOR(x) | 返回小于或等于某个值的最大整数 |
LEAST(E1,E2…) | 返回列表中的最小值 |
GREATEST(E1,E2…) | 返回列表中的最大值 |
MOD(X,Y) | 返回X除以Y后的余数 |
RAND() | 返回0~1的随机值 |
ROUND(X) | 返回一个对x的值进行四舍五入后,最接近与x的整数 |
ROUND(X,Y) | 返回一个对x的值进行四舍五入后最接近X的值,并保留到小数点后面Y位 |
TRUNCATE(X,Y) | 返回数字x截断y位小数的结果 |
SQRT(X) | 返回x的平方根。当x的值为负数时,返回null |
代码详细:
#----返回绝对值
SELECT ABS(-9); #----9
#----返回X的符号。正数返回1,负数返回-1,0返回0
SELECT SIGN(9),SIGN(-9),SIGN(0); #----1,-1,0
#----返回圆周率的值
SELECT PI(); #----3.141593
#----返回大于或等于某个值的最小整数
SELECT CEIL(5.4),CEILING(5.4); #----6,6
#----返回小于或等于某个值的最大整数
SELECT FLOOR(5.4); #----5
#----返回列表中的最小值
SELECT LEAST(5,7,2,3,9,1); #----1
#----返回列表中的最大值
SELECT GREATEST(5,7,9,12,1,8,96); #----96
#----返回X除以Y后的余数
SELECT MOD(5,2); #----1
#----返回0~1的随机值
SELECT RAND(); #----0.3000254965
#----返回0~1的随机值,其中x的值用作种子值,相同的X值会产生相同的随机数
SELECT RAND(10); #----0.6570515219
#----返回一个对x的值进行四舍五入后,最接近与x的整数
SELECT ROUND(5.2); #----5
#-----返回一个对x的值进行四舍五入后最接近x的值,并保留到小数点后y位
SELECT ROUND(5.25645,1); #----5.3
#-----返回数字x截断y位小数的结果
SELECT TRUNCATE(5.2665,3); #----5.266
#-----返回x的平方根。当x的值为负数时,返回null
SELECT SQRT(9); #----3
①、获取时间,日期
函数请去查阅单行函数PDF文档
可以对数值型数据使用
SELECT AVG(salary),SUM(salary)
FROM employees
WHERE last_name LIKE '%J%';
COUINT(*)返回表中记录总数,适用任意数据类型
COUNT(expr)返回expr不为空的记录总数
**问题一、**用count(*),count(1),count(列名)谁好呢?
**问题二、**能不能使用count(列名)替换count()?
WHERE一定放在from的后面
在SELECT列表中所有未包含在组函数中的列都应该包含在 GROUP BY子句中
例子:
#对部门ID进行分组。并求出组内数据的平均值
SELECT department_id,AVG(salary)
FROM employees
GROUP BY department_id;
包含在group by中的字段可以不出现在select语句中
例子:
WITH ROLLUP的介绍
注意事项:
例子详细:
区别1:WHERE 可以直接使用表中的字段作为筛选条件,但不能使用分组中的计算函数作为筛选条件;HAVING 必须要与 GROUP BY 配合使用,可以把分组计算的函数和分组字段作为筛选条件。
这决定了,在需要对数据进行分组统计的时候,HAVING 可以完成 WHERE 不能完成的任务。这是因为,在查询语法结构中,WHERE 在 GROUP BY 之前,所以无法对分组结果进行筛选。HAVING 在 GROUP BY 之后,可以使用分组字段和分组中的计算函数,对分组的结果集进行筛选,这个功能是 WHERE 无法完成的。另外,WHERE排除的记录不再包括在分组中。
区别2:如果需要通过连接从关联表中获取需要的数据,WHERE 是先筛选后连接,而 HAVING 是先连接后筛选 .
这一点,就决定了在关联查询中,WHERE 比 HAVING 更高效。因为 WHERE 可以先筛选,用一个筛选后的较小数据集和关联表进行连接,这样占用的资源比较少,执行效率也比较高。HAVING 则需要先把结果集准备好,也就是用未被筛选的数据集进行关联,然后对这个大的数据集进行筛选,这样占用的资源就比较多,执行效率也较低。
小结:
优点 | 缺点 | |
---|---|---|
WHERE | 先筛选数据在关联 | 不能使用分组中的计算函数进行筛选 |
HAVING | 可以使用分组中的计算函数 | 在最后的结果集中进行筛选,执行效率较低 |
开发中的选择
#方式1:
SELECT ...,....,...
FROM ...,...,....
WHERE 多表的连接条件
AND 不包含组函数的过滤条件
GROUP BY ...,...
HAVING 包含组函数的过滤条件
ORDER BY ... ASC/DESC
LIMIT ...,...
#方式2:
SELECT ...,....,...
FROM ... JOIN ...
ON 多表的连接条件
JOIN ...
ON ...
WHERE 不包含组函数的过滤条件
AND/OR 不包含组函数的过滤条件
GROUP BY ...,...
HAVING 包含组函数的过滤条件
ORDER BY ... ASC/DESC
LIMIT ...,...
##其中----解释关键字:
#(1)from:从哪些表中筛选
#(2)on:关联多表查询时,去除笛卡尔积
#(3)where:从表中筛选的条件
#(4)group by:分组依据
#(5)having:在统计结果中再次筛选
#(6)order by:排序
#(7)limit:分页
你需要记住一下两个顺序
1、关键字的顺序是不能颠倒的:
SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY ... LIMIT...
2、SELECT 语句的执行顺序(在 MySQL 和 Oracle 中,SELECT 执行顺序基本相同):
FROM -> WHERE -> GROUP BY -> HAVING -> SELECT 的字段 -> DISTINCT -> ORDER BY -> LIMIT
书写顺序例子:
#筛选出--名字中含 a 且 所在部门的薪资平均值 > 10000 且对部门平均薪资做降序排序,并且只显示前两行数据
SELECT e.last_name,d.department_name,MAX(e.salary) AS z_avg
FROM employees e
JOIN departments d
ON e.department_id = d.department_id
WHERE e.last_name LIKE '%a%'
GROUP BY e.department_id
HAVING z_avg > 10000
ORDER BY z_avg DESC
LIMIT 2;
在 SELECT 语句执行这些步骤的时候,每个步骤都会产生一个 虚拟表 ,然后将这个虚拟表传入下一个步骤中作为输入。需要注意的是,这些步骤隐含在 SQL 的执行过程中,对于我们来说是不可见的。
2022-6-5
#方法一--------------------------
SELECT last_name,salary
FROM employees
WHERE last_name = 'Abel'; #查询出Abel的工资
SELECT last_name,salary
FROM employees
WHERE salary > 11000 #将数据库中的员工工资与其比较
#方法二---------------------------
SELECT e2.last_name,e2.salary
FROM employees e1,employees e2 #自连接的方式
WHERE e1.last_name = 'Abel'
AND e2.salary > e1.salary;
方法三----------------------------
SELECT last_name,salary
FROM employees
WHERE salary > ( #利用子查询的方式
SELECT salary
FROM employees
WHERE last_name = 'Abel'
);
分类方式一:
我们按内查询的结果返回一条还是多条记录,将子查询分为 单行子查询 、 多行子查询
分类方式二:
操作符 | 含义 |
---|---|
= | equal to |
> | greater than |
>= | greater than or equal to |
< | less than |
<= | less than or equal to |
<> | not equal to |
**题目一:**查询工资大于149号员工工资的员工的信息
**题目二:**返回job_id与141号员工相同,salary比143号员工多的员工姓名,job_id和工资
**题目三:**返回公司工资最少的员工的last_name,job_id和salary
题目四:查询最低工资大于50号部门最低工资的部门id和其最低工资
#题目四:查询最低工资大于50号部门最低工资的部门id和其最低工资
SELECT department_id,MIN(salary)
FROM employees
GROUP BY department_id
HAVING MIN(salary) > (
SELECT MIN(salary)
FROM employees
GROUP BY department_id
HAVING department_id = 50
);
CASE中的子查询:题目:显式员工的employee_id,last_name和location。其中,若员工department_id与location_id为1800的department_id相同,则location为’Canada’,其余则为’USA’
#显式员工的employee_id,last_name和location。其中,若员工department_id与location_id为1800的department_id相同,则location为’Canada’,其余则为’USA’
SELECT employee_id, last_name,
(CASE department_id
WHEN
(SELECT department_id FROM departments
WHERE location_id = 1800)
THEN 'Canada' ELSE 'USA' END) location
FROM employees;
SELECT last_name, job_id
FROM employees
WHERE job_id =(
SELECT job_id
FROM employees
WHERE last_name = 'Haas'
);
#此时的结果为空
SELECT employee_id, last_name
FROM employees
WHERE salary =(
SELECT MIN(salary)
FROM employees
GROUP BY department_id
);
操作符 | 含义 |
---|---|
IN | 等于列表中的任意一个 |
ANY | 需要和单行比较操作符一起使用,和子查询返回的某一个值比较 |
ALL | 需要和单行比较操作符一起使用,和子查询返回的所有值比较 |
SOME | 实际上是ANY的别名,作用相同,一般常使用ANY |
体会ANY 和 ALL 的区别
#返回其它job_id中比job_id为‘IT_PROG’部门任一工资低的员工的员工号、姓名、job_id 以及salary
SELECT employee_id,last_name,job_id,salary
FROM employees
WHERE salary < ANY (
SELECT salary
FROM employees
WHERE job_id = 'IT_PROG'
)
AND job_id != 'IT_PROG';
#返回其它job_id中比job_id为‘IT_PROG’部门所有工资都低的员工的员工号、姓名、job_id以及salary
SELECT employee_id,last_name,job_id,salary
FROM employees
WHERE salary < ALL (
SELECT salary
FROM employees
WHERE job_id = 'IT_PROG'
)
AND job_id != 'IT_PROG';
#查询平均工资最低的部门id
SELECT department_id
FROM employees
GROUP BY department_id
HAVING AVG( salary ) <= ALL (
SELECT AVG(salary)
FROM employees
GROUP BY department_id
);
SELECT last_name
FROM employees
WHERE employee_id NOT IN (
SELECT manager_id
FROM employees
);
如果子查询的执行依赖于外部查询,通常情况下都是因为子查询中的表用到了外部的表,并进行了条件关联,因此每执行一次外部查询,子查询都要重新计算一次,这样的子查询就称之为 关联子查询 。
相关子查询按照一行接一行的顺序执行,主查询的每一行都执行一次子查询
2022-6-6
#查询员工中工资大于本部门平均工资的员工的last_name,salary和其department_id
#方法一--------------------
SELECT last_name,salary,department_id
FROM employees e1
WHERE salary > (
SELECT AVG(salary)
FROM employees
WHERE department_id = e1.department_id
);
#方法二------------------在 FROM 中使用子查询
SELECT last_name,salary,e1.department_id
FROM employees e1,(SELECT department_id,AVG(salary) dept_avg_sal FROM employees GROUP
BY department_id) e2
WHERE e1.`department_id` = e2.department_id
AND e2.dept_avg_sal < e1.`salary`;
#rom型的子查询:子查询是作为from的一部分,子查询要用()引起来,并且要给这个子查询取别名, 把它当成一张“临时的虚拟的表”来使用
#查询员工的id,salary,按照department_name 排序
#多表查询-----------------------------------------------------------
SELECT e.employee_id,e.salary
FROM employees e,departments d
WHERE e.department_id = d.department_id
ORDER BY d.department_name; #会自动过滤department_name为null的字段
#子查询-------------------------------------------------------------
SELECT employee_id,salary
FROM employees e
ORDER BY (
SELECT department_name
FROM departments d
WHERE e.department_id = d.department_id
);
#题目:若employees表中employee_id与job_history表中employee_id相同的数目不小于2,输出这些相同id的员工的employee_id,last_name和其job_id
SELECT e.employee_id, last_name,e.job_id
FROM employees e
WHERE 2 <= (
SELECT COUNT(*)
FROM job_history
WHERE employee_id = e.employee_id
);
**题目:**查询公司管理者的employee_id,last_name,job_id,department_id信息
#查询公司管理者的employee_id,last_name,job_id,department_id信息
#----exists
SELECT employee_id, last_name, job_id, department_id
FROM employees e1
WHERE EXISTS (
SELECT *
FROM employees e2
WHERE e2.manager_id =e1.employee_id
);
#----自连接
SELECT DISTINCT e1.employee_id, e1.last_name, e1.job_id, e1.department_id
FROM employees e1
JOIN employees e2
WHERE e1.employee_id = e2.manager_id;
#----多行子查询
SELECT employee_id,last_name,job_id,department_id
FROM employees
WHERE employee_id IN(
SELECT DISTINCT manager_id
FROM employees
);
**题目2:**查询departments表中,不存在于employees表中的部门的department_id和department_name
#查询departments表中,不存在于employees表中的部门的department_id和department_name
SELECT department_id, department_name
FROM departments d
WHERE NOT EXISTS (
SELECT 'X'
FROM employees
WHERE department_id = d.department_id
);
2022-6-7
使用这种语法一次只能插入一条数据
情况1:为表中所有字段按默认顺序插入数据
#插入一条数据
INSERT INTO departments
VALUES (280,'Pub',100,1700); #插入成功
#插入数据的错误方式
INSERT INTO departments
VALUES (80,'Pub',100,1700);
#报错信息
> 1062 - Duplicate entry '80' for key 'departments.PRIMARY' #主键有唯一性,不能重复插入
> 时间: 0s
情况2:为表中指定的字段插入数据
为表的指定字段插入数据,就是在INSERT语句中只向部分字段中插入值,而其他字段的值为表定义时的
默认值。
在 INSERT 子句中随意列出列名,但是一旦列出,VALUES中要插入的value1,…valuen需要与
column1,…columnn列一一对应。如果类型不同,将无法插入,并且MySQL会产生错误。
INSERT INTO departments(department_id,department_name)
VALUES (290,'zzj');
情况3:插入多条数据
INSERT INTO departments(department_id,department_name)
VALUES (300,'lmg'),
(310,'xxl'),
(320,'zhh');
使用INSERT同时插入多条记录时,MySQL会返回一些在执行单行插入时没有的额外信息,这些信息的含
义如下:
一个同时插入多行记录的INSERT语句等同于多个单行插入的INSERT语句,但是多行的INSERT语句
在处理过程中 效率更高 。因为MySQL执行单条INSERT语句插入多行数据比使用多条INSERT语句
快,所以在插入多条记录时最好选择使用单条INSERT语句的方式插入。
小结
INSERT还可以将SELECT语句查询的结果插入到表中,此时不需要把每一条记录的值一个一个输入,只需
要使用一条INSERT语句和一条SELECT语句组成的组合语句即可快速地从一个或多个表中向一个表中插入
多行 。
举例子:
#前提是查询到的数据与插入表的结构一样
INSERT INTO employees_copy1
SELECT *
FROM employees
WHERE department_id = 90;
在 INSERT 语句中加入子查询。
不必书写 VALUES 子句。
子查询中的值列表应与 INSERT 子句中的列名对应
使用 UPDATE 语法对数据进行更新
可以一次性更新多条数据
如果需要回滚数据,需要保证 DML 前,进行设置:SET AUTOCOMMIT = FALSE;
使用 WHERE 子句指定需要更新的数据
#指定first_name的员工的employee_id为110
UPDATE zzj
SET employee_id = 110
WHERE first_name = 'Lex'; #假如舍弃 where 则全部数据将会被更新
更新数据中的完整性错误
UPDATE employees
SET department_id = 55 #此时第55号部门不存在
WHERE department_id = 110;
#1452 - Cannot add or update a child row: a foreign key constraint fails (`atguigudb`.`employees`, CONSTRAINT `emp_dept_fk` FOREIGN KEY (`department_id`) REFERENCES `departments` (`department_id`))
使用 DELETE 语句从表中删除数据
DELETE FROM zzj
WHERE employee_id = 110;
#当没有 where 时,则默认删除所有的数据量
删除时发生的完整性错误:
DELETE FROM departments
WHERE department_id = 60;
#不能删除包含在另一个表中用作外键的主键的行。
什么叫计算列呢?简单来说就是某一列的值是通过别的列计算得来的。例如,a列值为1、b列值为2,c列
不需要手动插入,定义a+b的结果为c的值,那么c就是计算列,是通过别的列计算得来的。
在MySQL 8.0中,CREATE TABLE 和 ALTER TABLE 中都支持增加计算列。下面以CREATE TABLE为
例进行讲解。
举例:定义数据表tb1,然后定义字段id、字段a、字段b和字段c,其中字段c为计算列,用于计算a+b的
值。 首先创建测试表tb1,语句如下
CREATETABLEtb1(
id INT,
a INT,
b INT,
c INT GENERATED ALWAYS AS (a+b) VIRTUAL
);
插入数据
INSERT INTO tb1(a,b)VALUES(100,200);
查询数据表tb1中的数据
SELECT * FROM tb1;
更新数据中的数据
UPDATE tb1 SET a = 500;
2022-6-8
类型 | 类型举例 |
---|---|
整型类型 | TINYINT、SMALLINT、MEDIUMINT、INT(或INTEGER)、BIGINT |
浮点型 | FLOAT、DOUBLE |
定点数类型 | DECIMAL |
位类型 | BIT |
日期时间类型 | YEAR、TIME、DATE、DATETIME、TIMESTAMP |
文本字符串类型 | CHAR、VARCHAR、TINYTEXT、TEXT、MEDIUMTEXT、LONGTEXT |
枚举类型 | ENUM |
集合类型 | SET |
二进制字符串类型 | BINARY、VARBINARY、TINYBLOB、BLOB、MEDIUMBLOB、LONGBLOB |
JSON类型 | JSON对象、JSON数组 |
空间数据类型 | 单值类型:GEOMETRY、POINT、LINESTRING、POLYGON; 集合类型:MULTIPOINT、MULTILINESTRING、MULTIPOLYGON、 GEOMETRYCOLLECTION |
常见数据类型的属性,如下:
MySQL | 含义 |
---|---|
NULL | 数据列可包含NULL值 |
NOT NULL | 数据不允许有NULL值 |
DEFAULT | 默认值 |
PRIMARY KEY | 主键 |
AUTO_INCREMENT | 自动递增,适用于整数类型 |
UNSIGNED | 无符号 |
CHARACTER SRT name | 指定一个字符串 |
整数类型一共有5种,包括 TINYINT、SMALLINT、MEDIUMINT、INT(INTEGER)和 BIGINT。
它们的区别如下表所示:
整数类型 | 字节 | 有符号取值范围 | 无符号数取值范围 |
---|---|---|---|
TINYINT | 1 | -128~127 | 0~255 |
SMALLINT | 2 | -32768~32767 | 0~65535 |
MEDIUMINT | 3 | -8388608~8388607 | 0~16777215 |
INT、INTEGER | 4 | -2147483648~2147483647 | 0~4294967295 |
BIGINT | 8 | 9223372036854775808~9223372036854775807 | 0~18446744073709551615 |
整数类型的可选属性有三个:
2.1–M
M: 表示显示宽度,M的取值范围是(0, 255)。例如,int(5):当数据宽度小于5位的时候在数字前面需要用字符填满宽度。该项功能需要配合“ ZEROFILL ”使用,表示用“0”填满宽度,否则指定显示宽度无效。
如果设置了显示宽度,那么插入的数据宽度超过显示宽度限制,会不会截断或插入失败?
整型数据类型可以在定义表结构时指定所需要的显示宽度,如果不指定,则系统为每一种类型指定默认
的宽度值。
举例子:
CREATE TABLE test_int1 ( x TINYINT, y SMALLINT, z MEDIUMINT, m INT, n BIGINT );
TINYINT(—tinyint—)有符号数和无符号数的取值范围分别为-128127和0255,由于负号占了一个数字位,因此TINYINT默认的显示宽度为4。同理,其他整数类型的默认显示宽度与其有符号数的最小值的宽度相同。
举例子:
2.2—UNSIGNED(—unsigned—)
2.3----ZEROFILL
2022-6-12
存储过程的参数类型可以是IN、out和nout。根据这点分类如下:
参数详细说明:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IYpkfS0j-1657463727869)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20220612174413014.png)]
存储过程体:
1. BEGIN…END:BEGIN…END 中间包含了多个语句,每个语句都以(;)号为结束符。
2. DECLARE:DECLARE 用来声明变量,使用的位置在于 BEGIN…END 语句中间,而且需要在其他语句使用之前进
行变量的声明。
3. SET:赋值语句,用于对变量进行赋值。
4. SELECT… INTO:把从数据表中查询的结果存放到变量中,也就是为变量赋值。
需要设置新的结束标记:
DELIMITER 新的结束符
例子详细:
DELIMITER $
CREATE PROCEDURE 存储过程名(IN|OUT|INOUT 参数名 参数类型,...)
[characteristics ...]
BEGIN
sql语句1;
sql语句2;
END $
CREATE PROCEDURE 存储过程名(IN|OUT|INOUT 参数名 参数类型,...)
[characteristics ...]
BEGIN
存储过程体
END
#举例1:创建存储过程select_all_data(),查看 emps 表的所有数据
CREATE PROCEDURE select_all_data()
BEGIN
SELECT *
FROM zzj;
end
CALL select_all_data();
#举例2:创建存储过程avg_employee_salary(),返回所有员工的平均工资
CREATE DEFINER=`root`@`localhost` PROCEDURE `avg_emp`()
BEGIN
SELECT AVG(salary)
FROM employees;
END
CALL avg_emp();
#举例3:创建存储过程show_min_salary(),查看“emps”表的最低薪资值。并将最低薪资通过OUT参数“ms”输出
CREATE DEFINER=`root`@`localhost` PROCEDURE `show_min`(OUT ms DOUBLE)
BEGIN
SELECT MIN(salary) INTO ms
FROM employees;
END
CALL show_min(@ms); #@用户自定义的变量
#举例4:创建存储过程show_someone_salary(),查看“emps”表的某个员工的薪资,并用IN参数empname输入员工姓名
CREATE DEFINER=`root`@`localhost` PROCEDURE `show_someone_salary2`(IN emp_name VARCHAR(20),OUT emp_salary DOUBLE)
BEGIN
SELECT salary INTO emp_salary
FROM employees
WHERE last_name = emp_name;
END
#举例5:创建存储过程show_mgr_name(),查询某个员工领导的姓名,并用INOUT参数“empname”输入员工姓名,输出领导的姓名。
CREATE DEFINER=`root`@`localhost` PROCEDURE `show_mgr_name`(INOUT emp_name VARCHAR(20))
BEGIN
SELECT last_name INTO emp_name
FROM employees
WHERE employee_id = (SELECT manager_id FROM employees WHERE last_name = emp_name); #查询出领导的id,进而查出名字
END
存储过程有多种调用方法。存储过程必须使用CALL语句调用,并且存储过程和数据库相关,如果要执行其他数据库中的存储过程,需要指定数据库名称,例如CALL dbname.procname。
CALL 存储过程名(实参列表)
格式:
1、调用in模式的参数
CALL sp1('值');
2、调用out模式的参数
SET @name;
CALL sp1(@name);
SELECT @name;
3、调用inout模式的参数
SET @name;
CALL sp1(@name);
SELECT @name;
举例1:
DELIMITER //
CREATE PROCEDURE CountProc(IN sid INT,OUT num INT)
BEGIN
SELECT COUNT(*) INTO num FROM fruits
WHERE s_id = sid;
END //
DELIMITER ;
调用存储过程:
mysql> CALL CountProc (101, @num);
Query OK, 1 row affected (0.00 sec)
查看返回结果:
mysql> SELECT @num;
该存储过程返回了指定 s_id=101 的水果商提供的水果种类,返回值存储在num变量中,使用SELECT查看,返回结果为3。
举例2:
#创建存储过程,实现累加运算,计算 1+2+…+n 等于多少。具体的代码如下:
DELIMITER //
CREATE PROCEDURE `add_num`(IN n INT)
BEGIN
DECLARE i INT;
DECLARE sum INT;
SET i = 1;
SET sum = 0;
WHILE i <= n DO
SET sum = sum + i;
SET i = i +1;
END WHILE;
SELECT sum;
END //
DELIMITER ;
如果你用的是 Navicat 工具,那么在编写存储过程的时候,Navicat 会自动设置 DELIMITER 为其他符号,我们不需要再进行 DELIMITER 的操作。
直接使用 CALL add_num(50); 即可。这里我传入的参数为 50,也就是统计 1+2+…+50 的积累之和
前面学习了很多函数,使用这些函数可以对数据进行的各种处理操作,极大地提高用户对数据库的管理效率。MySQL支持自定义函数,定义好之后,调用方式与调用MySQL预定义的系统函数一样。
学过的函数:LENGTH、SUBSTR、CONCAT等
语法格式:
CREATE FUNCTION 函数名(参数名 参数类型,...)
RETURNS 返回值类型
[characteristics ...]
BEGIN
函数体 #函数体中肯定有 RETURN 语句
END
说明:
在MySQL中,存储函数的使用方法与MySQL内部函数的使用方法是一样的。换言之,用户自己定义的存储函数与MySQL内部函数是一个性质的。区别在于,存储函数是 用户自己定义 的,而内部函数是MySQL的 开发者定义的。
SELECT 函数名(实参列表)
举例1:
#创建存储函数,名称为email_by_name(),参数定义为空,该函数查询Abel的email,并返回,数据类型为字符串型。
CREATE DEFINER=`root`@`localhost` FUNCTION `email_retrun`() RETURNS varchar(30) CHARSET utf8mb3
READS SQL DATA
DETERMINISTIC
BEGIN
RETURN (SELECT email FROM employees WHERE last_name = 'Abel');
END
SELECT email_by_name(); #调用,Navicat不用调用
#创建存储函数,名称为email_by_id(),参数传入emp_id,该函数查询emp_id的email,并返回,数据类型为字符串型
SET GLOBAL log_bin_trust_function_creators = 1;
#创建带参数的函数时0
CREATE DEFINER=`root`@`localhost` FUNCTION `email_by_id`(emp_id INT)
RETURNS varchar(25) CHARSET utf8mb3
BEGIN
RETURN (SELECT email FROM employees WHERE employee_id = emp_id);
END
注意:
关键字 | 调用语法 | 返回值 | 应用场景 | |
---|---|---|---|---|
存储过程 | PROCEDURE | CALL 存储过程() | 理解为有0个或多个 | 一般用于更新 |
存储函数 | FUNCTION | SELECT 函数 () | 只能是一个 | 一般用于查询结果为一个值并 返回时 |
2022-6-16
创建完成后,怎么知道我们创建的存储过程,存储是否创建成功了呢
MySQL存储了存储过程和函数的状态信息,用户可以使用show status语句或show create语句进行查看,也可以直接从系统的information_schema数据库中查询,这里介绍3中方法
使用show create语句查看
SHOW CREATE {PROCEDURE | FUNCTION} 存储过程名或函数名
SHOW CREATE PROCEDURE select_all_data;#查看存储 过程 的基本信息
SHOW CREATE FUNCTION email_by_id ; #查看存储 函数 的基本信息
使用show status语句查看
SHOW FUNCTION STATUS LIKE 'email_by_id'; #符合模糊查询的基本规则
SHOW PROCEDURE STATUS LIKE 'select_all_data';
从information_schema.Routines表中查看存储过程和函数的信息
**说明:**如果在MySQL数据库中存在存储过程和函数名称相同的情况,最好指定ROUTINE_TYPE查询条件来
指明查询的是存储过程还是函数。
MySQL中存储过程和函数的信息存储在information_schema数据库下的Routines表中。可以通过查询该表
的记录来查询存储过程和函数的信息。其基本语法形式如下:
举例:从Routines表中查询名称为CountProc的存储函数的信息,代码如下
SELECT * FROM information_schema.Routines
WHERE ROUTINE_NAME='count_by_id' AND ROUTINE_TYPE = 'FUNCTION'; #这个位置区分大小写
说明:
修改存储过程或函数,不影响存储过程或函数功能,只是修改相关特性。使用ALTER语句实现
其中,characteristic指定存储过程或函数的特性,其取值信息与创建存储过程、函数时的取值信息略有
不同。
特性说明:
修改存储过程使用ALTER PROCEDURE语句,修改存储函数使用ALTER FUNCTION语句。但是,这两
个语句的结构是一样的,语句中的所有参数也是一样的。
举例子:
#修改存储过程CountProc的定义。将读写权限改为MODIFIES SQL DATA,并指明调用者可以执行,代码如下:
ALTER PROCEDURE CountProc
MODIFIES SQL DATA
SQL SECURITY INVOKER ;
IF EXISTS:如果程序或函数不存储,它可以防止发生错误,产生一个用SHOW WARNINGS查看的警告。
查看所有或部分系统变量:
#查看所有全局变量
SHOW GLOBAL VARIABLES;
#查看所有会话变量
SHOW SESSION VARIABLES;
或
SHOW VARIABLES; #默认会话级别的变量
#查看满足条件的部分系统变量。
SHOW GLOBAL VARIABLES LIKE 'admin_%';
#查看满足条件的部分会话变量
SHOW SESSION VARIABLES LIKE 'admin_%';
查看指定系统变量:
作为 MySQL 编码规范,MySQL 中的系统变量以 两个“@” 开头,其中“@@global”仅用于标记全局系统变
量,“@@session”仅用于标记会话系统变量。“@@”首先标记会话系统变量,如果会话系统变量不存在,则标记全局系统变量。
#查看指定的系统变量的值
SELECT @@global.变量名;
SELECT @@global.character_set_client; #utf8mb4
#查看指定的会话变量的值
SELECT @@session.变量名;
SELECT @@session.pseudo_thread_id; #8
#或者
SELECT @@变量名; #现在会话中找,最后再去系统变量里找
有些时候,数据库管理员需要修改系统变量的默认值,以便修改当前会话或者MySQL服务实例的属性、
特征。具体方法:
举例:
#为某个系统变量赋值
#方式1:
SET @@global.变量名=变量值;
#方式2:
SET GLOBAL 变量名=变量值;
#为某个会话变量赋值
#方式1:
SET @@session.变量名=变量值;
#方式2:
SET SESSION 变量名=变量值;
用户变量是用户自己定义的,作为MySQL编码规范,MySQL中的用户变量以==一个’@'==开头。根据作用范围不同,有分为 会话用户变量 和 局部变量。
变量的定义
#方式1:“=”或“:=”
SET @用户变量 = 值;
SET @用户变量 := 值;
#方式2:“:=” 或 INTO关键字
SELECT @用户变量 := 表达式 [FROM 等子句];
SELECT 表达式 INTO @用户变量 [FROM 等子句];
查看用户变量的值 (查看、比较、运算等)
SELECT @用户变量
举例子:
SET @a = 1;
SELECT @a;
SELECT @num := COUNT(*) FROM employees;
SELECT @num;
SELECT AVG(salary) INTO @avgsalary FROM employees;
SELECT @avgsalary;
SELECT @big; #查看某个未声明的变量时,将得到NULL值
定义变量
DECLARE myparam INT DEFAULT 100;
给变量赋值
#方式一
SET enp_name = 'Abel';
#方式二
SELECT last_name,salary INTO emp_name,sal FROM employees; #给变量赋值
使用变量
SELECT emp_name;
综合例子
#声明局部变量,并分别赋值为employees表中employee_id为102的last_name和salary
DELIMITER //
CREATE PROCEDURE set_value()
BEGIN
DECLARE emp_name VARCHAR(25); #定义变量
DECLARE sal DOUBLE(10,2);
SELECT last_name,salary INTO emp_name,sal #给变量赋值
FROM employees
WHERE employee_id = 102;
SELECT emp_name,sal;
END //
DELIMITER ;
#声明两个变量,求和并打印 (分别使用会话用户变量、局部变量的方式实现)
#方式1:使用用户变量-----------------------
SET @m=1;
SET @n=1;
SET @sum=@m+@n;
SELECT @sum;
#方式2:使用局部变量----------------------
DELIMITER //
CREATE PROCEDURE add_value()
BEGIN
#局部变量
DECLARE m INT DEFAULT 1;
DECLARE n INT DEFAULT 3;
DECLARE SUM INT;
SET SUM = m+n;
SELECT SUM;
END //
DELIMITER ;
定义条件 是事先定义程序执行过程中可能遇到的问题, 处理程序 定义了在遇到问题时应当采取的处理方式,并且保证存储过程或函数在遇到警告或错误时能继续执行。这样可以增强存储程序处理问题的能力,避免程序异常停止运行。
**说明:**定义条件和处理程序在存储过程、存储函数中都是支持的。
INSERT INTO employees(last_name)
VALUES('TOM');
#1364 - Field 'email' doesn't have a default value
#email字段必须进行赋值----因为它不允许为空
DELIMITER //
CREATE PROCEDURE UpdateDataNoCondition()
BEGIN
SET @x = 1;
UPDATE employees SET email = NULL WHERE last_name = 'Abel';
SET @x = 2;
UPDATE employees SET email = 'aabbel' WHERE last_name = 'Abel';
SET @x = 3;
END //
DELIMITER ;
#报错信息
1048 - Column 'email' cannot be null
定义条件就是给MySQL中的错误码命名,这有助于存储的程序代码更清晰。它将一个 错误名字 和 指定的错误条件 关联起来。这个名字可以随后被用在定义处理程序的 DECLARE HANDLER 语句中。
定义条件使用DECLARE语句,语法格式如下 :
DECLARE 错误名称 CONDITION FOR 错误码(或错误条件)
错误码的说明:
举例子
#定义“Field_Not_Be_NULL”错误名与MySQL中违反非空约束的错误类型是“ERROR 1048 (23000)”对应
#使用MySQL_error_code
DECLARE Field_Not_Be_NULL CONDITION FOR 1048;
#使用sqlstate_value
DECLARE Field_Not_Be_NULL CONDITION FOR SQLSTATE '23000'; #怕起冲突所以使用SQLstate区分开来
可以为SQL执行过程中发生的某种类型的错误定义特殊的处理程序。定义处理程序时,使用DECLARE语句
的语法如下:
DECLARE 处理方式 HANDLER FOR 错误类型 处理语句
处理方式:处理方式有3个取值:CONTINUE、EXIT、UNDO。
错误类型(即条件)可以有如下取值:
SQLSTATE ‘字符串错误码’ :表示长度为5的sqlstate_value类型的错误代码;
MySQL_error_code :匹配数值类型错误代码;
错误名称 :表示DECLARE … CONDITION定义的错误条件名称。
SQLWARNING :匹配所有以01开头的SQLSTATE错误代码;
NOT FOUND :匹配所有以02开头的SQLSTATE错误代码;
SQLEXCEPTION :匹配所有没有被SQLWARNING或NOT FOUND捕获的SQLSTATE错误代码;
**处理语句:**如果出现上述条件之一,则采用对应的处理方式,并执行指定的处理语句。语句可以是像“ SET 变量 = 值 ”这样的简单语句,也可以是使用 BEGIN … END 编写的复合语句。
举例子:
#方法1:捕获sqlstate_value
DECLARE CONTINUE HANDLER FOR SQLSTATE '42S02' SET @info = 'NO_SUCH_TABLE';
#方法2:捕获mysql_error_value
DECLARE CONTINUE HANDLER FOR 1146 SET @info = 'NO_SUCH_TABLE';
#方法3:先定义条件,再调用
DECLARE no_such_table CONDITION FOR 1146;
DECLARE CONTINUE HANDLER FOR NO_SUCH_TABLE SET @info = 'NO_SUCH_TABLE';
#方法4:使用SQLWARNING
DECLARE EXIT HANDLER FOR SQLWARNING SET @info = 'ERROR';
#方法5:使用NOT FOUND
DECLARE EXIT HANDLER FOR NOT FOUND SET @info = 'NO_SUCH_TABLE';
#方法6:使用SQLEXCEPTION
DECLARE EXIT HANDLER FOR SQLEXCEPTION SET @info = 'ERROR';
**例题一、**在存储过程中,定义处理程序,捕获sqlstate_value值,当遇到MySQL_error_code值为1048时,执行
CONTINUE操作,并且将@proc_value的值设置为-1。
CREATE DEFINER=`root`@`localhost` PROCEDURE `UpdateDataNoCondition`()
BEGIN
DECLARE CONTINUE HANDLER FOR 1048 SET @proc_value = -1; #程序不报错,被处理,程序继续往下运行
SET @x = 1;
UPDATE employees SET email = NULL WHERE last_name = 'Abel';
SET @x = 2;
UPDATE employees SET email = 'aabbel' WHERE last_name = 'Abel';
SET @x = 3;
SELECT @proc_value; #这里的值是-1
SELECT @X; #这里的值是3
END
游标必须在声明处理程序之前被声明,并且变量和条件还必须在声明游标或处理程序之前被声明。
如果我们想要使用游标,一般需要经历四个步骤。不同的 DBMS 中,使用游标的语法可能略有不同。
第一步:声明游标
DECLARE cursor_name CURSOR FOR select_statement
这个语法适用于 MySQL,SQL Server,DB2 和 MariaDB。如果是用 Oracle 或者 PostgreSQL,需要写成:
DECLARE cursor_name CURSOR IS select_statement;
要使用 SELECT 语句来获取数据结果集,而此时还没有开始遍历数据,这里 select_statement 代表的是
SELECT 语句,返回一个用于创建游标的结果集。
DECLARE cur_emp CURSOR FOR
SELECT employee_id,salary FROM employees;
#---------------------------------------------
DECLARE cursor_fruit CURSOR FOR
SELECT f_name, f_price FROM fruits ;
第二步:打开游标
打开游标的语法如下:
OPEN cursor_name
当我们定义好游标之后,如果想要使用游标,必须先打开游标。打开游标的时候 SELECT 语句的查询结果集就会送到游标工作区,为后面游标的 逐条读取 结果集中的记录做准备
OPEN cur_emp ;
第三歩:使用游标
这句的作用是使用 cursor_name 这个游标来读取当前行,并且将数据保存到 var_name 这个变量中,游
标指针指到下一行。如果游标读取的数据行有多个列名,则在 INTO 关键字后面赋值给多个变量名即可。
FETCH cur_emp INTO emp_id, emp_sal ; #但是--emp_id, emp_sal是在游标声明前声明好的
第四步:关闭游标
CLOSE cursor_name
有 **OPEN 就会有 CLOSE,**也就是打开和关闭游标。当我们使用完游标后需要关闭掉该游标。因为游标会
占用系统资源 ,如果不及时关闭,游标会一直保持到存储过程结束,影响系统运行的效率。而关闭游标
的操作,会释放游标占用的系统资源。
关闭游标之后,我们就不能再检索查询结果中的数据行,如果需要检索只能再次打开游标。
#创建存储过程“get_count_by_limit_total_salary()”,声明IN参数 limit_total_salary,DOUBLE类型;声明OUT参数total_count,INT类型。函数的功能可以实现累加薪资最高的几个员工的薪资值,直到薪资总和达到limit_total_salary参数的值,返回累加的人数给total_count。
DELIMITER //
CREATE PROCEDURE get_count_by_limit_total_salary(IN limit_total_salary DOUBLE,OUT
total_count INT)
BEGIN
DECLARE sum_salary DOUBLE DEFAULT 0; #记录累加的总工资
DECLARE cursor_salary DOUBLE DEFAULT 0; #记录某一个工资值
DECLARE emp_count INT DEFAULT 0; #记录循环个数
#定义游标
DECLARE emp_cursor CURSOR FOR SELECT salary FROM employees ORDER BY salary DESC;
#打开游标
OPEN emp_cursor;
REPEAT
#使用游标(从游标中获取数据)
FETCH emp_cursor INTO cursor_salary;
SET sum_salary = sum_salary + cursor_salary;
SET emp_count = emp_count + 1;
UNTIL sum_salary >= limit_total_salary
END REPEAT;
SET total_count = emp_count;
#关闭游标
CLOSE emp_cursor;
END //
DELIMITER ;
**1、**在实际开发中,我们经常会遇到这样的情况:有 2 个或者多个相互关联的表,如 商品信息 和 库存信息 分别存放在 2 个不同的数据表中,我们在添加一条新商品记录的时候,为了保证数据的完整性,必须同时在库存表中添加一条库存记录。
**2、**这样一来,我们就必须把这两个关联的操作步骤写到程序里面,而且要用 事务 包裹起来,确保这两个操作成为一个 原子操作 ,要么全部执行,要么全部不执行。要是遇到特殊情况,可能还需要对数据进行手动维护,这样就很 容易忘记其中的一步 ,导致数据缺失。
**3、**这个时候,咱们可以使用触发器。你可以创建一个触发器,让商品信息数据的插入操作自动触发库存数
据的插入操作。这样一来,就不用担心因为忘记添加库存数据而导致的数据缺失了。
语法结构
CREATE TRIGGER 触发器名称
{BEFORE|AFTER} {INSERT|UPDATE|DELETE} ON 表名
FOR EACH ROW
触发器执行的语句块;
语法结构说明
#创建触发器:创建名称为before_insert的触发器,向test_trigger数据表插入数据之前,向test_trigger_log数据表中插入before_insert的日志信息。
CREATE TRIGGER before_insert_test_tri
BEFORE INSERT ON test_trigger
FOR EACH ROW
BEGIN
INSERT INTO test_trigger_log (t_log)
VALUES('before_insert_test_tri');
END
#开始向test_trigger表中插入数据-----
INSERT INTO test_trigger(id)
VALUES(12);
#创建名称为after_insert的触发器,向test_trigger数据表插入数据之后,向test_trigger_log数据表中插入after_insert的日志信息。
CREATE TRIGGER after_insert_test_tri
AFTER INSERT ON test_trigger
FOR EACH ROW #---立即执行
BEGIN
INSERT INTO test_trigger_log (t_log)
VALUES('after_insert_test_tri');
END
#开始向test_trigger表中插入数据
INSERT INTO test_trigger(id)
VALUES(13);
#定义触发器“salary_check_trigger”,基于员工表“employees”的INSERT事件,在INSERT之前检查将要添加的新员工薪资是否大于他领导的薪资,如果大于领导薪资,则报sqlstate_value为'HY000'的错误,从而使得添加失败。
CREATE TRIGGER salary_check_trigger_emp
BEFORE INSERT ON employees
FOR EACH ROW
BEGIN
DECLARE mar_salary DOUBLE;
SELECT salary INTO mar_salary FROM employees WHERE employee_id = NEW.manager_id;
IF mar_salary < NEW.salary
THEN SIGNAL SQLSTATE 'HY000' SET MESSAGE_TEXT = '薪资高于领导薪资错误';
END IF;
END
查看触发器是查看数据库中已经存在的触发器的定义、状态和语法信息等。
**方式一:**查看当前数据库的所有触发器的定义
SHOW TRIGGER \G;
**方式二:**查看当前数据库中某个触发器的定义
SHOW CREATE TRIGGER 触发器名;
**方式三:**从系统库information_schema的TRIGGERS表中查询“salary_check_trigger”触发器的信息。
SELECT * FROM information_schema.TRIGGERS;
触发器也是数据库对象,删除触发器也用DROP语句,语法格式如下:
DROP TRIGGER IF EXISTS 触发器名;
BY salary DESC;
#打开游标
OPEN emp_cursor;
REPEAT
#使用游标(从游标中获取数据)
FETCH emp_cursor INTO cursor_salary;
SET sum_salary = sum_salary + cursor_salary;
SET emp_count = emp_count + 1;
UNTIL sum_salary >= limit_total_salary
END REPEAT;
SET total_count = emp_count;
#关闭游标
CLOSE emp_cursor;
END //
DELIMITER ;
#### 19.4、小结
[外链图片转存中...(img-jMqPBEnq-1657463727872)]
## 20、触发器
>**1、**在实际开发中,我们经常会遇到这样的情况:有 2 个或者多个相互关联的表,如 ==商品信息== 和 ==库存信息== 分别存放在 2 个不同的数据表中,我们在添加一条新商品记录的时候,为了保证数据的完整性,必须同时在库存表中添加一条库存记录。
>**2、**这样一来,我们就必须把这两个关联的操作步骤写到程序里面,而且要用 事务 包裹起来,确保这两个操作成为一个 原子操作 ,要么全部执行,要么全部不执行。要是遇到特殊情况,可能还需要对数据进行手动维护,这样就很 容易忘记其中的一步 ,导致数据缺失。
>**3、**这个时候,咱们可以使用触发器。你可以创建一个触发器,让商品信息数据的插入操作自动触发库存数
>据的插入操作。这样一来,就不用担心因为忘记添加库存数据而导致的数据缺失了。
#### 20.1、概念
- MySQL从 ==5.0.2== 版本开始支持触发器。MySQL的触发器和存储过程一样,都是嵌入到MySQL服务器的一段程序。
- 触发器是由 ==事件来触发== 某个操作,这些事件包括 ==INSERT 、 UPDATE 、 DELETE== 事件。所谓事件就是指用户的动作或者触发某项行为。如果定义了触发程序,当数据库执行这些语句时候,就相当于事件发生了,就会 ==自动== 激发触发器执行相应的操作。
- 当对数据表中的数据执行插入、更新和删除操作,需要自动执行一些数据库逻辑时,可以使用触发器来实现
#### 20.2、触发器的创建
- 语法结构
- ```sql
CREATE TRIGGER 触发器名称
{BEFORE|AFTER} {INSERT|UPDATE|DELETE} ON 表名
FOR EACH ROW
触发器执行的语句块;
```
- 语法结构说明
- **表名 :**表示触发器监控的对象。
- **BEFORE|AFTER :**表示触发的时间。BEFORE 表示在事件之前触发;AFTER 表示在事件之后触发。
- **INSERT|UPDATE|DELETE :**表示触发的事件。
- **INSERT** 表示插入记录时触发;
- **UPDATE** 表示更新记录时触发;
- **DELETE** 表示删除记录时触发。
- **触发器执行的语句块** :可以是单条SQL语句,也可以是由BEGIN…END结构组成的复合语句块
#### 20.3、创建触发器语法
- ```sql
#创建触发器:创建名称为before_insert的触发器,向test_trigger数据表插入数据之前,向test_trigger_log数据表中插入before_insert的日志信息。
CREATE TRIGGER before_insert_test_tri
BEFORE INSERT ON test_trigger
FOR EACH ROW
BEGIN
INSERT INTO test_trigger_log (t_log)
VALUES('before_insert_test_tri');
END
#开始向test_trigger表中插入数据-----
INSERT INTO test_trigger(id)
VALUES(12);
[外链图片转存中…(img-CKxnRVFy-1657463727872)]
[外链图片转存中…(img-xNS84STS-1657463727873)]
#创建名称为after_insert的触发器,向test_trigger数据表插入数据之后,向test_trigger_log数据表中插入after_insert的日志信息。
CREATE TRIGGER after_insert_test_tri
AFTER INSERT ON test_trigger
FOR EACH ROW #---立即执行
BEGIN
INSERT INTO test_trigger_log (t_log)
VALUES('after_insert_test_tri');
END
#开始向test_trigger表中插入数据
INSERT INTO test_trigger(id)
VALUES(13);
#定义触发器“salary_check_trigger”,基于员工表“employees”的INSERT事件,在INSERT之前检查将要添加的新员工薪资是否大于他领导的薪资,如果大于领导薪资,则报sqlstate_value为'HY000'的错误,从而使得添加失败。
CREATE TRIGGER salary_check_trigger_emp
BEFORE INSERT ON employees
FOR EACH ROW
BEGIN
DECLARE mar_salary DOUBLE;
SELECT salary INTO mar_salary FROM employees WHERE employee_id = NEW.manager_id;
IF mar_salary < NEW.salary
THEN SIGNAL SQLSTATE 'HY000' SET MESSAGE_TEXT = '薪资高于领导薪资错误';
END IF;
END
查看触发器是查看数据库中已经存在的触发器的定义、状态和语法信息等。
**方式一:**查看当前数据库的所有触发器的定义
SHOW TRIGGER \G;
**方式二:**查看当前数据库中某个触发器的定义
SHOW CREATE TRIGGER 触发器名;
**方式三:**从系统库information_schema的TRIGGERS表中查询“salary_check_trigger”触发器的信息。
SELECT * FROM information_schema.TRIGGERS;
触发器也是数据库对象,删除触发器也用DROP语句,语法格式如下:
DROP TRIGGER IF EXISTS 触发器名;
[外链图片转存中…(img-F6qq9I7r-1657463727873)]