From 085239451d20ca994bffb8c9ce571cde3d14db64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=84=E7=8F=8A=E7=8F=8A?= <3106036048@qq.com> Date: Wed, 25 Oct 2023 23:25:29 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E4=BD=9C=E4=B8=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- "48 \345\272\204\347\217\212\347\217\212/20230920 RBAC.md" | 3 ++- .../20231014 \345\201\232\347\273\203\344\271\240.md" | 1 + ...200\201\345\270\210\350\256\262\351\242\230\347\233\256.md" | 1 + ...237\245\350\257\206\347\202\271\351\233\206\345\220\210.md" | 0 4 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 "48 \345\272\204\347\217\212\347\217\212/20231014 \345\201\232\347\273\203\344\271\240.md" create mode 100644 "48 \345\272\204\347\217\212\347\217\212/20231015 \350\200\201\345\270\210\350\256\262\351\242\230\347\233\256.md" create mode 100644 "48 \345\272\204\347\217\212\347\217\212/20231025 \347\237\245\350\257\206\347\202\271\351\233\206\345\220\210.md" diff --git "a/48 \345\272\204\347\217\212\347\217\212/20230920 RBAC.md" "b/48 \345\272\204\347\217\212\347\217\212/20230920 RBAC.md" index cd7e326..194f8c8 100644 --- "a/48 \345\272\204\347\217\212\347\217\212/20230920 RBAC.md" +++ "b/48 \345\272\204\347\217\212\347\217\212/20230920 RBAC.md" @@ -1,6 +1,7 @@ ## RBAC是一种权限系统管理模型 -**RBAC**:基本模型有三个元素:用户、角色和权限。模型设计基于“多对多”原则,即多个用户可以具有相同的角色,一个用户可以具有多个角色。同样,可以将同一权限分配给多个角色,也可以将同一角色分配给多个权限。 +**RBAC**:什么是RBAC +RBAC。英文名称Role-Based Access Control。顾名思义,就是基于**角色的权限控制系统**。理解的基本思路就是,在角色的前提下,**按照角色不同将权限分配到对应的角色,再将管理员设置成对应的角色,进而管理员获得权限。在管理员请求某个业务权限的时候,从数据库中查询该管理员所拥有的权限,并将请求的业务权限跟数据库比对。以此进行权限控制** 复习复习我又忘记这个了啊啊啊啊啊啊啊啊啊啊啊啊啊啊!!!!! diff --git "a/48 \345\272\204\347\217\212\347\217\212/20231014 \345\201\232\347\273\203\344\271\240.md" "b/48 \345\272\204\347\217\212\347\217\212/20231014 \345\201\232\347\273\203\344\271\240.md" new file mode 100644 index 0000000..d45a476 --- /dev/null +++ "b/48 \345\272\204\347\217\212\347\217\212/20231014 \345\201\232\347\273\203\344\271\240.md" @@ -0,0 +1 @@ +这天好像是做练习,然后去学点其他的好了 qwq \ No newline at end of file diff --git "a/48 \345\272\204\347\217\212\347\217\212/20231015 \350\200\201\345\270\210\350\256\262\351\242\230\347\233\256.md" "b/48 \345\272\204\347\217\212\347\217\212/20231015 \350\200\201\345\270\210\350\256\262\351\242\230\347\233\256.md" new file mode 100644 index 0000000..0fb0432 --- /dev/null +++ "b/48 \345\272\204\347\217\212\347\217\212/20231015 \350\200\201\345\270\210\350\256\262\351\242\230\347\233\256.md" @@ -0,0 +1 @@ +了解了一下多对多的关系判断,每次都感觉自己又行了,做题又是依托shit \ No newline at end of file diff --git "a/48 \345\272\204\347\217\212\347\217\212/20231025 \347\237\245\350\257\206\347\202\271\351\233\206\345\220\210.md" "b/48 \345\272\204\347\217\212\347\217\212/20231025 \347\237\245\350\257\206\347\202\271\351\233\206\345\220\210.md" new file mode 100644 index 0000000..e69de29 -- Gitee From bb9bb492e6207dbaf04beda1c6adebce0a026f10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=84=E7=8F=8A=E7=8F=8A?= <3106036048@qq.com> Date: Wed, 25 Oct 2023 23:27:35 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E4=BD=9C=E4=B8=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...06\347\202\271\351\233\206\345\220\210.md" | 0 ...06\347\202\271\351\233\206\345\220\210.md" | 1119 +++++++++++++++++ 2 files changed, 1119 insertions(+) delete mode 100644 "48 \345\272\204\347\217\212\347\217\212/20231025 \347\237\245\350\257\206\347\202\271\351\233\206\345\220\210.md" create mode 100644 "48 \345\272\204\347\217\212\347\217\212/20231025\347\237\245\350\257\206\347\202\271\351\233\206\345\220\210.md" diff --git "a/48 \345\272\204\347\217\212\347\217\212/20231025 \347\237\245\350\257\206\347\202\271\351\233\206\345\220\210.md" "b/48 \345\272\204\347\217\212\347\217\212/20231025 \347\237\245\350\257\206\347\202\271\351\233\206\345\220\210.md" deleted file mode 100644 index e69de29..0000000 diff --git "a/48 \345\272\204\347\217\212\347\217\212/20231025\347\237\245\350\257\206\347\202\271\351\233\206\345\220\210.md" "b/48 \345\272\204\347\217\212\347\217\212/20231025\347\237\245\350\257\206\347\202\271\351\233\206\345\220\210.md" new file mode 100644 index 0000000..43389c4 --- /dev/null +++ "b/48 \345\272\204\347\217\212\347\217\212/20231025\347\237\245\350\257\206\347\202\271\351\233\206\345\220\210.md" @@ -0,0 +1,1119 @@ + + +# 1.ER图 + + + +## 1、先看图熟悉E-R图中图形及代表的意思 + +长方形:表示实体。 + +什么是实体?实体是客观存在的事物。例如用户、商品、订单、供应商等。说直白点!!!你数据库的表名就可以做一个实体对象。一个系统是由很多个实体对象构成的,然后它们之间存在一定的关系和属性。 + +椭圆形:表示属性。 + +什么是属性?举例子:用户实体拥有属性【id、姓名、年龄、电话、身份证号】、商品实体拥有属性【id、商品名称、商品类型、商品价格、商品图片、商品描述、供应商名称】、订单实体拥有属性【id、订单编号、用户名称、商品名称、下单日期】、供应商实体拥有属性【id、名称、地址、联系电话】。 + +菱形:表示关系。 + +什么是关系?例如用户和商品应该购买关系(一个用户购买多件商品)、订单与商品应该是包含关系(一个订单中包含多件商品)。 + +## 2、ER图中关联关系有三种 + +一对一(1:1) :1对1关系是指对于实体集A与实体集B,A中的每一个实体至多与B中一个实体有关系;反之,在实体集B中的每个实体至多与实体集A中一个实体有关系。 + +**例如:一个用户只能拥有一张身份证,而一张身份证只属于一个用户。所以这就是一对一的关系。** + +一对多(1:n) :1对多关系是指实体集A与实体集B中至少有n(n>0)个实体有关系;并且实体集B中每一个实体至多与实体集A中一个实体有关系。 + +**例如:一对多和多对一是一样的。一个用户拥有多张银行卡,但是一张银行卡只属于一个用户。所以这就是一对多的关系。反过来说法就是多对一。** + +在例如:一个用户拥有多张银行卡,但是银行卡的拥有人只能是银行卡的主人 + +多对多(m:n) :多对多关系是指实体集A中的每一个实体与实体集B中至少有m(m>0)个实体有关系,并且实体集B中的每一个实体与实体集A中的至少n(n>0)个实体有关系。 + +**例如:用户与商品的关系,一个用户可拥有多件商品。同样一件商品可被多个用户所拥有。所以这就是多对多的关系。** + +# 2.三大范式 + +数据库设计的三大范式(项目小偷懒用反范式) + +1. 第一范式:每个属性,也就是字段要求**不可再分割**,也就要求有原子性 +2. 第二范式:在满足第一范式的基础上,要求非主键字段要完全依赖主键(有联合主键时,非主键要同时完全依赖这两个主键,而不能部分依赖) +3. 第三范式:在满足第二范式的基础上,要求非主键字段要直接依赖于主键(建多个表) + +# 3.RBAC是一种权限系统管理模型 + +**RBAC**:什么是RBAC +RBAC。英文名称Role-Based Access Control。顾名思义,就是基于**角色的权限控制系统**。理解的基本思路就是,在角色的前提下,**按照角色不同将权限分配到对应的角色,再将管理员设置成对应的角色,进而管理员获得权限。在管理员请求某个业务权限的时候,从数据库中查询该管理员所拥有的权限,并将请求的业务权限跟数据库比对。以此进行权限控制** + +实际开发怎么实现 +而在常规的后台开发中,权限其实对应的就是业务的控制权,再细化下去就是业务开发中,对应实现业务的代码。比如MVC模式中,就是对应的每一个Controller的Action,因为Action实现的就是暴露给外界的控制入口。那么实现过程就是,将这些实现业务的每一个代码接口【控制入口】列出来,每一个接口就是一个**permission,将这些接口保存到permission表;在admin表中设置一个默认的管理员。比如:super_admin;在role表个中设置一个拥有所有权限的角色。比如:super_role;在role_admin表中添加角色管理员关联的数据;在role_permission表中添加对应角色拥有的权限。那么在管理员登陆后要对后台的某个业务进行操作时,获取该操作的业务接口;在role_admin中查询获取该管理员对应的角色;在role_permission**表中查询对应角色拥有的权限;最后将请求的业务接口权限是否存在查询的数据中,比对一下就可以得知该管理员是否有操作权限 + + + +复习复习我又忘记这个了啊啊啊啊啊啊啊啊啊啊啊啊啊啊!!!!! + +## 数据定义语言(DDL) + +由CREATE、ALTER、DROP和TRUNCATE四个语法组成 + +## 数据操纵语言(DML) + +主要有insert、update、delete语法组成。 + +## 数据查询语言(DQL) + +即最常用的select语句 + +## 数据控制语言(DCL) + +比如常见的授权、取消授权、回滚、提交等等操作。 + +# 4.关于spu和sku的图文理解 + + + + + + + +**SPU和SKU**都是属性的集合 + +SPU是公共属性的集合, SPU定义了产品,但不是商品,产品描述了商品的公共属性,不影响库存和价格。一个SPU可以拥有多个SKU,影响库存和价格。 + +SKU是独有属性的集合。 SKU定义了商品,是在SPU的基础上描述了其独有的属性,SKU是库存量的最小存货单位,也是用户能够选择的最小单位是实际购买的商品。 + +# 5.视图 + +## 1.创建视图: + +```mysql +create or replace view employee_vu(姓名,员工号,部门号)as + +select last_name,employee_id,department_id from employees; +``` + +## 2.修改视图: + + + +**alter view 视图名 as select 基本表的字段名 from 基本表 where 字段 in (需要修改的东西)** + +还有就是作业里面的: + + + + + +## 3.修改视图中的数据会使原数据发生变化的: + +update 视图名 set 视图字段=‘修改后的值’ where 视图字段=‘所查找的’ + +就比如说: + +把视图表view2中叫做李楠的初级工程师改成高级工程师 + +update view2 set pos='高级工程师' where name='李楠'; + +这个跟表修改数据是一样的方法,同理insert into 。。。values。。。这个也是跟表一样的 + +语法1:查看视图的结构 + +```mysql +DESC 视图名称; +``` + +语法2:查看视图的属性信息 + +```mysql +# 查看视图信息(显示数据表的存储引擎、版本、数据行数和数据大小等) +SHOW TABLE STATUS LIKE '视图名称' +``` + +执行结果显示,注释Comment为VIEW,说明该表为视图,其他的信息为NULL,说明这是一个虚表。 语法4:查看视图的详细定义信息 + +```mysql +SHOW CREATE VIEW 视图名称; +``` + + + +# 6.很多很多单行函数(容易记混) + +1.length()返回字符串的长度 + +2.substring(s,index,len) 返回从字符串s的index位置截取len个字符 + +3.trim(s) 去掉字符串s开始与结尾的空格 + +### 1) 基本函数 + +| 函数 | 用法 | +| ------------------- | ------------------------------------------------------------ | +| ABS(x) | 返回x的绝对值 | +| SIGN(X) | 单元格 | +| PI() | 返回圆周率的值 | +| CEIL(x),CEILING(x) | 返回大于或等于某个值的最小整数 | +| FLOOR(x) | 返回小于或等于某个值的最大整数 | +| LEAST(e1,e2,e3…) | 返回列表中的最小值 | +| GREATEST(e1,e2,e3…) | 返回列表中的最大值 | +| MOD(x,y) | 返回X除以Y后的余数 | +| RAND() | 返回0~1的随机值 | +| RAND(x) | 返回0~1的随机值,其中x的值用作种子值,相同的X值会产生相同的随机 数 | +| ROUND(x) | 返回一个对x的值进行四舍五入后,最接近于X的整数 | +| ROUND(x,y) | 返回一个对x的值进行四舍五入后最接近X的值,并保留到小数点后面Y位 | +| TRUNCATE(x,y) | 返回数字x截断为y位小数的结果 | +| SQRT(x) | 返回x的平方根。当X的值为负数时,返回NULL | + +## 2. 字符串函数 + +| 函数 | 用法 | +| ------------------------------------------------- | ------------------------------------------------------------ | +| ASCII(S) | 返回字符串S中的第一个字符的ASCII码值 | +| CHAR_LENGTH(s) | 返回字符串s的字符数。作用与CHARACTER_LENGTH(s)相同 | +| LENGTH(s) | 返回字符串s的字节数,和字符集有关 utf8 一个汉字=3个字节 | +| CONCAT(s1,s2,......,sn) | 连接s1,s2,......,sn为一个字符串 | +| CONCAT_WS(x, s1,s2,......,sn) | 同CONCAT(s1,s2,...)函数,但是每个字符串之间要加上x | +| INSERT(str, idx, len, replacestr) | 将字符串str从第idx位置开始,len个字符长的子串替换为字符串replacestr | +| REPLACE(str, a, b) | 用字符串b替换字符串str中所有出现的字符串a | +| UPPER(s) 或 UCASE(s) | 将字符串s的所有字母转成大写字母 | +| LOWER(s) 或LCASE(s) | 将字符串s的所有字母转成小写字母 | +| LEFT(str,n) | 返回字符串str最左边的n个字符 | +| RIGHT(str,n) | 返回字符串str最右边的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(s1 FROM s) | 去掉字符串s开始与结尾的s1 | +| TRIM(LEADING s1 FROM s) | 去掉字符串s开始处的s1 | +| TRIM(TRAILING s1 FROM s) | 去掉字符串s结尾处的s1 | +| REPEAT(str, n) | 返回str重复n次的结果 | +| SPACE(n) | 返回n个空格 | +| STRCMP(s1,s2) | 比较字符串s1,s2的ASCII码值的大小 | +| SUBSTR(s,index,len) | 返回从字符串s的index位置其len个字符,作用与SUBSTRING(s,n,len)、 MID(s,n,len)相同 | +| LOCATE(substr,str) | 返回字符串substr在字符串str中首次出现的位置,作用于POSITION(substr IN str)、INSTR(str,substr)相同。未找到,返回0 | +| ELT(m,s1,s2,…,sn) | 返回指定位置的字符串,如果m=1,则返回s1,如果m=2,则返回s2,如果m=n,则返回sn | +| FIELD(s,s1,s2,…,sn) | 返回字符串s在字符串列表中第一次出现的位置 | +| FIND_IN_SET(s1,s2) | 返回字符串s1在字符串s2中出现的位置。其中,字符串s2是一个以逗号分隔的字符串 | +| REVERSE(s) | 返回s反转后的字符串 | +| NULLIF(value1,value2) | 比较两个字符串,如果value1与value2相等,则返回NULL,否则返回 value1 | + +> 注意: +> +> 1.MySQL中,字符串的位置是从1开始的。 +> +> 2.TRIM()函数可以去掉字符串两端的空格,但是不能去掉字符串中间的空格。如果我们要去掉字符串中间的空格,可以使用REPLACE()函数。 +> +> 3.使用length()函数时需要注意,1个中文输出的字节长度是3。 + +# 7.存储过程与函数 + +## 1. 存储过程概述 + +### 1) 理解 + +**含义:**存储过程的英文是 Stored Procedure 。它的思想很简单,就是一组经过 预先编译的 SQL 语句 的封装。 + +执行过程:存储过程**预先存储**在 MySQL 服务器上,**需要执行的时候**,客户端只需要向服务器端**发出调用存储过程的命令**,服务器端就可以把预先存储好的这一系列 **SQL 语句全部执行**。 + +**好处:** + +* 1、简化操作,提高了sql语句的重用性,减少了开发程序员的压力。 +* 2、减少操作过程中的失误,提高效率。 +* 3、减少网络传输量(客户端不需要把所有的 SQL 语句通过网络发给服务器)。 +* 4、减少了 SQL 语句暴露在 网上的风险,也提高了数据查询的安全性。 + +**和视图、函数的对比:** + +它和视图有着同样的优点,清晰、安全,还可以减少网络传输量。**不过它和视图不同,视图是虚拟表** ,通常不对底层数据表直接操作,而存储过程是程序化的 SQL,可以 直接操作底层数据表 ,相比于面向集合的操作方式,能够实现一些更复杂的数据处理。 + +一旦存储过程被创建出来,使用它就像使用函数一样简单,我们直接通过调用存储过程名即可。相较于函数,存储过程是 没有返回值 的。 + +### 2) 分类 + +存储过程的参数类型可以是IN、OUT和INOUT。根据这点分类如下: + +1、没有参数(无参数无返回) + +2、仅仅带 IN 类型(有参数无返回) + +3、仅仅带 OUT 类型(无参数有返回) + +4、既带 IN 又带 OUT(有参数有返回) + +5、带 INOUT(有参数有返回) + +注意:IN、OUT、INOUT 都可以在一个存储过程中带多个。 + +1. ```mysql + 1. BEGIN…END:BEGIN…END 中间包含了多个语句,每个语句都以(;)号为结束符。 + 2. DECLARE:DECLARE 用来声明变量,使用的位置在于 BEGIN…END 语句中间,而且需要在其他语句使用之前进 + 行变量的声明。 + 3. SET:赋值语句,用于对变量进行赋值。 + 4. SELECT… INTO:把从数据表中查询的结果存放到变量中,也就是为变量赋值。 + ``` + + 示例: + + ```mysql + DELIMITER // + CREATE PROCEDURE 存储过程名(IN|OUT|INOUT 参数名 参数类型,...) + [characteristics ...] + BEGIN + sql语句1; + sql语句2; + END $ + ``` + + ### 2) 代码举例(复习看着个举例好嘛)那个// 是结束这个存储过程的结束符 + + 举例1:创建存储过程select_all_data(),查看 emps 表的所有数据 + + ```mysql + DELIMITER // + CREATE PROCEDURE select_all_data() + BEGIN + SELECT * FROM emps; + END // + DELIMITER ; + ``` + + ## 3.调用存储过程 + + **格式:** + + 1、调用in模式的参数: + + ```mysql + CALL sp1('值'); + ``` + + 2、调用out模式的参数: + + ```mysql + SET @name; + CALL sp1(@name); + SELECT @name; + ``` + + 3、调用inout模式的参数: + + ```mysql + SET @name=值; + CALL sp1(@name); + SELECT @name; + ``` + + ### 2) 代码举例 + + **举例1:** + + ```mysql + 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); + ``` + + 查看返回结果: + + ```mysql + SELECT @num; + ``` + + # 8. 流程控制 + + 解决复杂问题不可能通过一个 SQL 语句完成,我们需要执行多个 SQL 操作。流程控制语句的作用就是控 制存储过程中 SQL 语句的执行顺序,是我们完成复杂操作必不可少的一部分。只要是执行的程序,流程就分为三大类: + + * 顺序结构 :程序从上往下依次执行 + * 分支结构 :程序按条件进行选择执行,从两条或多条路径中选择一条执行 + * 循环结构 :程序满足一定条件下,重复执行一组语句 + + 针对于MySQL 的流程控制语句主要有 3 类。注意:只能用于存储程序。 + + * 条件判断语句 :IF 语句和 CASE 语句 + * 循环语句 :LOOP、WHILE 和 REPEAT 语句 + * 跳转语句 :ITERATE 和 LEAVE 语句 + + ## 1) 分支结构之 IF + + * IF 语句的语法结构是: + + ```mysql + IF 表达式1 THEN 操作1 sql语句 + [ELSEIF 表达式2 THEN 操作2]…… + [ELSE 操作N] + END IF + ``` + + 根据表达式的结果为TRUE或FALSE执行相应的语句。这里“[]”中的内容是可选的。 + + * 特点:① 不同的表达式对应不同的操作 ② 使用在begin end中 + + * 举例1: + + ```mysql + IF val IS NULL + THEN SELECT 'val is null'; + ELSE SELECT 'val is not null'; + END IF; + ``` + + * 举例2:声明存储过程“update_salary_by_eid1”,定义IN参数emp_id,输入员工编号。判断该员工薪资如果低于8000元并且入职时间超过5年,就涨薪500元;否则就不变。 + + ```mysql + DELIMITER // + CREATE PROCEDURE update_salary_by_eid1(IN emp_id INT) + BEGIN + DECLARE emp_salary DOUBLE; + DECLARE hire_year DOUBLE; + SELECT salary INTO emp_salary FROM employees WHERE employee_id = emp_id; + SELECT DATEDIFF(CURDATE(),hire_date)/365 INTO hire_year + FROM employees WHERE employee_id = emp_id; + IF emp_salary < 8000 AND hire_year > 5 + THEN UPDATE employees SET salary = salary + 500 WHERE employee_id = emp_id; + END IF; + END // + DELIMITER ; + ``` + + ## 2) 分支结构之 CASE + + * CASE 语句的语法结构1: + + ```mysql + #情况一:类似于switch + CASE 表达式 + WHEN 值1 THEN 结果1或语句1(如果是语句,需要加分号) + WHEN 值2 THEN 结果2或语句2(如果是语句,需要加分号) + ... + ELSE 结果n或语句n(如果是语句,需要加分号) + END [case](如果是放在begin end中需要加上case,如果放在select后面不需要) + ``` + + * CASE 语句的语法结构2: + + ```mysql + #情况二:类似于多重if + CASE + WHEN 条件1 THEN 结果1或语句1(如果是语句,需要加分号) + WHEN 条件2 THEN 结果2或语句2(如果是语句,需要加分号) + ... + ELSE 结果n或语句n(如果是语句,需要加分号) + END [case](如果是放在begin end中需要加上case,如果放在select后面不需要) + ``` + + * 举例1:使用CASE流程控制语句的第1种格式,判断val值等于1、等于2,或者两者都不等。 + + ```mysql + CASE val + WHEN 1 THEN SELECT 'val is 1'; + WHEN 2 THEN SELECT 'val is 2'; + ELSE SELECT 'val is not 1 or 2'; + END CASE; + ``` + + * 举例2:声明存储过程“update_salary_by_eid4”,定义IN参数emp_id,输入员工编号。判断该员工 薪资如果低于9000元,就更新薪资为9000元;薪资大于等于9000元且低于10000的,但是奖金比例 为NULL的,就更新奖金比例为0.01;其他的涨薪100元。 + + ```mysql + DELIMITER // + CREATE PROCEDURE update_salary_by_eid4(IN emp_id INT) + BEGIN + DECLARE emp_sal DOUBLE; + DECLARE bonus DECIMAL(3,2); + SELECT salary INTO emp_sal FROM employees WHERE employee_id = emp_id; + SELECT commission_pct INTO bonus FROM employees WHERE employee_id = emp_id; + CASE + WHEN emp_sal<9000 + THEN UPDATE employees SET salary=9000 WHERE employee_id = emp_id; + WHEN emp_sal<10000 AND bonus IS NULL + THEN UPDATE employees SET commission_pct=0.01 WHERE employee_id = emp_id; + ELSE + UPDATE employees SET salary=salary+100 WHERE employee_id = emp_id; + END CASE; + END // + DELIMITER ; + ``` + + * 举例3:声明存储过程update_salary_by_eid5,定义IN参数emp_id,输入员工编号。判断该员工的 入职年限,如果是0年,薪资涨50;如果是1年,薪资涨100;如果是2年,薪资涨200;如果是3年, 薪资涨300;如果是4年,薪资涨400;其他的涨薪500。 + + ```mysql + DELIMITER // + CREATE PROCEDURE update_salary_by_eid5(IN emp_id INT) + BEGIN + DECLARE emp_sal DOUBLE; + DECLARE hire_year DOUBLE; + SELECT salary INTO emp_sal FROM employees WHERE employee_id = emp_id; + SELECT ROUND(DATEDIFF(CURDATE(),hire_date)/365) INTO hire_year FROM employees + WHERE employee_id = emp_id; + CASE hire_year + WHEN 0 THEN UPDATE employees SET salary=salary+50 WHERE employee_id = emp_id; + WHEN 1 THEN UPDATE employees SET salary=salary+100 WHERE employee_id = emp_id; + WHEN 2 THEN UPDATE employees SET salary=salary+200 WHERE employee_id = emp_id; + WHEN 3 THEN UPDATE employees SET salary=salary+300 WHERE employee_id = emp_id; + WHEN 4 THEN UPDATE employees SET salary=salary+400 WHERE employee_id = emp_id; + ELSE UPDATE employees SET salary=salary+500 WHERE employee_id = emp_id; + END CASE; + END // + DELIMITER ; + ``` + + ## 3) 循环结构之LOOP + + LOOP循环语句用来重复执行某些语句。LOOP内的语句一直重复执行直到循环被退出(使用LEAVE子 句),跳出循环过程。 + + LOOP语句的基本格式如下: + + ```mysql + [loop_label:] LOOP + 循环执行的语句 + END LOOP [loop_label] + ``` + + 其中,loop_label表示LOOP语句的标注名称,该参数可以省略。 + + 举例1:使用LOOP语句进行循环操作,id值小于10时将重复执行循环过程。 + + ```mysql + DECLARE id INT DEFAULT 0; + add_loop:LOOP + SET id = id +1; + IF id >= 10 THEN LEAVE add_loop; + END IF; + END LOOP add_loop; + ``` + + 举例2:当市场环境变好时,公司为了奖励大家,决定给大家涨工资。声明存储过程 “update_salary_loop()”,声明OUT参数num,输出循环次数。存储过程中实现循环给大家涨薪,薪资涨为 原来的1.1倍。直到全公司的平均薪资达到12000结束。并统计循环次数。 + + ```mysql + DELIMITER // + CREATE PROCEDURE update_salary_loop(OUT num INT) + BEGIN + DECLARE avg_salary DOUBLE; + DECLARE loop_count INT DEFAULT 0; + SELECT AVG(salary) INTO avg_salary FROM employees; + label_loop:LOOP + IF avg_salary >= 12000 THEN LEAVE label_loop; + END IF; + UPDATE employees SET salary = salary * 1.1; + SET loop_count = loop_count + 1; + SELECT AVG(salary) INTO avg_salary FROM employees; + END LOOP label_loop; + SET num = loop_count; + END // + DELIMITER ; + ``` + + ## 4) 循环结构之WHILE + + WHILE语句创建一个带条件判断的循环过程。WHILE在执行语句执行时,先对指定的表达式进行判断,如 果为真,就执行循环内的语句,否则退出循环。WHILE语句的基本格式如下: + + ```mysql + [while_label:] WHILE 循环条件 DO + 循环体 + END WHILE [while_label]; + ``` + + while_label为WHILE语句的标注名称;如果循环条件结果为真,WHILE语句内的语句或语句群被执行,直 至循环条件为假,退出循环。 + + * 举例1:WHILE语句示例,i值小于10时,将重复执行循环过程,代码如下: + + ```mysql + DELIMITER // + CREATE PROCEDURE test_while() + BEGIN + DECLARE i INT DEFAULT 0; + WHILE i < 10 DO + SET i = i + 1; + END WHILE; + SELECT i; + END // + DELIMITER ; + #调用 + CALL test_while(); + ``` + + * 举例2:市场环境不好时,公司为了渡过难关,决定暂时降低大家的薪资。声明存储过程 “update_salary_while()”,声明OUT参数num,输出循环次数。存储过程中实现循环给大家降薪,薪资降 为原来的90%。直到全公司的平均薪资达到5000结束。并统计循环次数。 + + ```mysql + DELIMITER // + CREATE PROCEDURE update_salary_while(OUT num INT) + BEGIN + DECLARE avg_sal DOUBLE ; + DECLARE while_count INT DEFAULT 0; + SELECT AVG(salary) INTO avg_sal FROM employees; + WHILE avg_sal > 5000 DO + UPDATE employees SET salary = salary * 0.9; + SET while_count = while_count + 1; + SELECT AVG(salary) INTO avg_sal FROM employees; + END WHILE; + SET num = while_count; + END // + DELIMITER ; + ``` + + ## 5) 循环结构之REPEAT + + REPEAT语句创建一个带条件判断的循环过程。与WHILE循环不同的是,REPEAT 循环首先会执行一次循环,然后在 UNTIL 中进行表达式的判断,如果满足条件就退出,即 END REPEAT;如果条件不满足,则会就继续执行循环,直到满足退出条件为止。 + + REPEAT语句的基本格式如下: + + ```mysql + [repeat_label:] REPEAT + 循环体的语句 + UNTIL 结束循环的条件表达式 + END REPEAT [repeat_label] + ``` + + repeat_label为REPEAT语句的标注名称,该参数可以省略;REPEAT语句内的语句或语句群被重复,直至 expr_condition为真。 + + 举例1: + + ```mysql + DELIMITER // + CREATE PROCEDURE test_repeat() + BEGIN + DECLARE i INT DEFAULT 0; + REPEAT + SET i = i + 1; + UNTIL i >= 10 + END REPEAT; + SELECT i; + END // + DELIMITER ; + ``` + + 举例2:当市场环境变好时,公司为了奖励大家,决定给大家涨工资。声明存储过程 “update_salary_repeat()”,声明OUT参数num,输出循环次数。存储过程中实现循环给大家涨薪,薪资涨 为原来的1.15倍。直到全公司的平均薪资达到13000结束。并统计循环次数。 + + ```mysql + DELIMITER // + CREATE PROCEDURE update_salary_repeat(OUT num INT) + BEGIN + DECLARE avg_sal DOUBLE ; + DECLARE repeat_count INT DEFAULT 0; + SELECT AVG(salary) INTO avg_sal FROM employees; + REPEAT + UPDATE employees SET salary = salary * 1.15; + SET repeat_count = repeat_count + 1; + SELECT AVG(salary) INTO avg_sal FROM employees; + UNTIL avg_sal >= 13000 + END REPEAT; + SET num = repeat_count; + END // + DELIMITER ; + ``` + + **对比三种循环结构:** + + 1. 这三种循环都可以省略名称,但如果循环中添加了循环控制语句(LEAVE或ITERATE)则必须添加名称。 + + 2. LOOP:一般用于实现简单的"死"循环 WHILE:先判断后执行 + + 3. REPEAT:先执行后判断,无条件至少执行一次 + + ## 6) 跳转语句之LEAVE语句 + + LEAVE语句:可以用在循环语句内,或者以 BEGIN 和 END 包裹起来的程序体内,表示跳出循环或者跳出 程序体的操作。如果你有面向过程的编程语言的使用经验,你可以把 LEAVE 理解为 break。 + + 基本格式如下: + + ```mysql + LEAVE 标记名 + ``` + + 其中,label参数表示循环的标志。LEAVE和BEGIN ... END或循环一起被使用。 + + 举例1:创建存储过程 “leave_begin()”,声明INT类型的IN参数num。给BEGIN...END加标记名,并在 BEGIN...END中使用IF语句判断num参数的值。 + + 如果num<=0,则使用LEAVE语句退出BEGIN...END; 如果num=1,则查询“employees”表的平均薪资; 如果num=2,则查询“employees”表的最低薪资; 如果num>2,则查询“employees”表的最高薪资。 + + IF语句结束后查询“employees”表的总人数。 + + ```mysql + DELIMITER // + CREATE PROCEDURE leave_begin(IN num INT) + begin_label: BEGIN + IF num<=0 + THEN LEAVE begin_label; + ELSEIF num=1 + THEN SELECT AVG(salary) FROM employees; + ELSEIF num=2 + THEN SELECT MIN(salary) FROM employees; + ELSE + SELECT MAX(salary) FROM employees; + END IF; + SELECT COUNT(*) FROM employees; + END // + DELIMITER ; + ``` + + 举例2: 当市场环境不好时,公司为了渡过难关,决定暂时降低大家的薪资。声明存储过程“leave_while()”,声明 OUT参数num,输出循环次数,存储过程中使用WHILE循环给大家降低薪资为原来薪资的90%,直到全公司的平均薪资小于等于10000,并统计循环次数。 + + ```mysql + DELIMITER // + CREATE PROCEDURE leave_while(OUT num INT) + BEGIN + DECLARE avg_sal DOUBLE;#记录平均工资 + DECLARE while_count INT DEFAULT 0; #记录循环次数 + SELECT AVG(salary) INTO avg_sal FROM employees; #① 初始化条件 + while_label:WHILE TRUE DO #② 循环条件 + #③ 循环体 + IF avg_sal <= 10000 THEN + LEAVE while_label; + END IF; + UPDATE employees SET salary = salary * 0.9; + SET while_count = while_count + 1; + #④ 迭代条件 + SELECT AVG(salary) INTO avg_sal FROM employees; + END WHILE; + #赋值 + SET num = while_count; + END // + DELIMITER ; + ``` + + ## 7) 跳转语句之ITERATE语句 + + ITERATE语句:只能用在循环语句(LOOP、REPEAT和WHILE语句)内,表示重新开始循环,将执行顺序转到语句段开头处。如果你有面向过程的编程语言的使用经验,你可以把 ITERATE 理解为 continue,意思为“再次循环”。 + + 语句基本格式如下: + + ```mysql + ITERATE label + ``` + + label参数表示循环的标志。ITERATE语句必须跟在循环标志前面。 + + 举例: 定义局部变量num,初始值为0。循环结构中执行num + 1操作。 + + * 如果num < 10,则继续执行循环; + * 如果num > 15,则退出循环结构; + + ```mysql + DELIMITER // + CREATE PROCEDURE test_iterate() + BEGIN + DECLARE num INT DEFAULT 0; + my_loop:LOOP + SET num = num + 1; + IF num < 10 + THEN ITERATE my_loop; + ELSEIF num > 15 + THEN LEAVE my_loop; + END IF; + SELECT 'MySQL'; + END LOOP my_loop; + END // + DELIMITER ; + ``` + + # 9. 游标 + + ## 1) 什么是游标(或光标) + + 虽然我们也可以通过筛选条件 WHERE 和 HAVING,或者是限定返回记录的关键字 LIMIT 返回一条记录, 但是,却无法在结果集中像指针一样,向前定位一条记录、向后定位一条记录,或者是随意定位到某一 条记录 ,并对记录的数据进行处理。 + + 这个时候,就可以用到游标。游标,提供了一种灵活的操作方式,让我们能够对结果集中的每一条记录进行定位,并对指向的记录中的数据进行操作的数据结构。游标让 SQL 这种面向集合的语言有了面向过程开发的能力。 + + 在 SQL 中,游标是一种临时的数据库对象,可以指向存储在数据库表中的数据行指针。这里游标 充当了 指针的作用 ,我们可以通过操作游标来对数据行进行操作。 + + MySQL中游标可以在存储过程和函数中使用。 + + ## 2) 使用游标步骤 + + 游标必须在声明处理程序之前被声明,并且变量和条件还必须在声明游标或处理程序之前被声明。 + + 如果我们想要使用游标,一般需要经历四个步骤。不同的 DBMS 中,使用游标的语法可能略有不同。 + + **第一步,声明游标** + + 在MySQL中,使用DECLARE关键字来声明游标,其语法的基本形式如下: + + ```mysql + DECLARE cursor_name CURSOR FOR select_statement; + ``` + + 这个语法适用于 MySQL,SQL Server,DB2 和 MariaDB。如果是用 Oracle 或者 PostgreSQL,需要写成: + + ```mysql + DECLARE cursor_name CURSOR IS select_statement; + ``` + + 要使用 SELECT 语句来获取数据结果集,而此时还没有开始遍历数据,这里 select_statement 代表的是 SELECT 语句,返回一个用于创建游标的结果集。 + + 比如: + + ```mysql + DECLARE cur_emp CURSOR FOR + SELECT employee_id,salary FROM employees; + ``` + + **第二步,打开游标** + + 打开游标的语法如下: + + ```mysql + OPEN cursor_name + ``` + + 当我们定义好游标之后,如果想要使用游标,必须先打开游标。打开游标的时候 SELECT 语句的查询结果集就会送到游标工作区,为后面游标的 逐条读取 结果集中的记录做准备。 + + ```mysql + OPEN cur_emp; + ``` + + **第三步,使用游标(从游标中取得数据)** + + 语法如下: + + ```mysql + FETCH cursor_name INTO var_name [, var_name] ... + ``` + + 这句的作用是使用 cursor_name 这个游标来读取当前行,并且将数据保存到 var_name 这个变量中,游标指针指到下一行。如果游标读取的数据行有多个列名,则在 INTO 关键字后面赋值给多个变量名即可。 + + 注意:var_name必须在声明游标之前就定义好。 + + ```mysql + FETCH cur_emp INTO emp_id, emp_sal ; + ``` + + 注意:**游标的查询结果集中的字段数,必须跟 INTO 后面的变量数一致**,否则,在存储过程执行的时 候,MySQL 会提示错误。 + + **第四步,关闭游标** + + ```mysql + CLOSE cursor_name + ``` + + 有 OPEN 就会有 CLOSE,也就是打开和关闭游标。当我们使用完游标后需要关闭掉该游标。因为游标会 占用系统资源 ,如果不及时关闭,游标会一直保持到存储过程结束,影响系统运行的效率。而关闭游标 的操作,会释放游标占用的系统资源。 + + 关闭游标之后,我们就不能再检索查询结果中的数据行,如果需要检索只能再次打开游标。 + + ```mysql + CLOSE cur_emp; + ``` + + ## 3) 举例 + + 创建存储过程“get_count_by_limit_total_salary()”,声明IN参数 limit_total_salary,DOUBLE类型;声明 OUT参数total_count,INT类型。函数的功能可以实现累加薪资最高的几个员工的薪资值,直到薪资总和达到limit_total_salary参数的值,返回累加的人数给total_count。 + + ```mysql + 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; + ``` + + ## 4) 小结 + + 游标是 MySQL 的一个重要的功能,为 逐条读取 结果集中的数据,提供了完美的解决方案。跟在应用层面实现相同的功能相比,游标可以在存储程序中使用,效率高,程序也更加简洁。 + + 但同时也会带来一些性能问题,比如在使用游标的过程中,会对数据行进行 加锁 ,这样在业务并发量大 的时候,不仅会影响业务之间的效率,还会 消耗系统资源 ,造成内存不足,这是因为游标是在内存中进行的处理。 + + 建议:养成用完之后就关闭的习惯,这样才能提高系统的整体效率。 + + + +# 10.触发器和窗口函数 + +#### 序号函数 + +row_number()、rank()、dense_rank() + +#### 窗口函数 + +窗口函数名称(参数) over(partition by ... order by... 窗口大小) + +#### 前后函数 + +返回当前行的前某一行的值,或者后某一行的值 + +lag(列名,前第n行) + +lead(列名,后第n行) + +#### 头尾函数 + +返回第一个或最后一个列的值 + +first_value(列名) + +last_value(列名) + +#### 分布函数 + +##### cume_dist() + +分组内 <= 或 >= 当前值的行数占比(包含当前值本身) + +```mysql +# 查询各部门员工小于等于当前薪资的比例 + +select ename,deptno,sal, +cume_dist() over(partition by deptno order by sal) '占比' +from emp; + +-- order by 控制计算的列 +-- asc 小于等于 +-- desc 大于等于 +``` + +------------------------------------------- + +```mysql +# percent_rank() 百分比排序(小数形式) +``` + +----------------------------------------------------------------------------------- + +-- rows 启用窗口大小 +-- between ... and ... 范围区间 +-- unbounded preceding 起始行 +-- unbounded following 最后行 +-- current row 当前行 #当前行 # 最后一位员工 +select salary,sum(salary) over (order by salary rows between current row and unbounded following)from employee; + +--------------------------------- + +```mysql +# EXTRACT() 函数用于返回日期/时间的单独部分,比如年、月、日、小时、分钟等等。 +select dname,ename,birth,EXTRACT(YEAR from birth)-lag(EXTRACT(YEAR from birth)) over (partition by dname order by birth) from employee; +``` + +#### 创建触发器: trigger 和 after insert on emp for each row(例子) + +```mysql +CREATE TRIGGER 触发器名称 +{BEFORE|AFTER} {INSERT|UPDATE|DELETE} ON 表名 FOR EACH ROW +触发器执行的语句块 +``` + +创建名称为before_insert的触发器,向test_trigger数据表插入数据之前,向 test_trigger_log数据表中插入before_insert的日志信息。 + +```mysql +DELIMITER // +CREATE TRIGGER before_insert +BEFORE INSERT ON test_trigger FOR EACH ROW +BEGIN + INSERT INTO test_trigger_log (t_log) + VALUES('before_insert'); +END // +DELIMITER ; +``` + +3. 向test_trigger数据表中插入数据 + +```mysql +INSERT INTO test_trigger (t_note) VALUES ('测试 BEFORE INSERT 触发器'); +``` + +4. 查看test_trigger_log数据表中的数据 + +# 1.2卡手但是能搞懂的易忘题 + +-- case when + +```mysql +# case +# when 条件表达式1 then 值1 +# when 条件表达式2 then 值2 +# ... +# else 值n +# end +### 前后函数 +# 返回当前行的前某一行的值,或者后某一行的值 +# lag(列名,前第n行) +``` + + + +```mysql +按员工工资进行排序,比较相邻两个员工的工资,输出比较高的工资 + +# 这个查询语句使用了窗口函数LAG()来比较当前行的工资和上一行的工资。它将根据工资的升序排序,然后对每一行计算出上一行的工资。 + +# CASE语句用于判断上一行的工资是否大于当前行的工资。如果是,它会返回上一行的工资作为"higher_salary"列的值;否则,它会返回当前行的工资 + +select +case when lag(salary) over (order by salary)>salary +then lag(salary) over (order by salary) +else salary end 逐行比较工资较高的工资 +from employee; +``` + +```mysql +按员工工资进行排序,查询当前员工至最后一位员工的工资总和 +-- rows 启用窗口大小 +-- between ... and ... 范围区间 +-- unbounded preceding 起始行 +-- unbounded following 最后行 +-- current row 当前行 #当前行 # 最后一位员工 +select salary,sum(salary) over (order by salary rows between current row and unbounded following)from employee; +``` + +```mysql +每个部门按年龄进行排序,求当前员工与前一位员工的年龄差 +# ### 前后函数 +# 返回当前行的前某一行的值,或者后某一行的值 +# lag(列名,前第n行) +# EXTRACT() 函数用于返回日期/时间的单独部分,比如年、月、日、小时、分钟等等。 +select dname,ename,birth,EXTRACT(YEAR from birth)-lag(EXTRACT(YEAR from birth)) over (partition by dname order by birth) from employee; + +``` + +# 11.窗口函数(亲,这边建议直接去看窗口函数的那个文档和练习,结合起来更易进食) + +比较忘得明明白白的: + +开窗聚合函数 + +sum()|avg()|min()|max()|count()| over (partition by ... order by...) + +rows 启用窗口大小 + +between ... and ... 范围区间 + +unbounded preceding 起始行 + +current row 当前行 + +# 12.索引 + +索引: + +## 创建普通索引 + +create index 索引名 on 表名(列名) + +**alter** **table** 表名 **add** **index** 索引名(列名) + +创建表时直接创索引 + +create table tab( + id int primary key, + name varchar(20), + index 索引名 (列名) primary key(列名) +); + + + +## 创建唯一索引 + +比普通索引多了unique关键字 + +create unique index 索引名 on 表名(列名); +alter table 表名 add unique 索引名 (列名) +create table tab( + id int primary key, + name varchar(20), + unique [INDEX] 索引名 (列名) +); + +# 13.事务: + +特点: + +1.原子性:事务已经是最小单位了,不可能会比事务更小 + +事务是将多条sql语句捆绑一起执行的要么一起成功要么一起失败 + +2.一致性:数据从a到b一定要一致,同时状态也要一致 + +3.隔离性:两个事务之间,事务a与事务b之间有一个隔离墙 + +4.持久性:事务一旦提交,对数据库的更改时永久性的 + +事务的操作: + +事务的操作: + +事务一开始为隐式事务,是自动提交的,所以我们要手动打开它,代码如下: + +```mysql +set autocommit=0;-- 手动提交事务 +set autocommit=off; +``` + +提交事务的代码如下: + +```mysql +start transaction; -- 开启事务 +create table test1(id int);-- 建表语句 +set autocommit=0;-- 手动提交事务 +insert into test1 values(1);-- 插入一条数据的sql语句 +commit;-- 提交事务!!(提交之后将关闭事务) +``` + +将事务提交后悔时可以使用回滚事务,代码如下: + +```mysql +start transaction; -- 开启事务 +create table test1(id int);-- 建表语句 +set autocommit=0;-- 手动提交事务 +insert into test1 values(1);-- 插入一条数据的sql语句 +rollback;-- 回滚数据将事务内这条插入语句回滚回刚开始的数据(回滚后也会关闭事务) +``` + +savepoint关键字的作用是当我们只想回滚部分数据是可以用它来实现,它就相当于一个存档,通过回滚指令来读档,代码如下: + +```mysql +start transaction; -- 开启事务 +create table test1(id int);-- 建表语句 +set autocommit=0;-- 手动提交事务 +insert into test1 values(1);-- 插入一条数据的sql语句 +savepoint a1;-- 在此处建立一个存档a1 +insert into test1 values(2);-- 插入第二条数据的sql语句 +savepoint a2; -- 在此处建立一个存档a2 +-- 当我们只想回滚到a2的操作时就可以用到这个存档了 +rollback to a2; +``` + +对于事务中的一些问题 + +### 脏读:我写了错误答案,同桌b抄了我的错误答案上交了,而我在上交前把错误答案修改成正确答案 + +### 读已提交:同桌b可以看到所有已提交的答案进行操作 + +(这种情况发生 在一个事务内多次读同一数据。A事务查询某条数据,**该事务未结束时**,**B事务也访问同一数据并进行了修改**。那么在A事务中的两 次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。) + +### 可重复读 + +一个事务操作中对于一个读取操作不管多少次,读取到的结果都是一样的。 + +### 幻读 + +脏读、不可重复读、可重复读、幻读,其中最难理解的是幻读。 + +全部结合就是它 + -- Gitee