Mysql视图、触发器、存储过程、函数

1 Mysql视图

1.1 视图概述

视图是由数据库中的一个表或多个表导出的虚拟表,是一种虚拟存在的表,方便用户对数据的操作。

image-20220128142708469.png

1.1.1 视图的概念

视图是一个虚拟表,是从数据库中一个或多个表中导出来的表,其内容由查询定义。同真实表一样,视图包含一系列带有名称的列和行数据。但是,数据库中只存放了视图的定义,而并没有存放视图中的数据。这些数据存放在原来的表中。使用视图查询数据时,数据库系统会从原来的表中取出对应的数据。因此,视图中的数据是依赖于原来的表中的数据的。一旦表中的数据发生改变,显示在视图中的数据也会发生改变。

视图是存储在数据库中的查询的SQL语句,它主要出于两种原因:安全原因,视图可以隐藏一些数据,例如,员工信息表,可以用视图只显示姓名、工龄、地址,而不显示社会保险号和工资数等;另一个原因是可使复杂的查询易于理解和使用。

总结:小型项目的数据库可以不使用视图,但是在大型项目中,以及数据表比较复杂的情况下,视图的价值就凸显出来了,它可以帮助我们把经常查询的结果集放到虚拟表中,提升使用效率。理解和使用起来都非常方便。

1.1.2 视图的作用

对其中所引用的基础表来说,视图的作用类似于筛选。定义视图的筛选可以来自当前或其他数据库的一个或多个表,或者其他视图。通过视图进行查询没有任何限制,通过它们进行数据修改时的限制也很少。视图的作用归纳为如下几点。

1、简单性

看到的就是需要的。视图不仅可以简化用户对数据的理解,也可以简化他们的操作。那些被经常使用的查询可以被定义为视图,从而使得用户不必为以后的操作每次指定全部的条件。

2、安全性

视图的安全性可以防止未授权用户查看特定的行或列,使有权限用户只能看到表中特定行的方法,如下:

(1)在表中增加一个标志用户名的列。

(2)建立视图,使用户只能看到标有自己用户名的行。

(3)把视图授权给其他用户。

3、逻辑数据独立性

视图可以使应用程序和数据库表在一定程度上独立。如果没有视图,程序一定是建立在表上的。有了视图之后,程序可以建立在视图之上,从而程序与数据库表被视图分割开来。视图可以在以下几个方面使程序与数据独立。

(1)如果应用建立在数据库表上,当数据库表发生变化时,可以在表上建立视图,通过视图屏蔽表的变化,从而使应用程序可以不动。

(2)如果应用建立在数据库表上,当应用发生变化时,可以在表上建立视图,通过视图屏蔽应用的变化,从而使数据库表不动。

(3)如果应用建立在视图上,当数据库表发生变化时,可以在表上修改视图,通过视图屏蔽表的变化,从而使应用程序可以不动。

(4)如果应用建立在视图上,当应用发生变化时,可以在表上修改视图,通过视图屏蔽应用的变化,从而使数据库可以不动。

1.2 创建视图

创建视图是指在已经存在的数据库表上建立视图。视图可以建立在一张表中,也可以建立在多张表中。

1.2.1 查看创建视图的权限

创建视图需要具有CREATE VIEW的权限。同时应该具有查询涉及的列的SELECT权限。可以使用SELECT语句来查询这些权限信息。查询语法如下:

  1. SELECT Select_priv,Create_view_priv FROM mysql.user WHERE user='用户名';

参数说明:

  • Select_priv:属性表示用户是否具有SELECT权限,Y表示拥有SELECT权限,N表示没有。
  • Create_view_priv:属性表示用户是否具有CREATE VIEW权限;
  • mysql.user:表示MySQL数据库下面的user表。
  • 用户名:参数表示要查询是否拥有权限的用户,该参数需要用单引号引起来。

示例:查询MySQL中root用户是否具有创建视图的权限。

  1. SELECT * FROM mysql.user WHERE user='root';

1.2.2 创建视图

MySQL中,创建视图是通过CREATE VIEW语句实现的。其语法如下:

  1. CREATE [OR REPLACE] [ALGORITHM={UNDEFINED|MERGE|TEMPTABLE}]
  2. VIEW 视图名[(属性清单)]
  3. AS SELECT语句
  4. [WITH [CASCADED|LOCAL] CHECK OPTION];

参数说明:

  • ALGORITHM:可选项,表示视图选择的算法。
  • 视图名:表示要创建的视图名称。
  • 属性清单:可选项,指定视图中各个属性的名词,默认情况下与SELECT语句中的查询的属性相同。
  • SELECT语句:表示一个完整的查询语句,将查询记录导入视图中。
  • WITH CHECK OPTION:可选项,表示更新视图时要保证在该视图的权限范围之内。

示例:创建视图。

  1. CREATE OR REPLACE VIEW view_user
  2. AS
  3. SELECT id,name FROM tb_user;

示例:创建视图同时,指定属性清单。

  1. CREATE OR REPLACE VIEW view_user (a_id,a_name)
  2. AS
  3. SELECT id,name FROM tb_user;

创建视图时需要注意以下几点:

  • 运行创建视图的语句需要用户具有创建视图(create view)的权限,若加了[or replace]时,还需要用户具有删除视图(drop view)的权限;
  • select语句不能包含from子句中的子查询;
  • select语句不能引用系统或用户变量;
  • select语句不能引用预处理语句参数;
  • 在存储子程序内,定义不能引用子程序参数或局部变量;
  • 在定义中引用的表或视图必须存在。但是,创建了视图后,能够舍弃定义引用的表或视图。要想检查视图定义是否存在这类问题,可使用check table语句;
  • 在定义中不能引用temporary表,不能创建temporary视图;
  • 在视图定义中命名的表必须已存在;
  • 不能将触发程序与视图关联在一起;
  • 在视图定义中允许使用order by,但是,如果从特定视图进行了选择,而该视图使用了具有自己order by的语句,它将被忽略。

1.3 修改视图

修改视图是指修改数据库中已存在的表的定义。当基本表的某些字段发生改变时,可以通过修改视图来保持视图和基本表之间一致。MySQL中通过CREATE OR REPLACE VIEW语句和ALTER VIEW语句来修改视图。

示例:修改视图

  1. ALTER VIEW view_user
  2. AS
  3. SELECT id,name FROM tb_user where id in (select id from tb_user);

说明:ALTER VIEW语句改变了视图的定义,该语句与CREATE OR REPLACE VIEW语句有着同样的限制,如果删除并重新创建一个视图,就必须重新为它分配权限。

1.4 删除视图

删除视图是指删除数据库中已存在的视图。删除视图时,只能删除视图的定义,不会删除数据。MySQL中,使用DROP VIEW语句来删除视图。但是,用户必须拥有DROP权限。

示例:删除视图。

  1. DROP VIEW IF EXISTS view_user;

1.5 MySQL视图中使用IF和CASE语句

在创建视图时,经常需要使用到MySQL的流程控制语句,如:IF语句和CASE语句。

示例:创建MySQL视图中使用IF和CASE语句。

(1)创建员工信息表。

— 判断数据表是否存在,存在则删除

  1. DROP TABLE IF EXISTS tb_staff;

— 创建数据表

  1. CREATE TABLE IF NOT EXISTS tb_staff
  2. (
  3. id INT AUTO_INCREMENT PRIMARY KEY COMMENT '编号',
  4. NAME VARCHAR(50) NOT NULL COMMENT '姓名',
  5. sex INT COMMENT '性别(1:男;2:女;)',
  6. dept_code VARCHAR(10) COMMENT '部门编号',
  7. is_post BIT COMMENT '是否在职(0:否;1:是)'
  8. ) COMMENT = '员工信息表';

(2)新增员工数据。

— 新增数据

  1. INSERT INTO tb_staff(NAME,sex,dept_code,is_post) VALUES('pan_junbiao的博客_01',1,'1001',1);
  2. INSERT INTO tb_staff(NAME,sex,dept_code,is_post) VALUES('pan_junbiao的博客_02',2,'1002',1);
  3. INSERT INTO tb_staff(NAME,sex,dept_code,is_post) VALUES('pan_junbiao的博客_03',1,'1003',0);
  4. INSERT INTO tb_staff(NAME,sex,dept_code,is_post) VALUES('pan_junbiao的博客_04',1,'1001',1);
  5. INSERT INTO tb_staff(NAME,sex,dept_code,is_post) VALUES('pan_junbiao的博客_05',2,'1008',1);
  6. INSERT INTO tb_staff(NAME,sex,dept_code,is_post) VALUES('pan_junbiao的博客_06',1,'1001',0);
  7. INSERT INTO tb_staff(NAME,sex,dept_code,is_post) VALUES('pan_junbiao的博客_07',2,'1002',1);
  8. INSERT INTO tb_staff(NAME,sex,dept_code,is_post) VALUES('pan_junbiao的博客_08',1,'1003',0);
  9. INSERT INTO tb_staff(NAME,sex,dept_code,is_post) VALUES('pan_junbiao的博客_09',1,'1001',1);
  10. INSERT INTO tb_staff(NAME,sex,dept_code,is_post) VALUES('pan_junbiao的博客_10',2,'1008',0);

查询员工信息表结果:

image-20211003212813936.png

(3)创建员工视图,在视图中使用IF和CASE语句。

— 创建视图

  1. CREATE OR REPLACE VIEW view_staff
  2. AS
  3. SELECT id
  4. ,NAME
  5. ,IF(sex=1,'男','女') AS sex_name
  6. ,CASE dept_code
  7. WHEN '1001' THEN '研发部'
  8. WHEN '1002' THEN '人事部'
  9. WHEN '1003' THEN '财务部'
  10. ELSE '其他'
  11. END AS dept_name
  12. ,IF(is_post,'在职','离职') AS is_post_name
  13. FROM tb_staff
  14. ;

查询员工视图结果:

image-20211003212842246.png

1.6 Mysql中默认的视图

1.6.1 information_schema.tables视图

information_schema.tables视图常用列属性

  1. DESC information_schema.TABLES
  2. TABLE_SCHEMA ---->所有数据库的库名
  3. TABLE_NAME ---->所有表的表名
  4. ENGINE ---->引擎
  5. TABLE_ROWS ---->表的行数
  6. AVG_ROW_LENGTH ---->表中行的平均行(字节)
  7. INDEX_LENGTH ---->索引的占用空间大小(字节)

1.6.2 information_schema.tables视图的案例说明

  1. 查询整个数据库中所有库和所对应的表信息
  1. SELECT TABLE_SCHEMA,GROUP_CONCAT(TABLE_NAME)
  2. FROM information_schema.tables
  3. GROUP BY TABLE_SCHEMA;
  1. 统计所有库下的表个数
  1. SELECT TABLE_SCHEMA,COUNT(TABLE_NAME)
  2. FROM information_schema.tables
  3. GROUP BY TABLE_SCHEMA;
  1. 查询所有innodb引擎的表及所在的库
  1. SELECT TABLE_SCHEMA,TABLE_NAME,ENGINE
  2. FROM information_schema.TABLES
  3. WHERE ENGINE='innodb';
  1. 统计mysql数据库下每张表的磁盘空间占用

数据库下表所占用空间的计算

  1. 表中的平均行(字节)*表的行数+索引的占用空间大小
  2. AVG_ROW_LENGTH*TABLE_ROWS+INDEX_LENGTH
  3. 单位为字节
  4. SELECT TABLE_SCHEMA,CONCAT((TABLE_ROWS*AVG_ROW_LENGTH+INDEX_LENGTH)/1024,"KB") AS size_KB
  5. FROM information_schema.TABLES
  6. WHERE TABLE_SCHEMA='mysql';
  7. #CONCAT((TABLE_ROWS*AVG_ROW_LENGTH+INDEX_LENGTH)/1024,"KB")
  8. #拼接单位KB
  1. 统计每个数据库所占用的磁盘空间
  1. SELECT
  2. TABLE_SCHEMA,
  3. CONCAT(SUM(TABLE_ROWS*AVG_ROW_LENGTH+INDEX_LENGTH)/1024," KB") AS Total_KB
  4. FROM information_schema.tables
  5. GROUP BY table_schema;

1.7 视图总结

image-20220128144051477.png

2 触发器

使用触发器可以定制用户对表进行【增、删、改】操作时前后的行为,注意:没有查询

2.1 创建触发器

  1. # 插入前
  2. CREATE TRIGGER tri_before_insert_tb1 BEFORE INSERT ON tb1 FOR EACH ROW
  3. BEGIN
  4. ...
  5. END
  6. # 插入后
  7. CREATE TRIGGER tri_after_insert_tb1 AFTER INSERT ON tb1 FOR EACH ROW
  8. BEGIN
  9. ...
  10. END
  11. # 删除前
  12. CREATE TRIGGER tri_before_delete_tb1 BEFORE DELETE ON tb1 FOR EACH ROW
  13. BEGIN
  14. ...
  15. END
  16. # 删除后
  17. CREATE TRIGGER tri_after_delete_tb1 AFTER DELETE ON tb1 FOR EACH ROW
  18. BEGIN
  19. ...
  20. END
  21. # 更新前
  22. CREATE TRIGGER tri_before_update_tb1 BEFORE UPDATE ON tb1 FOR EACH ROW
  23. BEGIN
  24. ...
  25. END
  26. # 更新后
  27. CREATE TRIGGER tri_after_update_tb1 AFTER UPDATE ON tb1 FOR EACH ROW
  28. BEGIN
  29. ...
  30. END
  1. #准备表
  2. CREATE TABLE cmd (
  3. id INT PRIMARY KEY auto_increment,
  4. USER CHAR (32),
  5. priv CHAR (10),
  6. cmd CHAR (64),
  7. sub_time datetime, #提交时间
  8. success enum ('yes', 'no') #0代表执行失败
  9. );
  10. CREATE TABLE errlog (
  11. id INT PRIMARY KEY auto_increment,
  12. err_cmd CHAR (64),
  13. err_time datetime
  14. );
  15. #创建触发器
  16. delimiter //
  17. CREATE TRIGGER tri_after_insert_cmd AFTER INSERT ON cmd FOR EACH ROW
  18. BEGIN
  19. IF NEW.success = 'no' THEN #等值判断只有一个等号
  20. INSERT INTO errlog(err_cmd, err_time) VALUES(NEW.cmd, NEW.sub_time) ; #必须加分号
  21. END IF ; #必须加分号
  22. END//
  23. delimiter ;
  24. #往表cmd中插入记录,触发触发器,根据IF的条件决定是否插入错误日志
  25. INSERT INTO cmd (
  26. USER,
  27. priv,
  28. cmd,
  29. sub_time,
  30. success
  31. )
  32. VALUES
  33. ('egon','0755','ls -l /etc',NOW(),'yes'),
  34. ('egon','0755','cat /etc/passwd',NOW(),'no'),
  35. ('egon','0755','useradd xxx',NOW(),'no'),
  36. ('egon','0755','ps aux',NOW(),'yes');
  37. #查询错误日志,发现有两条
  38. mysql> select * from errlog;
  39. +----+-----------------+---------------------+
  40. | id | err_cmd | err_time |
  41. +----+-----------------+---------------------+
  42. | 1 | cat /etc/passwd | 2017-09-14 22:18:48 |
  43. | 2 | useradd xxx | 2017-09-14 22:18:48 |
  44. +----+-----------------+---------------------+
  45. 2 rows in set (0.00 sec)

2.2 使用触发器

触发器无法由用户直接调用,而知由于对表的【增/删/改】操作被动引发的。

2.3 删除触发器

  1. drop trigger tri_after_insert_cmd;

3 存储过程

3.1 介绍

SQL的存储过程和视图一样,都是对SQL代码进行封装,可以反复利用。它和视图有着同样的优点,清晰、安全,还可以减少网络传输量。不过它和视图不同,视图是虚拟表,通常不对底层数据表直接操作,而存储过程是程序化的SQL,可以直接操作底层数据表,相比于面向集合的操作方式,能够实现一些更复杂的数据处理。存储过程可以说是由SQL语句和流控制语句构成的语句集合,它和我们之前学到的函数一样,可以接受输入参数,也可以返回输出参数给调用者,返回计算结果。

使用存储过程的优点:

  1. #1. 用于替代程序写的SQL语句,实现程序与sql解耦
  2. #2. 基于网络传输,传别名的数据量小,而直接传sql数据量

使用存储过程的缺点:

  1. #1. 程序员扩展功能不方便

补充:程序与数据库结合使用的三种方式

  1. #方式一:
  2. MySQL:存储过程
  3. 程序:调用存储过程
  4. #方式二:
  5. MySQL
  6. 程序:纯SQL语句
  7. #方式三:
  8. MySQL:
  9. 程序:类和对象,即ORM(本质还是纯SQL语句)

3.2 创建简单存储过程(无参)

  1. delimiter //
  2. create procedure p1()
  3. BEGIN
  4. select * from blog;
  5. INSERT into blog(name,sub_time) values("xxx",now());
  6. END //
  7. delimiter ;
  8. #在mysql中调用
  9. call p1()
  10. #在python中基于pymysql调用
  11. cursor.callproc('p1')
  12. print(cursor.fetchall())

3.3 创建存储过程(有参)

  1. 对于存储过程,可以接收参数,其参数有三类:
  2. #in 仅用于传入参数用
  3. #out 仅用于返回值用
  4. #inout 既可以传入又可以当作返回值
  1. #in传入参数
  2. delimiter //
  3. create procedure p2(
  4. in n1 int,
  5. in n2 int
  6. )
  7. BEGIN
  8. select * from blog where id > n1;
  9. END //
  10. delimiter ;
  11. #在mysql中调用
  12. call p2(3,2)
  13. #在python中基于pymysql调用
  14. cursor.callproc('p2',(3,2))
  15. print(cursor.fetchall())
  1. #out返回值
  2. delimiter //
  3. create procedure p3(
  4. in n1 int,
  5. out res int
  6. )
  7. BEGIN
  8. select * from blog where id > n1;
  9. set res = 1;
  10. END //
  11. delimiter ;
  12. #在mysql中调用
  13. set @res=0; #0代表假(执行失败),1代表真(执行成功)
  14. call p3(3,@res);
  15. select @res;
  16. #在python中基于pymysql调用
  17. cursor.callproc('p3',(3,0)) #0相当于set @res=0
  18. print(cursor.fetchall()) #查询select的查询结果
  19. cursor.execute('select @_p3_0,@_p3_1;') #@p3_0代表第一个参数,@p3_1代表第二个参数,即返回值
  20. print(cursor.fetchall())
  1. #inout即可传入也可返回
  2. delimiter //
  3. create procedure p4(
  4. inout n1 int
  5. )
  6. BEGIN
  7. select * from blog where id > n1;
  8. set n1 = 1;
  9. END //
  10. delimiter ;
  11. #在mysql中调用
  12. set @x=3;
  13. call p4(@x);
  14. select @x;
  15. #在python中基于pymysql调用
  16. cursor.callproc('p4',(3,))
  17. print(cursor.fetchall()) #查询select的查询结果
  18. cursor.execute('select @_p4_0;')
  19. print(cursor.fetchall())

3.4 执行存储过程

  1. #在mysql中调用存储过程
  2. -- 无参数
  3. call proc_name()
  4. -- 有参数,全in
  5. call proc_name(1,2)
  6. -- 有参数,有inoutinout
  7. set @t1=0;
  8. set @t2=3;
  9. call proc_name(1,2,@t1,@t2)
  10. 执行存储过程

3.5 删除存储过程

  1. drop procedure proc_name;

3.6 关于存储过程使用的争议

尽管存储过程有诸多优点,但是对于存储过程的使用,一直都存在着很多争议,比如有些公司对于大型项目要求使用存储过程,而有些公司在手册中明确禁止使用存储过程,为什么这些公司对存储过程的使用需求差别这么大呢?

我们得从存储过程的特点来找答案。

你能看到存储过程有很多好处。

首先存储过程可以一次编译多次使用。存储过程只在创造时进行编译,之后的使用都不需要重新编译,这就提升了 SQL 的执行效率。其次它可以减少开发工作量。将代码封装成模块,实际上是编程的核心思想之一,这样可以把复杂的问题拆解成不同的模块,然后模块之间可以重复使用,在减少开发工作量的同时,还能保证代码的结构清晰。还有一点,存储过程的安全性强,我们在设定存储过程的时候可以设置对用户的使用权限,这样就和视图一样具有较强的安全性。最后它可以减少网络传输量,因为代码封装到存储过程中,每次使用只需要调用存储过程即可,这样就减少了网络传输量。同时在进行相对复杂的数据库操作时,原本需要使用一条一条的 SQL 语句,可能要连接多次数据库才能完成的操作,现在变成了一次存储过程,只需要连接一次即可。

基于上面这些优点,不少大公司都要求大型项目使用存储过程,比如微软、IBM 等公司。但是国内的阿里并不推荐开发人员使用存储过程,这是为什么呢?

存储过程虽然有诸如上面的好处,但缺点也是很明显的。

它的可移植性差,存储过程不能跨数据库移植,比如在 MySQL、Oracle 和 SQL Server 里编写的存储过程,在换成其他数据库时都需要重新编写。

其次调试困难,只有少数 DBMS 支持存储过程的调试。对于复杂的存储过程来说,开发和维护都不容易。

此外,存储过程的版本管理也很困难,比如数据表索引发生变化了,可能会导致存储过程失效。我们在开发软件的时候往往需要进行版本管理,但是存储过程本身没有版本控制,版本迭代更新的时候很麻烦。

最后它不适合高并发的场景,高并发的场景需要减少数据库的压力,有时数据库会采用分库分表的方式,而且对可扩展性要求很高,在这种情况下,存储过程会变得难以维护,增加数据库的压力,显然就不适用了。

了解了存储过程的优缺点之后,我想说的是,存储过程既方便,又有局限性。尽管不同的公司对存储过程的态度不一,但是对于我们开发人员来说,不论怎样,掌握存储过程都是必备的技能之一。

3.7 存储过程总结

image-20220128150307334.png

4 函数

4.1 常用函数大全

MySQL中提供了许多内置函数,例如:

  1. 一、数学函数
  2. ROUND(x,y)
  3. 返回参数x的四舍五入的有y位小数的值
  4. RAND()
  5. 返回0到1内的随机值,可以通过提供一个参数(种子)使RAND()随机数生成器生成一个指定的值。
  6. 二、聚合函数(常用于GROUP BY从句的SELECT查询中)
  7. AVG(col)返回指定列的平均值
  8. COUNT(col)返回指定列中非NULL值的个数
  9. MIN(col)返回指定列的最小值
  10. MAX(col)返回指定列的最大值
  11. SUM(col)返回指定列的所有值之和
  12. GROUP_CONCAT(col) 返回由属于一组的列值连接组合而成的结果
  13. 三、字符串函数
  14. CHAR_LENGTH(str)
  15. 返回值为字符串str 的长度,长度的单位为字符。一个多字节字符算作一个单字符。
  16. CONCAT(str1,str2,...)
  17. 字符串拼接
  18. 如有任何一个参数为NULL ,则返回值为 NULL
  19. CONCAT_WS(separator,str1,str2,...)
  20. 字符串拼接(自定义连接符)
  21. CONCAT_WS()不会忽略任何空字符串。 (然而会忽略所有的 NULL)。
  22. CONV(N,from_base,to_base)
  23. 进制转换
  24. 例如:
  25. SELECT CONV('a',16,2); 表示将 a 16进制转换为2进制字符串表示
  26. FORMAT(X,D)
  27. 将数字X 的格式写为'#,###,###.##',以四舍五入的方式保留小数点后 D 位, 并将结果以字符串的形式返回。若 D 0, 则返回结果不带有小数点,或不含小数部分。
  28. 例如:
  29. SELECT FORMAT(12332.1,4); 结果为: '12,332.1000'
  30. INSERT(str,pos,len,newstr)
  31. str的指定位置插入字符串
  32. pos:要替换位置其实位置
  33. len:替换的长度
  34. newstr:新字符串
  35. 特别的:
  36. 如果pos超过原字符串长度,则返回原字符串
  37. 如果len超过原字符串长度,则由新字符串完全替换
  38. INSTR(str,substr)
  39. 返回字符串 str 中子字符串的第一个出现位置。
  40. LEFT(str,len)
  41. 返回字符串str 从开始的len位置的子序列字符。
  42. LOWER(str)
  43. 变小写
  44. UPPER(str)
  45. 变大写
  46. REVERSE(str)
  47. 返回字符串 str ,顺序和字符顺序相反。
  48. SUBSTRING(str,pos) , SUBSTRING(str FROM pos) SUBSTRING(str,pos,len) , SUBSTRING(str FROM pos FOR len)
  49. 不带有len 参数的格式从字符串str返回一个子字符串,起始于位置 pos。带有len参数的格式从字符串str返回一个长度同len字符相同的子字符串,起始于位置 pos 使用 FROM的格式为标准 SQL 语法。也可能对pos使用一个负值。假若这样,则子字符串的位置起始于字符串结尾的pos 字符,而不是字符串的开头位置。在以下格式的函数中可以对pos 使用一个负值。
  50. mysql> SELECT SUBSTRING('Quadratically',5);
  51. -> 'ratically'
  52. mysql> SELECT SUBSTRING('foobarbar' FROM 4);
  53. -> 'barbar'
  54. mysql> SELECT SUBSTRING('Quadratically',5,6);
  55. -> 'ratica'
  56. mysql> SELECT SUBSTRING('Sakila', -3);
  57. -> 'ila'
  58. mysql> SELECT SUBSTRING('Sakila', -5, 3);
  59. -> 'aki'
  60. mysql> SELECT SUBSTRING('Sakila' FROM -4 FOR 2);
  61. -> 'ki'
  62. 四、日期和时间函数
  63. CURDATE()或CURRENT_DATE() 返回当前的日期
  64. CURTIME()或CURRENT_TIME() 返回当前的时间
  65. DAYOFWEEK(date) 返回date所代表的一星期中的第几天(1~7)
  66. DAYOFMONTH(date) 返回date是一个月的第几天(1~31)
  67. DAYOFYEAR(date) 返回date是一年的第几天(1~366)
  68. DAYNAME(date) 返回date的星期名,如:SELECT DAYNAME(CURRENT_DATE);
  69. FROM_UNIXTIME(ts,fmt) 根据指定的fmt格式,格式化UNIX时间戳ts
  70. HOUR(time) 返回time的小时值(0~23)
  71. MINUTE(time) 返回time的分钟值(0~59)
  72. MONTH(date) 返回date的月份值(1~12)
  73. MONTHNAME(date) 返回date的月份名,如:SELECT MONTHNAME(CURRENT_DATE);
  74. NOW() 返回当前的日期和时间
  75. QUARTER(date) 返回date在一年中的季度(1~4),如SELECT QUARTER(CURRENT_DATE);
  76. WEEK(date) 返回日期date为一年中第几周(0~53)
  77. YEAR(date) 返回日期date的年份(1000~9999)
  78. 重点:
  79. DATE_FORMAT(date,format) 根据format字符串格式化date
  80. mysql> SELECT DATE_FORMAT('2009-10-04 22:23:00', '%W %M %Y');
  81. -> 'Sunday October 2009'
  82. mysql> SELECT DATE_FORMAT('2007-10-04 22:23:00', '%H:%i:%s');
  83. -> '22:23:00'
  84. mysql> SELECT DATE_FORMAT('1900-10-04 22:23:00',
  85. -> '%D %y %a %d %m %b %j');
  86. -> '4th 00 Thu 04 10 Oct 277'
  87. mysql> SELECT DATE_FORMAT('1997-10-04 22:23:00',
  88. -> '%H %k %I %r %T %S %w');
  89. -> '22 22 10 10:23:00 PM 22:23:00 00 6'
  90. mysql> SELECT DATE_FORMAT('1999-01-01', '%X %V');
  91. -> '1998 52'
  92. mysql> SELECT DATE_FORMAT('2006-06-00', '%d');
  93. -> '00'
  94. 五、加密函数
  95. MD5()
  96. 计算字符串strMD5校验和
  97. PASSWORD(str)
  98. 返回字符串str的加密版本,这个加密过程是不可逆转的,和UNIX密码加密过程使用不同的算法。
  99. 六、控制流函数
  100. CASE WHEN[test1] THEN [result1]...ELSE [default] END
  101. 如果testN是真,则返回resultN,否则返回default
  102. CASE [test] WHEN[val1] THEN [result]...ELSE [default]END
  103. 如果testvalN相等,则返回resultN,否则返回default
  104. IF(test,t,f)
  105. 如果test是真,返回t;否则返回f
  106. IFNULL(arg1,arg2)
  107. 如果arg1不是空,返回arg1,否则返回arg2
  108. NULLIF(arg1,arg2)
  109. 如果arg1=arg2返回NULL;否则返回arg1
  110. 七、控制流函数小练习
  111. #7.1、准备表
  112. /*
  113. Navicat MySQL Data Transfer
  114. Source Server : localhost_3306
  115. Source Server Version : 50720
  116. Source Host : localhost:3306
  117. Source Database : student
  118. Target Server Type : MYSQL
  119. Target Server Version : 50720
  120. File Encoding : 65001
  121. Date: 2018-01-02 12:05:30
  122. */
  123. SET FOREIGN_KEY_CHECKS=0;
  124. -- ----------------------------
  125. -- Table structure for course
  126. -- ----------------------------
  127. DROP TABLE IF EXISTS `course`;
  128. CREATE TABLE `course` (
  129. `c_id` int(11) NOT NULL,
  130. `c_name` varchar(255) DEFAULT NULL,
  131. `t_id` int(11) DEFAULT NULL,
  132. PRIMARY KEY (`c_id`),
  133. KEY `t_id` (`t_id`)
  134. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  135. -- ----------------------------
  136. -- Records of course
  137. -- ----------------------------
  138. INSERT INTO `course` VALUES ('1', 'python', '1');
  139. INSERT INTO `course` VALUES ('2', 'java', '2');
  140. INSERT INTO `course` VALUES ('3', 'linux', '3');
  141. INSERT INTO `course` VALUES ('4', 'web', '2');
  142. -- ----------------------------
  143. -- Table structure for score
  144. -- ----------------------------
  145. DROP TABLE IF EXISTS `score`;
  146. CREATE TABLE `score` (
  147. `id` int(11) NOT NULL AUTO_INCREMENT,
  148. `s_id` int(10) DEFAULT NULL,
  149. `c_id` int(11) DEFAULT NULL,
  150. `num` double DEFAULT NULL,
  151. PRIMARY KEY (`id`)
  152. ) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8;
  153. -- ----------------------------
  154. -- Records of score
  155. -- ----------------------------
  156. INSERT INTO `score` VALUES ('1', '1', '1', '79');
  157. INSERT INTO `score` VALUES ('2', '1', '2', '78');
  158. INSERT INTO `score` VALUES ('3', '1', '3', '35');
  159. INSERT INTO `score` VALUES ('4', '2', '2', '32');
  160. INSERT INTO `score` VALUES ('5', '3', '1', '66');
  161. INSERT INTO `score` VALUES ('6', '4', '2', '77');
  162. INSERT INTO `score` VALUES ('7', '4', '1', '68');
  163. INSERT INTO `score` VALUES ('8', '5', '1', '66');
  164. INSERT INTO `score` VALUES ('9', '2', '1', '69');
  165. INSERT INTO `score` VALUES ('10', '4', '4', '75');
  166. INSERT INTO `score` VALUES ('11', '5', '4', '66.7');
  167. -- ----------------------------
  168. -- Table structure for student
  169. -- ----------------------------
  170. DROP TABLE IF EXISTS `student`;
  171. CREATE TABLE `student` (
  172. `s_id` varchar(20) NOT NULL,
  173. `s_name` varchar(255) DEFAULT NULL,
  174. `s_age` int(10) DEFAULT NULL,
  175. `s_sex` char(1) DEFAULT NULL,
  176. PRIMARY KEY (`s_id`)
  177. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  178. -- ----------------------------
  179. -- Records of student
  180. -- ----------------------------
  181. INSERT INTO `student` VALUES ('1', '鲁班', '12', '男');
  182. INSERT INTO `student` VALUES ('2', '貂蝉', '20', '女');
  183. INSERT INTO `student` VALUES ('3', '刘备', '35', '男');
  184. INSERT INTO `student` VALUES ('4', '关羽', '34', '男');
  185. INSERT INTO `student` VALUES ('5', '张飞', '33', '女');
  186. -- ----------------------------
  187. -- Table structure for teacher
  188. -- ----------------------------
  189. DROP TABLE IF EXISTS `teacher`;
  190. CREATE TABLE `teacher` (
  191. `t_id` int(10) NOT NULL,
  192. `t_name` varchar(50) DEFAULT NULL,
  193. PRIMARY KEY (`t_id`)
  194. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  195. -- ----------------------------
  196. -- Records of teacher
  197. -- ----------------------------
  198. INSERT INTO `teacher` VALUES ('1', '大王');
  199. INSERT INTO `teacher` VALUES ('2', 'alex');
  200. INSERT INTO `teacher` VALUES ('3', 'egon');
  201. INSERT INTO `teacher` VALUES ('4', 'peiqi');
  202. #7.2、统计各科各分数段人数.显示格式:课程ID,课程名称,[100-85],[85-70],[70-60],[ <60]
  203. select score.c_id,
  204. course.c_name,
  205. sum(CASE WHEN num BETWEEN 85 and 100 THEN 1 ELSE 0 END) as '[100-85]',
  206. sum(CASE WHEN num BETWEEN 70 and 85 THEN 1 ELSE 0 END) as '[85-70]',
  207. sum(CASE WHEN num BETWEEN 60 and 70 THEN 1 ELSE 0 END) as '[70-60]',
  208. sum(CASE WHEN num < 60 THEN 1 ELSE 0 END) as '[ <60]'
  209. from score,course where score.c_id=course.c_id GROUP BY score.c_id;