一 SQL 介绍 SQL(Structured Query Language),语义是结构化语言, 是一门 ANSI 的标准计算机语言,用来访问和操作数据库系统; 二 数据库介绍2.1 数据库我们已经知道了SQL是操作数据库的语言,那么数据库是什么?数据库(database)是 保存一个文件或者一组文件的容器;这样说你可能还不懂,可以比喻为数据是一个图书馆,图书馆里面收藏有好多书,我们可以认为数据库就是能够存储数据的容器;其实数据库很常见,你每天都在操作数据库,比如你看手机,上网,微信聊天,其实背后都在操作数据库,只是展现操作数据库的形式不一样而已; 2.2 数据库管理系统数据库里面有好多的数据,就像图书管一样有好多的书,书是由图书管理员进行管理,那么什么东西能够管理数据库呢?没错就是数据库管理系统(Database Manage System),比如 mysql, Access、DB2、Informix、 Server、Oracle、Sybase以及其他数据库系统; 2.3 表表是一种结构化的文件可以用来存储特定类型的数据;这么说你可能没听懂,我们再举个例子,表就像图书馆里面的图书架子,每个书架都放了好多书;再不懂也没关系,你总用过excel吧,excel表格里面的标签页 (sheet) 我们就可类比数据表,表存放的就是特定类型的数据,跟excel的sheet一样;重点是每个表的表名是唯一,不可重复; 2.4 列和数据类型列是表中的一个字段,一个表由多个列组成;每个列都由特定的数据类型,只能存放指定数据类型的数据;听不懂没关系,我们类比excel,excel中的sheet也是由多个列组成,每个列都有指定的数据类型,比如文本,数字,日期类型;那数据库的表跟excel的结果是类似的; 数据类型就是限定表中的每个列只能存储特定类型的数据,常见的数据类型有整型,数字,文本,字符串,日期等等。 2.5 行行就是表中的一条记录,类比excel,很容易理解; 2.6 主键主键就是每行的唯一标识,其特性是主键,不能为空,不能重复,不能修改;主键我们可以类比身份证号,每个身份证号都是唯一不可重复的; 2.7 行号行号指表中每个行的行号; 三 基本检索如果是初学者,建议去网上寻找安装Mysql的文章安装,以及使用navicat连接数据库,以后的示例基本是使用mysql数据库管理系统; 需要建立一张学生表,列分别是id,名称,年龄,学生信息;本示例中文章篇幅原因SQL注释略; 建表语句: CREATE TABLE `student` ( 表中的数据:
3.1 select 关键字sql 都是由许多关键字(keyword)组成的语句,关键字是数据库的保留字,用户不能将其当作建表的表名,字段等;表中的数据检索使用select关键字作为开头进行查询数据库表的信息; 3.2 检索单个字段语句示例: SELECT name FROM student 查询结果:
语句分析: select 是 查询 的意思 , name 代表表中的字段 ,from 表示来自哪张表,其后面的student就是表;连起来的意思就是查询字段是name的数据来自表student; 3.3 sql语句注意点多sql语句一起执行使用封号(;)隔开,在拼写语句时表的关键字都建议使用大写,表的字段和表名使用小写;为了容易阅读建议将sql分成多行(由于文章篇幅原因就不分了);sql语言也是使用英文字母,不要开中文,以免造成符号错误不容易发现;sql语句默认不区分大小写; 3.4 检索多个字段语句示例: SELECT name,age FROM student; 语句结果:
语句分析: 查询字段 名字,年龄来自表student;多个列检索时使用逗号(,)隔开; 3.5 检索所有字段语句示例: SELECT * FROM student; 语句结果:
语句分析: 通配符 (*)表示返回表中的所有列,如果不是必须建议不要使用通配符,会影响数据库性能; 3.6 distinctdistinct 表示区分,意指检索出来的行是唯一(去重),其放在列的最前面;如果使用了关键字distinct,其作用于后面的所有列(由于本示例数据没有重复示例,结果就不是很清晰); SELECT DISTINCT name, age FROM student; 语句结果:
3.7 限制条数access 和 sql server : SELECT TOP 2 name FROM student TOP 2 表示限制返回前2行 postgresql 、SQLite和 mysql:
limit 2 表示限制返回前2行; DB2: SELECT name FROM student FETCH FIRST 2 ROWS ONLY FETCH FIRST 2 ROWS ONLY 表示只抓取前2行数据 语句结果:
3.8偏移示例: SELECT name FROM student LIMIT 1 OFFSET 1; 语句分析 表示查询列名称来自学生表 限制条数1,偏移值1;意思就是查询学生表中的第二行数据;offset表示跳跃或者偏移; mysql和MariaDB简化形式:
表示查询字段名称来自学生表,限制2条,偏移1条;注意顺序; 语句结果: youku2 四 排序检索4.1 ORDER BY 子句示例:
语句分析; 检索字段名称,年龄来自学生表按照列年龄排序;注意默认是升序,ORDER BY 子句通常在语句末尾; 语句结果: smile 17 4.2 多列排序示例:
语句分析: 查询名称,年龄来自学生表,按照年龄降序,名称升序进行排序;关键字 DESC(descending) 意指降序,字母默认Z-A; ASC(ascending)意指升序,字母默认A-Z;多列情况下,每个列后面指定使用DESC,使用逗号(,)隔开,如果不写,默认升序; 语句结果: jeff 25 4.3 按位排序语句示例:
按位指查询字段的位置,2 对应字段age,1对应字段name;结果和4.2一致; 五 过滤检索sql 语句中过滤条件(filter condition)的关键字是 WHERE,其使用方式是跟在表名之后; 5.1 WHERE语句操作符根据不同的数据库管理系统,其支持的操作符略有不同,所以下文较多重复意思的操作符,应查阅官方文档说明,哪些操作符是支持使用的数据库管理系统;
5.2 单条件过滤示例: SELECT * FROM student WHERE name = 'jeff'; 语句分析; 查询所有字段来自学生表 条件是 学生名称是 jeff;注意 非表字段,表名,关键字,使用2个单引号('')括起来,里面存放的是我们输入的数据; 语句结果;
5.3 多条件过滤多条件过滤时使用 AND 或者 OR 子句;AND连接表达式表示过滤条件都为真的数据;OR连接表达式表示匹配过滤条件任意一个; AND示例: SELECT * FROM student WHERE age >= '18' AND age <= '23'; 语句分析: 查询所有字段来自学生表,条件是 学生年龄大于等于18 并且 学生年龄小于 23; 语句结果:
OR示例: SELECT * FROM student WHERE age >= '18' OR age <= '23'; 语句分析: 检索所有字段来自学生表,条件是 学生年龄大于等于18,或者学生年龄小于23; OR 和 AND 示例:
语句分析: 在使用OR 和 AND 的时候应该明确过滤条件,然后使用小括号括起来,由于数据库管理系统是根据顺序执行,如果不使用小括号很容易造成语义错误;查询所有字段来自学生表 过滤条件 年龄大于 18 并且 (年龄小于23 或者 id 大于2)的数据; 5.4 范围查询示例: SELECT * FROM student WHERE age BETWEEN '18' And '23'; 语句分析 BETWEEN 表示范围查询,查询所有字段来自学生表,过滤条件学生年龄在18至23之间; 语句结果:
5.5 空值查询示例: SELECT * FROM student WHERE age IS NULL; 语句分析: 查询所有字段来自学生表,过滤条件 学生年龄 为空;数据库表不填充数据默认为空(NULL),当然你也可以给指定类型的列设置默认值; 5.6 IN 操作示例:
语句分析: 查询所有字段来自学生表 ,过滤条件 年龄 是 18 或者 23 或者 25 ;IN是个范围查询,匹配小括号中指定的任意值,其功能跟OR类似,一个IN 就相当于好多个OR; 语句结果: 1 youku1 18 大一新生 5.7 NOT 操作符示例:
语句分析: NOT 操作符表是否定;其跟在WHERE后面功能类似<>; 六 通配符检索6.1 通配符的介绍通配符是组成匹配模式的特殊字符串;如果有用过正则表达式的同学肯定不陌生,这个与其类似;检索文本的通配符是用在关键词Like 后面; 6.2 通配符 %示例: SELECT * FROM student WHERE name LIKE 'you%' ; 语句分析: 查询所有字段来自学生表,过滤条件 名字匹配以you开头的字符串后面匹配任意个任意字符;%通配符代表任意个任意字符串,0也算在内,但不包括null; 语句结果:
示例: SELECT * FROM student WHERE name LIKE '%i%' ; 语句分析: 查询所有列来自学生表,过滤条件学生的名称匹配中间必须出现一次字母i,字母i的前后可以匹配任意个任意字符串; 语句结果;
6.3 通配符 _通配符 _ 代表匹配一个字符串;在Access数据库中不是 _ 而是 ?; 示例: SELECT * FROM student WHERE name LIKE 'youku_' ; 语句分析: 查询所有列来自学生表,过滤条件 学生名称 匹配模式 youku 后面一个为任意字符串; 语句结果;
6.4 通配符 []通配符 [] 表示匹配指定一个位置一个字符;其里面可以存放多个字符,关系是or,模式匹配时只占用一个位置,Access,SQL SERVER 支持; 七 字段基本操作7.1 字段拼接示例: SELECT concat('你好啊',name,'327今天心情怎么样') FROM student WHERE id = '1' ; 语句分析: 函数 concat 就是将多个字符子串拼接成一个字符串;不同的数据库管理系统其使用的方式略有差别,应查阅官方文档;在 mysql 中使用 concat函数;在postgresql中使用 || ;在 Access和 sql server中使用 +; 语句结果:
7.2 去除空白字符串语句示例: SELECT RTRIM(' 哥,今天管饱 ') FROM student WHERE id = '1' ; 语句分析: RTRIM(STR) 函数是去掉右边的字符串;TRIM(STR)是去掉字符串两边的空白字符;LTRIM(STR)是去掉字符串左边的空白字符; 语句结果;
7.3 别名语句示例: SELECT name as student_name FROM student WHERE id = '1' ; 语句分析: 别名(alias)是可以给字段,或者表起别名;当多表操作出现重复字段时使用别名是个很好的选择;别名可以使用AS关键字,虽然其是可省略,但平时我们最好加上它增强阅读性; 7.4 计算
语句示例:
语句结果: 16 # 八 聚集函数的使用 先声明一下,下面的库表只是简易的学习示例,不是生产的设计,不要深究,此文我们的目的是学习sql的检索不是库表设计;初学者最好跟着作者的文章一步一步敲一遍,如果没有使用过sql的可以查阅作者SQL系列专栏; 顾客表
商品表 CREATE TABLE `product` ( 订单表
聚集函数的定义就是讲一些行的数据运行某些函数,返回一个期望值;下面讲述的是开发中经常使用到的聚集函数; 8.1 avg()avg函数也就是计算行的数量,通过计算这些行的特定列值和,计算出平均值(特定列值之和/行数=平均值);使用时注意其会忽略列值为NULL的行; 语句示例: SELECT AVG(price) FROM product; 语句结果:
语句分析:查询价格平均值来自商品表(5+4+3)/3=4; 8.2 count()count函数用于计算行数,其中count(*)计算所有行的数目,count('column')会忽略column为NULL的行数; 语句示例: SELECT count(*) FROM product; 语句结果:
语句分析: 查询总行数来自商品表; 8.3 max()max函数返回特定列值的最大值;忽略特定列为NULL的行; 语句示例: SELECT max(price) FROM product; 语句结果:
语句分析: 查询价格的最大值来自商品表; 8.4 min()返回特定列的最小值;忽略特定列为NULL的行; 语句示例: SELECT min(price) FROM product; 语句结果:
语句分析: 查询价格的最小值来自商品表; 8.5 sum()返回特定列的和;忽略特定列为NULL的行; 语句示例: SELECT sum(price) FROM product; 语句结果:
语句分析: 查询价格的总和来自商品表; 九 分组数据分组定义就是按照特定的列进行分组查询,使用 GROUP BY 子句进行分组查询;注意点:SELEC后面的列必须出现在group by 子句后面,否则报语法错误;通常 group by 子句的位置是where 条件之后,order by 子句之前; 9.1 分组求和语句示例: SELECT sum(price) FROM product GROUP BY productName; 语句结果:
语句分析: 先根据商品名称分为三组 苹果 ,梨 , 香蕉 ;再根据不同的分组求和,因为我们表中的数据只有这三条所以就是每行的值; 9.2 分组过滤语句示例: SELECT count(*) FROM `order` GROUP BY userId HAVING count(*) > 1; 语句结果:
语句分析 查询 条数来自 订单表 根据客户id分组,过滤条件 条数大于2;注意 having 与 where其实差别不大,通常我们讲where当作标准的过滤条件,having用作分组过滤条件;注意有的数据库管理系统having不支持别名作为分组过滤条件中的一部分; 9.3 分组排序语句示例: SELECT count(*) as count FROM `order` GROUP BY userId ORDER BY count; 语句结果:
语句分析查询 行数 来自 订单表 根据 客户id分组,根据 行数排序;注意点是经过分组后结果看似经过排序,其实并不能确保是排序后的结果,所以要排序一定要使用order by子句; 十 子查询子查询的定义是在查询中嵌套查询;注意子查询只能返回单列,若企图返回多列会报语法错误; 语句示例: SELECT 语句结果:
语句分析: 是执行语句 【SELECT userId FROM 十一 联结表联结表也就是我们通常意义上的关联表查询,主要功能是能在多表中使用一条sql检索出期望值,但实际库表中是存在的,只在查询期间存在;其主要分为内联结和外连接使用的 11.1 简单联结语句示例: SELECT 语句结果:
语句分析 : 查询 用户名来自用户表,查询订单名称来自订单表,根据 订单表的客户id 等于 客户表的客户id做为联结条件;也就是说会查询出两张表根据userId为等值条件的 userName 和 orderName 的 数据; 注意点 :简单联结中where子句后面 必须 要带上 两张表的联结关系,否则会出现笛卡尔集(比如3行数据联结另一张表3行数据会产生3*3=9条) 11.2 内联结内连接(inner join) 又称等值联结,其查询结果跟之前的简单联结一致; 语句示例: SELECT 语句结果:
语句分析: 跟之前的简单联结稍微不同的是 等值条件 是放在 on 关键字后面,在等值条件后面还可以进行 where 子句过滤条件查询; 11.3 自然联结自然联结与标准的联结不同就是只返回值唯一的列,不会返回重复的列; 自然联结示例: SELECT 自然联结结果
非自然联结示例: SELECT 非自然联结结果:
重复列是 userId; 11.4 外联结右外联结 语句示例: SELECT 右外联结是指 相对于 OUTER JOIN 右边的表,那么这会查询出右边表的所有数据 和根据等值条件匹配左边表的数据,如果左边表的数据不匹配,那么其返回列的值是NULL充当; 左外联结 语句示例:
左外联结是指 相对于 OUTER JOIN 左边的表,那么这会查询出左边表的所有数据 和根据等值条件匹配右边表的数据,如果右边表的数据不匹配,那么其返回列的值是NULL充当; 区别: 左外联结和右外联结其实没什么不同,只是查询表顺序不一致,我们通过置换 表的相对位置就可以查询出一样的结果; 十二 组合查询组合查询是指可以执行多条SELECT 语句,其查询的结构是一致的,返回查询结果,通常我们称为复合操作或者并(union) 语句示例: SELECT 返回结果:
语句分析: union 关联的字段或者聚合函数在两张表中必须是相同的,其默认会讲结果进行去重处理;如果不去重可以使用 语句示例: SELECT 执行结果:
语句分析:等同于讲客户表和订单表的用户id都合并为一个并集查询出来,而且不去重;如果对组合语句进行排序,默认是会作用于组合后的数据字段排序,而不是作用于其中的一条查询语句; 十三 插入插入数据库记录也就是使用 INSERT 关键字,能将一条语句插入数据库,高级的可以组合 SELECT 关键字 实现 插入查询的结果集,插入整张表; 建表语句: CREATE TABLE `user` ( 13.1 插入一条完整数据语句示例:
语句结果: 2 zszxz 1327 语句分析: 插入 数据 到 user 表 字段分别是 id, name , telephone; 值 分别是 2 , zszxz , 1327; 这是插一条完整的语句,虽然INTO可以忽略不写,但不建议这么做,原因是在数据库管理系统间会出现移植性问题;还有字段也可以忽略不写,但也不建议这么做,这容易造成插入数据出错;字段的位置和值的位置是一 一对应;如果有的位置没值可以使用NULL代替; 13.2 插入部分数据语句示例:
语句结果: 3 zszxz 语句分析: 插入数据到user表,字段分别是 id , name ; 值分别是,3,zszxz; 可以看见我们没有插入telephone字段; 13.3 插入检索数据插入检索的数据也就是能将查询的结果插入进另一张表;我们可以使用 INSERT SELECT 关键组合成一条语句实现; 语句示例:
语句结果: 4 smile 语句分析: 插入数据到 user 表, 字段分别是 id, name ,值是查询字段 id ,name 来自 student 表,条件是 id 等于 4;可以看见我们插入数据的列根查询的列名称是匹配对应的,其实只要列顺序一致即可,不过为了不出错,建议使用名称匹配; 13.4 复制表复制表即,检索一张表的数据全部插入另一张表;有两种方法,但是不同的数据库管理系统支持不同,具体的看下文; 语句示例:
语句分析 查询字段 id, name 插入 student_copy 表,来自 student 表;注意 这条语句会帮我们自动创建表 student_copy,由于作者使用的是mysql做演示,这条sql执行失败,原因是其不支持这种方式;如果是想复制整张表可以使用通配符 * ; 语句示例: CREATE TABLE student_copy AS 语句结果:
语句分析: 创建表 student_copy 数据结构来源 查询 所有字段来自 student 表; 十四 更新更新数据库的行使用 UPDATE 关键字;更新操作是个很危险的操作,在每次执行前都应该检查是否丢了 where 子句; 14.1 更新所有行语句示例: UPDATE student_copy set age = Null; 语句结果:
语句分析: 更新 student_copy 表, 设置 字段 age 值为null;可以看见表中所有的学生年龄都是Null; 如果有多个字段需要更新,使用 逗号隔开; 14.2 更新特定的行语句示例: UPDATE student_copy set age = '18' WHERE id = '4'; 语句结果:
语句分析: 更新 student_copy 设置 学生的年龄是 18 条件是 id 等于 4; 14.3 更新来自查询的结果集语句示例: UPDATE student_copy set age= student.age, name = student.name 语句分析: 更新 student_copy 表 设置 age 是 student 表的 age,name 是 student 表的 name 条件是 student 的id 等于 student_copy 表的 id; 遗憾的是 mysql 数据库管理系统又执行失败了,其不支持这种方法更新,如果是postgresql就支持,其他数据库应查阅官方文档查看是否支持这种方式更新; 语句示例:
语句结果: 1 youku1 18 大一新生 语句分析更新 student_copy 关联 student 条件 是 student 的 id 等于 student_copy 表的id ; 设置 student_copy 表的 age 等于 student 的 age ; 设置 student_copy 表的 name 等于 student 的 name ;这才是正确进入Mysql 的更新查询姿势; 十五 删除表删除表中的行可以使用 DELETE 关键字 ,可以删除特定的行或者全部;使用时请先看是否丢了where子句; 15.1 删除整张表数据
语句分析 删除 全部行 来自 student_copy 表; 15.2 删除特定的行语句示例: DELETE from student WHERE id = '4'; 语句分析: 删除 行 来自 student 表条件时 id 等于 4; 15.3 更新和删除的建议
十六 SQL 分类操作16.1 SQL 分类sql 对数据库的操作分为如下三种类型,如果都学会这三种SQL语言熟练对数据库操作,说明你已经对数据库登堂入室,如果再学会数据库高级操作,说明你对数据库就有一定的使用经验,如果你还学会对数据库进行优化,分表分库,读写分离等操作,说明你使用数据库已经到专家级别;
16.2 数据库基操数据库的基本操作如下,也就是我们日常使用的操作
16.3 建表语句数据库表的日常操作如下 CREATE [TEMPORARY] TABLE[ IF NOT EXISTS] [库名.]表名 ( 表的结构定义 ) [ 表选项] 其中 TEMPORARY 表示临时表;中括号内容都表示可选,在正规的数据库版本管理开发会经常使用到; 字段的修饰如下 数据类型
表选项一般就是指定数据库引擎和字符集:
示例 CREATE TABLE IF NOT EXISTS `customer` ( 16.4 修改表结构查看所有表
查看指定数据库的表 SHOW TABLES FROM 数据库名称; 删除表
清空表(清除数据) TRUNCATE [TABLE] 表名 复制表结构
复制表结构和数据 CREATE TABLE 表名 [AS] SELECT * FROM 要复制的表名; 常见的alter操作如下: 增加一列(追加至末尾)
增加到第一列 alter table [数据库名.]表名 add [column] 字段 数据类型 first; 增加一列到指定字段名后
修改字段名的 数据类型 alter table [数据库名.]表名称 modify [column] 字段名 新的数据类型; 修改表字段的数据类型,并且移动至第一列
修改表字段的数据类型,并且移动至指定字段后面 alter table [数据库名.]表名称 modify [column] 字段名 数据类型 after 另一个字段名; 修改表字段的名称
添加主键 alter table [数据库名.]表名称 ADD PRIMARY KEY(字段名); 添加唯一键
添加索引 alter table [数据库名.]表名称 ADD INDEX [索引名] (字段名) 删除一列
删除索引 alter table [数据库名.]表名称 DROP INDEX 索引名 删除主键
删除外键 alter table [数据库名.]表名称 DROP FOREIGN KEY 外键 十七 视图17.1 视图的概念视图其实就是一张虚表,其本质上SQL的检索语句,所以其不储存任何的数据成分;我们使用视图有什么好处呢?
我们对视图的操作只能停留在查询上,如果是单表生成的视图还可以进行插入数据;如果是多表关联生成的视图,插入不会起作用;切记任何时候进行关联的时候如果关联3张表以上就是不符合规范,严重的拖累查询性能,视图也是如此,使用复杂的嵌套视图和多表关联也会极大的降低查询性能; 17.2 视图的规范
17.3 视图语句小伙伴们要记得使用视图之前要看看自己的MYSQL版本,5.0以上支持;
17.4 视图操作我们的准备表如下
准备数据如下 INSERT INTO `zszxz`.`order`(`id`, `order_name`, `create_time`, `year`) VALUES (1, '知识追寻者的订单', '2020-03-04 11:01:25', 2019); 新建简单的视图示例如下,使用 order 表 的 id , order_name , year 三个字段组成视图;as 后面就是查询语句,也可以是子查询,多表关联等复杂的查询语句;
查询视图,其使用本质与查询表一样;示例如下 SELECT * from `zszxz_order` 向视图中插入数据,插入的数据实际在实体表 order 表中切记;
删除视图 drop view `zszxz_order` 17.5 小结视图的本质上查询语句,故可以对一些简单的数据统计做成视图是个不错的选择;其次如果是开放权限给第三方公司,使用视图查询部分实体表的数据作为开放的表也是对视图的合理应用;最后,也可以将简单的表联结做成视图,简化开发; 由于视图的本质是查询语句,你可以理解为其前世就是查询,今生就是虚表,徒有其“表”,名不副实,只拥有表的部分功能; 十八 储存过程18.1 储存过程的概念我们经常使用的SQL查询语句都是单条语句,如果要使用多条语句达到一个目的就显得力不从心了,储存过程就是使用多条语句完成业务的操作,你可以理解为linux脚本编程类似,window的批处理文件那样;简单的定义储存过程就是多条SQL的集合; 我们使用储存过程能够简化复杂的单条SQL,相比于单条复杂的SQL极大提高了性能;如果表结构发生变化只需要改变储存过程使用到SQL语句的表名,如果业务逻辑发生变化,只需要跳转储存过程即可,具有很强的灵活性;建立一次储存过程,即可使用,不用反复建立,保证开发人员使用到都是相同的储存过程,保证数据可靠性;总之使用储存过程,简单,灵活,安全可靠,性能好; 18.2 存储过程语法
IN 表示输入; 示例 IN var1 Decimal(6,2)
call 储存过程名称
使用 set 和 select into 语句为变量赋值。
CASE 值 WHEN 匹配值 THEN 结果
[开始标签:] loop
repeat
18.3 储存过程示例我们准备表如下,一个订单明细表;
准备数据如下 INSERT INTO `zszxz`.`oder_detail`(`id`, `detail_name`, `price`, `oid`) VALUES (1, '毛巾', 20.00, 1); 无参储存过程 查看订单明细的所有订单名称,示例如下,跟普通的查询语句没区别;
我们再调用储存过程 call slelect_detail(); 此时就会打印内容如下
删除储存过程 drop procedure slelect_detail; 带入参储存过程示例 现在我们需要查询oid为动态的所有订单明细名称,由于考虑到oid为动态,就是需要用户自己输入,故将oid作为入参;
调用储存过程,只查询oid为1的用户的订单明细名称 call slelect_detail(1); 打印内容
删除储存过程 drop procedure slelect_detail; 带入参和出参的存储过程示例 查询任意用户的订单明细的所有金额;定义入参订单id 为 order_id , 输出总金额为 total;
调用储存过程示例 call slelect_toatal_money(1,@total); 查询 order_id 为1 总金额示例
输出为 40; 删除储存过程 drop procedure slelect_toatal_money; 18.4 if 语句示例上节的储存过程都是单条SQL,这次我们开始使用控制流程,实现复杂的储存过程; 知识追寻者对输入的 order_id 自动加5 ,然后判断 var 是否 小于7 ,如果是就查询订单明细价格,否则查询订单明细价格总和;
调用 call slelect_toatal_money(1); 输出
调用 call slelect_toatal_money(2); 输出
18.5 while 语句示例对 变量 var 进行判断,如果 var <7 就执行 查询价格语句,并且var 进行自增; create procedure slelect_toatal_money(IN order_id INT) 调用示例
输出 price 18.6 case语句示例如下语句实现的效果与上面if语句实现效果一致;
调用示例 call slelect_toatal_money(2); 输出
将参数改为1试试结果 18.7 loop语句如果 var 小于3 就 计算 价格 + var 的值; create procedure slelect_toatal_money(IN order_id INT) 调用示例
会输出三组结果 18.8 repeatrepeat 与 while 的不同之处就是 ,while 在 执行之前检查条件,其实执行之后检查条件; create procedure slelect_toatal_money(IN order_id INT) 调用示例
此时会输出2组相同结果; price
十九 游标19.1 游标的概念游标的本质就是查询后的结果集;当我们对查询的结果集进行前一行或者后一行类似的操作时就可以使用到游标 19.2 游标的语法
19.3 使用游标准备的表
准备的数据 INSERT INTO `zszxz`.`oder_detail`(`id`, `detail_name`, `price`, `oid`) VALUES (1, '毛巾', 20.00, 1); 简单的使用游标 查询oid为1 的订单明细名称 的结果集作为游标; 打开游标后抓取每行将结果赋值给变量name
调用储存过程 call printName; 打印结果如下,只有一条数据,说明上述方式只在游标中抓取到一条数据,而且是表里面行号最小的行;
在循环中使用游标 将 查询oid为1的 结果集赋值给游标;通过游标抓取每行 将 订单明细名称和价格分别赋值给变量 name 和 detail_price; 在 循环无法继续时 会出现 SQLSTATE '02000' ; 即此通过 变量 drop procedure if exists printDetail; 调用储存过程
美中不足的是会多遍历最后一行,如果要精细处理还是需要自定义标志位进行跳出循环; 二十 触发器20.1触发器的概念触发器是指当表发生改变的时候触发的动作;听起来有点抽象,举个栗子,当你往表中插入数据的时候,此时表发生了改变,现在想要在每次插入数据之前检测所有的入参是否都是小写,此时就可以用触发器来检测;经过上面的分析知道使用一个基本的触发器,至少表要发生改变,还要满足一个被触发的事件; 表发生改变通常指 增删改,其动作可以发生在增删改 之前或者之后;触发事件就是我们需要写的储存过程;
20.2 触发器的基本语法
20.3 insert 触发器示例
-- insert 触发器
-- 删除触发器 20.4 update 触发器示例将插入后触发器改为更新后的触发器如下 , 只需要改动 after insert 为 after update 即可;
将之前的插入的SQL语句进行修改价格,并查询价格,此时价格为30;NEW虚表储存的是即将更新的数据; UPDATE `oder_detail` SET `price` = 30.00 WHERE `id` = 6; 删除触发器
将 更新触发器的NEW表改为OLD表 CREATE TRIGGER getPrice AFTER update ON oder_detail FOR EACH ROW 更新价格为40
此时查询 价格为30,说明OLD表触发的是原始数据值; select @price;
20.5 delete 触发器将 更新触发器改为 delete 触发器, 之前我们省略了 begin, end 如果是多条执行语句则需要加上;
删除之前的SQL数据 delete from oder_detail where `id` = 6; 查询价格为40,OLD表存放的是将要被删除的数据;
二十一 用户操作有关用户账号的信息储存mysql的MYSQL数据库,故如果需要查看用户信息,则需要进入MYSQL库; 21.1查看用户信息user表储存了所有的登陆账号;使用mysql库查询user表中的user; use mysql; 打印
21.2 创建用户
示例:创建用户 zszxz ,并指定密码为 zszxz; create user zszxz IDENTIFIED by 'zszxz'; 21.3 重名名用户
示例重命名 用户 zszxz 为 lsc
21.4 删除用户drop user 用户名 示例:删除用户lsc drop user lsc; 21.5 更改密码
示例:为用户 zszxz 更改密码为 lsc
二十二 权限操作22.1 查看用户权限
示例:查看用户zszxz 拥有的权限 SHOW GRANTS FOR zszxz 打印
查询出一条权限,但 USAGE 表示 根本没有权限; 22.2 授予权限
常见的权限 all, create, drop, insert, update, delete,select; 示例 给用户zszxz 分配 zszxz 库中的所有表的查询权限; grant select on zszxz.* to zszxz; 再看下 zszxz变成 2 条
22.3 撤销权限
示例:撤销 用户 zszxz 对 zszxz库里面所有表的查询操作; revoke select on zszxz.* from zszxz 22.4 权限列表使用授权,撤销权限时可以参考如下权限列表;
二十三 mysql 架构与锁23.1 MYSQL架构概览MYSQL 的层级大概可以分为3类;第一层 为 连接层,只要负责MYSQL的数据库连接,安全认证的功能; 第二层是MYSQL的核心层面,其主要功能包括,MYSQL的查询,缓存,执行计划,优化等都在第二层实现; 第三层是引擎层,为MYSQL指定不同的引擎将达到不同的数据操作效果; 23.2 Query CacheMYSQL 的 Query Cache 是 基于 hash 值计算进行匹配的缓存机制;通常在大数据量的情况下如果开启Query Cache 会 频繁的计算Hash ,会增加性能的消耗,得不偿失,生产环境中建议关闭该选项; 可以使用 语句
https://blog.csdn.net/dongnan591172113/article/details/52084041 https://www.jianshu.com/p/3ab10180fbd5 23.3 读锁关于锁的知识希望读者学习过高并发相关知识,对所有的锁分类有个清晰的认识,学习本篇关于锁的概念将不会遇到阻碍;在MYSQL中根据不同的引擎,主要会出现三类锁的情况,即 表锁,读锁 和写锁;读锁很好理解,在MYSQL 中 读锁也是 一般情况下我们手动给一条或者某个范围内(一般使用在储存过程)的数据加上读锁; 使用语法示例如下 SELECT 字段 from 表名 [where 条件] lock in share mode; 23.4 写锁写锁是 格式示例
23.5 锁粒度锁粒度是指对资源锁定范围的一个程度,使用不同的锁定策略达到并发性能较优的结果;通常锁粒度使用策略情况分为,行锁,表锁,页锁的情况; **表锁:**即对整张表进行加锁,其性能开销较小,加锁的速度较快,但缺点也很明显,其锁粒度大,并发低;如果要手动加表锁,语法示例 行锁:即对行进行锁定,能够最大支持并发量,故锁粒度最小,但其枷锁速度慢,性能消耗大,会出现死锁;行锁的种类又有 记录锁(主键或者唯一索引都属于记录锁),间隙锁(GAP),记录锁和间隙锁的组合(next -key lock);间隙锁一般用于查询条件是范围情况下,而非相等条件; 页锁:通常情况下遇不到页锁,其开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间;
还有锁的其它分类也会使用到比如乐观锁(基于版本号实现),注意点是条件必须是主键,读取时将数据版本号读出,更新数据时,版本号加1;将查询的数据进行对比,如果版本号不一致就是过期数据; 查询示例 select id,value,version from 表名 where id = #{id} 更新示例
悲观锁(如表锁,行锁,读写锁都是悲观锁); 如果看了知识追寻者写的锁知识还有困惑可以参考如下链接 https:///post/5b82e0196fb9a019f47d1823 23.6 引擎简介在上面的图例中可以看见MYSQL支持多种引擎,当然远不止图中显示的引擎数量;我们主流使用的引擎就是 InnoDB,其次是 MyISAM,特殊情况下会使用到Memory;引擎的知识是一本书都无法概括的内容,知识追寻者在这边给小伙伴们做个简介,有个大概的了解就好; InnoDB 是使用最广泛的引擎,也是最重要的引擎,读者有必要了解其储存性能;InnoDB 是 可重复读的事物隔离级别,但其实现了next key lock ,防止的幻读出现;其基于聚簇索引实现;其组要组成结构为内存结构,线程,磁盘文件组; MyISAM在早期版本是MYSQL的默认引擎,在MYSQL5.1之后不再使用;其不支持事物,不支持行锁,默认表锁,并发量低,; Menory引擎故名思意,其储存内容都是存放在引擎当中,支持Hash和Btree索引,其数据读取快,但缺点也很明显,服务器如果发生故障重启后就会造成数据丢失; 二十四 锁等待锁等待的意思非常好理解,就是session (事物会话,开启一个事物代表一个会话)A 对 某行数据获取独占锁(在这边一般就是写锁),然后session B 对相同的行进行获取独占锁就发生了锁等待;简单理解就是 小孩子抢玩具,谁先抢到 谁 先玩,没抢到的玩具的孩子只能 等待 抢到玩具孩子玩腻了再给你,瞬间泪奔有木有,就是这么残酷,当然MYSQL 没 这么残忍 其 还是有一个保留参数 知识追寻者 做个实验: session A 执行如下语句,开启事物,更新索引为1 的语句;此时 session A 获取了 id= 1 这条 语句的 写锁权限; BEGIN; session B 执行如下 语句 , 跟 上面的语句一样 ,由于 id =1 这条数据的写锁已经被session A 获取,故会发生锁等待的情况;
知识追寻者这边默认等待了50秒 就报了如下异常 Lock wait timeout exceeded; try restarting transaction 查看 默认锁等待 语句
二十五 死锁25.1 死锁的产生死锁 就是 两个以上的会话 在 抢占 资源过程中 ,产生相互等待的情况;有点绕是不是,其实很简单 死锁是建立在 锁等待的基础上,session A 获取 id = 1 的写锁 , session B 获取 id =2 的写锁 ,此时由于索引不同,故不会长生锁等待现象 ;当 session A 尝试 获取 id =2 的 写锁时 ,由于 id = 2 写锁已经被 session B 获取 ,故产生锁等待;当 session B 尝试 获取 id = 1 的写锁时 ,由于id =1 写锁已经被 session A 获取, 此时 产生锁等待;由于 session A 与 session B 同时 都在 锁 等待状态,产生了等待对方释放锁,故会产生死锁; 知识追寻者做个试验 session A 执行语句, 获取 id =1 的 写锁权限; BEGIN; session B 执行语句, 获取 id =2 的 写锁权限;
session A 执行语句, 尝试获取 id =2 的 写锁权限,进入锁等待状态 update `order` set `year`= '2022' where id = '2'; session B 执行语句, 尝试获取 id =1 的 写锁权限,进入锁等待状态
当 B 进入 锁等待后就直接报死锁异常 Deadlock found when trying to get lock; try restarting transaction 25.2 查看死锁可以使用
不得不说下字母代表锁得类型如下
可以看见上面得语句 (1) 代表 事物A ,MYSQL 线程id 17001 ;(2) 代表事物B, MYSQL 线程id 17002 ; 事物 A 与B 都在等待 对方释放锁 ,产生了死锁;
如何解决死锁,知识追寻者这边给个思路: 查找到死锁线程,杀死MYSQL死锁的线程(kill命令); 如果事物未提交,直接回滚事物; 25.3 如何避免死锁
程序员GitHub 专注于分享GitHub上Python,Java,Go,前端开发等优质的学习资源,并分享程序员圈的新鲜趣事,热门干货,职场感悟。 42篇原创内容 公众号 |
|
来自: O听_海_轩O > 《Python基础入门讲解》