mysql_1

MySql初级查漏补缺

1,支持的json类型

​ 从mysql5.7开始支持,到8.x版本时JSON类型提供了可以进行自动验证的JSON文档和优化的存储结构,使用更方便。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

CREATE DATABASE IF NOT EXISTS test01_office CHARACTER SET 'utf8';

USE test01_office

CREATE TABLE test_json(
js json
);

-- json 类型 '{"KEY1":"value1","KEY2":"value2" }'
INSERT INTO test_json (js)
VALUES (
'{"KEY":"value","name":"songhk", "age":18, "address":{"province":"beijing",
"city":"beijing"}}'
)


SELECT * FROM test_json;

2,使用时间类型选择

​ 在多数开发过程中,都是选择 DATETIME 类型,包括了完整的日期和时间信息,取值范围也最大,使用起来比较方便。 但是如果是记录注册时间或商品发布时间等。使用_TIMESTAMP(时间戳)更方便计算。

3,主键自增一个小知识 —自增变量的持久化

​ 在MySQL 8.0之前,自增主键AUTO_INCREMENT的值如果大于max(primary key)+1,在MySQL重启后,会重置AUTO_INCREMENT=max(primary key)+1,这种现象在某些情况下会导致业务主键冲突或者其他难以发现的问题。 下面通过案例来对比不同的版本中自增变量是否持久化。

img

​ 从结果可以看出,虽然删除了id为4的记录,但是再次插入空值时,并没有重用被删除的4,而是分配了5。 删除id为5的记录,结果如下:

img

​ 从结果可以看出,新插入的0值分配的是4,按照重启前的操作逻辑,此处应该分配6。出现上述结果的主要原因是自增主键没有持久化。 在MySQL 5.7系统中,对于自增主键的分配规则,是由InnoDB数据字典内部一个 计数器 来决定的,而该计数器只在 内存中维护 ,并不会持久化到磁盘中。当数据库重启时,该计数器会被初始化。

​ 在MySQL 8.0版本中,上述测试步骤最后一步的结果如下:

img

从结果可以看出,自增变量已经持久化了。

​ MySQL 8.0将自增主键的计数器持久化到 重做日志 中。每次计数器发生改变,都会将其写入重做日志中。如果数据库重启,InnoDB会根据重做日志中的信息来初始化计数器的内存值。

4,存储过程

创建存储过程格式

1
2
3
4
5
CREATE PROCEDURE 存储过程名(IN|OUT|INOUT 参数名 参数类型,...)
[characteristics ...]
BEGIN
存储过程体
END

​ 说明:INOUT :当前参数既可以为输入参数,也可以为输出参数。

​ 类似于Java中的方法:

1
2
3
4
修饰符 返回类型 方法名(参数类型 参数名,...){
方法体;
}

​ 例子:

1
2
3
4
5
6
7
8
9
DELIMITER //
CREATE PROCEDURE CountProc(IN sid INT,OUT num INT)
BEGIN
SELECT COUNT(*) INTO num FROM fruits
WHERE s_id = sid;
END //
DELIMITER ;


调用存储过程及查看返回结果

1
2
3
4
5
6
mysql> CALL CountProc (101, @num);
Query OK, 1 row affected (0.00 sec)

-- 查看返回结果
mysql> SELECT @num;

5,变量、流程控制与游标的重要知识

变量

​ 系统变量分为全局系统变量(需要添加 global 关键字)以及会话系统变量(需要添加 session 关键字),有时也把全局系统变量简称为全局变量,有时也把会话系统变量称为local变量。如果不写,默认会话级别。静态变量(在 MySQL 服务实例运行期间它们的值不能使用 set 动态修改)属于特殊的全局系统变量。

1
2
3
4
5
6
7
#查看所有全局变量
SHOW GLOBAL VARIABLES;
#查看所有会话变量
SHOW SESSION VARIABLES;

SHOW VARIABLES;

​ 用户变量是用户自己定义的,作为 MySQL 编码规范,MySQL 中的用户变量以 一个“@” 开头。根据作用范围不同,又分为 会话用户变量 和 局部变量 。

​ 会话用户变量:作用域和会话变量一样,只对 当前连接 会话有效。
​ 局部变量:只在 BEGIN 和 END 语句块中有效。局部变量只能在 存储过程和函数 中使用。

1
2
3
4
5
6
7
#方式1:“=”或“:=”
SET @用户变量 = 值;
SET @用户变量 := 值;
#方式2:“:=” 或 INTO关键字
SELECT @用户变量 := 表达式 [FROM 等子句];
SELECT 表达式 INTO @用户变量 [FROM 等子句];

查看用户变量的值 (查看、比较、运算等)

1
2
3
4
5
6
7
SELECT @用户变量
SELECT @a;
SELECT @num := COUNT(*) FROM employees;
SELECT @num;
SELECT AVG(salary) INTO @avgsalary FROM employees;
SELECT @avgsalary;
SELECT @big; #查看某个未声明的变量时,将得到NULL值

局部变量

1
2
3
DECLARE 变量名 类型 [default 值]; # 如果没有DEFAULT子句,初始值为NULL
DECLARE myparam INT DEFAULT 100;

6,流程控制

分支

1
2
3
4
5
IF 表达式1 THEN 操作1
[ELSEIF 表达式2 THEN 操作2]……
[ELSE 操作N]
END IF

例子:声明存储过程“update_salary_by_eid1”,定义IN参数emp_id,输入员工编号。判断该员工薪资如果低于8000元并且入职时间超过5年,就涨薪500元;否则就不变。

1
2
3
4
5
6
7
8
9
10
11
DELIMITER //
CREATE PROCEDURE update_salary_by_eid1(IN emp_id INT)
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 ;

分支结构之 CASE

1
2
3
4
5
6
7
#情况一:类似于switch
CASE 表达式
WHEN 值1 THEN 结果1或语句1(如果是语句,需要加分号)
WHEN 值2 THEN 结果2或语句2(如果是语句,需要加分号)
...
ELSE 结果n或语句n(如果是语句,需要加分号)
END [case](如果是放在begin end中需要加上case,如果放在select后面不需要)

例3:声明存储过程“update_salary_by_eid4”,定义IN参数emp_id,输入员工编号。判断该员工薪资如果低于9000元,就更新薪资为9000元;薪资大于等于9000元且低于10000的,但是奖金比例为NULL的,就更新奖金比例为0.01;其他的涨薪100元。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
DELIMITER //
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 ;

循环结构之LOOP

使用LOOP语句进行循环操作,id值小于10时将重复执行循环过程。

1
2
3
4
5
6
7
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;

循环结构之WHILE和REPEAT

WHILE语句创建一个带条件判断的循环过程。WHILE在执行语句执行时,先对指定的表达式进行判断,如果为真,就执行循环内的语句,否则退出循环。WHILE语句的基本格式如下:

例子:市场环境不好时,公司为了渡过难关,决定暂时降低大家的薪资。声明存储过程“update_salary_while()”,声明OUT参数num,输出循环次数。存储过程中实现循环给大家降薪,薪资降为原来的90%。直到全公司的平均薪资达到5000结束。并统计循环次数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 ;

REPEAT语句创建一个带条件判断的循环过程。与WHILE循环不同的是,REPEAT 循环首先会执行一次循环,然后在 UNTIL 中进行表达式的判断,如果满足条件就退出,即 END REPEAT;如果条件不满足,则会就继续执行循环,直到满足退出条件为止。

1
2
3
4
5
6
7
8
9
10
11
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 ;

跳转语句之LEAVE语句 (类似于 java中的break),跳转语句之ITERATE语句(类似于 java中的continue) 具体解释省略

7,游标

​ 定义:在 SQL 中,游标是一种临时的数据库对象,可以指向存储在数据库表中的数据行指针。这里游标 充当了指针的作用 ,我们可以通过操作游标来对数据行进行操作。

游标的使用过程:

第一步,声明游标();

1
DECLARE cur_emp CURSOR FOR

第二步,打开游标(OPEN cursor_name);

1
OPEN cursor_name

第三步使用游标(从游标中取得数据)();

1
FETCH cursor_name INTO var_name [, var_name] ...

第四步,关闭游标

1
CLOSE cursor_name

​ 例子:创建存储过程“get_count_by_limit_total_salary()”,声明IN参limit_total_salary,DOUBLE类型;声明OUT参数total_count,INT类型。函数的功能可以实现累加薪资最高的几个员工的薪资值,直到薪资总和达到limit_total_salary参数的值,返回累加的人数给total_count。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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 ;

8,触发器

​ 触发器是由 事件来触发 某个操作,这些事件包括 INSERT 、 UPDATE 、 DELETE 事件。所谓事件就是指用户的动作或者触发某项行为。如果定义了触发程序,当数据库执行这些语句时候,就相当于事件发生了,就会 自动 激发触发器执行相应的操作。当对数据表中的数据执行插入、更新和删除操作,需要自动执行一些数据库逻辑时,可以使用触发器来实现。

语法结构

1
2
3
4
5
CREATE TRIGGER 触发器名称
{BEFORE|AFTER} {INSERT|UPDATE|DELETE} ON 表名
FOR EACH ROW
触发器执行的语句块;

例子:创建触发器:创建名称为before_insert的触发器,向test_trigger数据表插入数据之前,向test_trigger_log数据表中插入before_insert的日志信息。

1
2
3
4
5
6
7
8
9
10
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 ;

查看、删除触发器

1
2
3
4
5
SHOW TRIGGERS\G
SHOW CREATE TRIGGER 触发器名
SELECT * FROM information_schema.TRIGGERS;
DROP TRIGGER IF EXISTS 触发器名称;