M ySql基础3

2023-11-10

一、MySql存储过程和函数

概念

事先经过编译并存储在数据库中的一段 SQL 语句的集合

好处

  • 存储过程和函数可以重复使用,减轻开发人员的工作量。类似于java中方法可以多次调用
  • 减少网络流量,存储过程和函数位于服务器上,调用的时候只需要传递名称和参数即可
  • 减少数据在数据库和应用服务器之间的传输,可以提高数据处理的效率
  • 将一些业务逻辑在数据库层面来实现,可以减少代码层面的业务处理

存储过程和函数的区别

  • 函数必须有返回值
  • 存储过程没有返回值

创建存储过程

创建存储过程语法

-- 修改分隔符为$
DELIMITER $

-- 标准语法
CREATE PROCEDURE 存储过程名称(参数...)
BEGIN
	sql语句;
END$

-- 修改分隔符为分号
DELIMITER ;

创建存储过程

-- 修改分隔符为$
DELIMITER $

-- 创建存储过程,封装分组查询学生总成绩的sql语句
CREATE PROCEDURE stu_group()
BEGIN
	SELECT gender,SUM(score) getSum FROM student GROUP BY gender ORDER BY getSum ASC;
END$

-- 修改分隔符为分号
DELIMITER ;

调用存储过程

-- 标准语法
CALL 存储过程名称(实际参数);

-- 调用stu_group存储过程
CALL stu_group();

查看存储过程

-- 查询数据库中所有的存储过程 标准语法
SELECT * FROM mysql.proc WHERE db='数据库名称';

删除存储过程

-- 标准语法
DROP PROCEDURE [IF EXISTS] 存储过程名称;

-- 删除stu_group存储过程
DROP PROCEDURE stu_group;

存储过程语法

定义变量
-- 标准语法
DECLARE 变量名 数据类型 [DEFAULT 默认值];
-- 注意: DECLARE定义的是局部变量,只能用在BEGIN END范围之内
	DECLARE num INT DEFAULT 10;   -- 定义变量

变量的赋值1
-- 标准语法
SET 变量名 = 变量值;
	SET NAME = '存储过程';       -- 为变量赋值

变量的赋值2
-- 标准语法
SELECT 列名 INTO 变量名 FROM 表名 [WHERE 条件];
SELECT SUM(score) INTO men FROM student WHERE gender='男';    -- 计算男同学总分数赋值给men


if语句的使用
-- 标准语法
IF 判断条件1 THEN 执行的sql语句1;
[ELSEIF 判断条件2 THEN 执行的sql语句2;]
...
[ELSE 执行的sql语句n;]
END IF;
演示:
IF total >= 380 THEN 
		SET description = '学习优秀';
	ELSEIF total >= 320 AND total < 380 THEN 
		SET description = '学习不错';
	ELSE 
		SET description = '学习一般';
	END IF;

参数的传递
-- 标准语法
CREATE PROCEDURE 存储过程名称([IN|OUT|INOUT] 参数名 数据类型)
BEGIN
	执行的sql语句;
END$
/*
	IN:代表输入参数,需要由调用者传递实际数据。默认的
	OUT:代表输出参数,该参数可以作为返回值
	INOUT:代表既可以作为输入参数,也可以作为输出参数
*/
DELIMITER ;
演示:
CREATE PROCEDURE pro_test5(IN total INT)

case语句的使用
-- 标准语法1
CASE 表达式
WHEN 值1 THEN 执行sql语句1;
[WHEN 值2 THEN 执行sql语句2;]
...
[ELSE 执行sql语句n;]
END CASE;

-- 标准语法2
CASE
WHEN 判断条件1 THEN 执行sql语句1;
[WHEN 判断条件2 THEN 执行sql语句2;]
...
[ELSE 执行sql语句n;]
END CASE;
-- 使用case判断
	CASE
	WHEN total >= 380 THEN
		SET description = '学习优秀';
	WHEN total >= 320 AND total < 380 THEN
		SET description = '学习不错';
	ELSE 
		SET description = '学习一般';
	END CASE;

while循环
-- 标准语法
初始化语句;
WHILE 条件判断语句 DO
	循环体语句;
	条件控制语句;
END WHILE;
	-- while循环
	WHILE num <= 100 DO
		-- 偶数判断
		IF num%2=0 THEN
			SET result = result + num; -- 累加
		END IF;
		
		-- 让num+1
		SET num = num + 1;         
	END WHILE;

repeat循环
-- 标准语法
初始化语句;
REPEAT
	循环体语句;
	条件控制语句;
	UNTIL 条件判断语句
END REPEAT;

-- 注意:repeat循环是条件满足则停止。while循环是条件满足则执行
-- repeat循环
	REPEAT
		-- 累加
		SET result = result + num;
		-- 让num+1
		SET num = num + 1;
		
		-- 停止循环
		UNTIL num>10
	END REPEAT;

loop循环
-- 标准语法
初始化语句;
[循环名称:] LOOP
	条件判断语句
		[LEAVE 循环名称;]
	循环体语句;
	条件控制语句;
END LOOP 循环名称;

-- 注意:loop可以实现简单的循环,但是退出循环需要使用其他的语句来定义。我们可以使用leave语句完成!
--      如果不加退出循环的语句,那么就变成了死循环。
	-- loop循环
	l:LOOP
		-- 条件成立,停止循环
		IF num > 10 THEN
			LEAVE l;
		END IF;
	
		-- 累加
		SET result = result + num;
		-- 让num+1
		SET num = num + 1;
	END LOOP l;

游标

  • 游标的概念

    • 游标可以遍历返回的多行结果,每次拿到一整行数据
    • 在存储过程和函数中可以使用游标对结果集进行循环的处理
    • 简单来说游标就类似于集合的迭代器遍历
    • MySQL中的游标只能用在存储过程和函数中

二、MySql触发器

触发器的概念

可以在 insert/update/delete 之前或之后,触发并执行触发器中定义的SQL语句
可以协助应用在数据库端确保数据的完整性 、日志记录 、数据校验等操作

触发器类型 OLD的含义 NEW的含义
INSERT 型触发器 无 (因为插入前状态无数据) NEW 表示将要或者已经新增的数据
UPDATE 型触发器 OLD 表示修改之前的数据 NEW 表示将要或已经修改后的数据
DELETE 型触发器 OLD 表示将要或者已经删除的数据 无 (因为删除后状态无数据)

创建触发器

标准语法

DELIMITER $

CREATE TRIGGER 触发器名称
BEFORE|AFTER INSERT|UPDATE|DELETE
ON 表名
[FOR EACH ROW]  -- 行级触发器
BEGIN
	触发器要执行的功能;
END$

DELIMITER ;
-- 创建INSERT触发器
DELIMITER $

CREATE TRIGGER account_insert
AFTER INSERT
ON account
FOR EACH ROW
BEGIN
	INSERT INTO account_log VALUES (NULL,'INSERT',NOW(),new.id,CONCAT('插入后{id=',new.id,',name=',new.name,',money=',new.money,'}'));
END$

DELIMITER ;

-- 向account表添加记录
INSERT INTO account VALUES (NULL,'王五',3000);

-- 查询account表
SELECT * FROM account;

-- 查询日志表
SELECT * FROM account_log;


-- 创建UPDATE触发器
DELIMITER $

CREATE TRIGGER account_update
AFTER UPDATE
ON account
FOR EACH ROW
BEGIN
	INSERT INTO account_log VALUES (NULL,'UPDATE',NOW(),new.id,CONCAT('修改前{id=',old.id,',name=',old.name,',money=',old.money,'}','修改后{id=',new.id,',name=',new.name,',money=',new.money,'}'));
END$

DELIMITER ;

-- 修改account表
UPDATE account SET money=3500 WHERE id=3;

-- 查询account表
SELECT * FROM account;

-- 查询日志表
SELECT * FROM account_log;



-- 创建DELETE触发器
DELIMITER $

CREATE TRIGGER account_delete
AFTER DELETE
ON account
FOR EACH ROW
BEGIN
	INSERT INTO account_log VALUES (NULL,'DELETE',NOW(),old.id,CONCAT('删除前{id=',old.id,',name=',old.name,',money=',old.money,'}'));
END$

DELIMITER ;

-- 删除account表数据
DELETE FROM account WHERE id=3;

-- 查询account表
SELECT * FROM account;

-- 查询日志表
SELECT * FROM account_log;

查看触发器

-- 标准语法
SHOW TRIGGERS;

-- 查看触发器
SHOW TRIGGERS;

删除触发器

-- 标准语法
DROP TRIGGER 触发器名称;

-- 删除DELETE触发器
DROP TRIGGER account_delete;

触发器的总结

  • 触发器是与表有关的数据库对象
  • 可以在 insert/update/delete 之前或之后,触发并执行触发器中定义的SQL语句
  • 触发器的这种特性可以协助应用在数据库端确保数据的完整性 、日志记录 、数据校验等操作
  • 使用别名 NEW 和 OLD 来引用触发器中发生变化的记录内容

三、MySQL事务

事物的概念

一条或多条 SQL 语句组成一个执行单元,其特点是这个单元要么同时成功要么同时失败,单元中的每条 SQL 语句都相互依赖,形成一个整体,如果某条 SQL 语句执行失败或者出现错误,那么整个单元就会回滚撤回到事务最初的状态,如果单元中所有的 SQL 语句都执行成功,则事务就顺利执行。

未管理事务演示

-- 张三给李四转账500元
-- 1.张三账户-500
UPDATE account SET money=money-500 WHERE NAME='张三';
-- 2.李四账户+500
出错了...
UPDATE account SET money=money+500 WHERE NAME='李四';

-- 该场景下,这两条sql语句要么同时成功,要么同时失败。就需要被事务所管理!

管理事务演示

-- 标准语法  开启事务
START TRANSACTION;

-- 标准语法  回滚事务
ROLLBACK;

-- 标准语法  提交事务
COMMIT;



-- 开启事务
START TRANSACTION;

-- 张三给李四转账500元
-- 1.张三账户-500
UPDATE account SET money=money-500 WHERE NAME='张三';
-- 2.李四账户+500
-- 出错了...
UPDATE account SET money=money+500 WHERE NAME='李四';

-- 回滚事务(出现问题)
ROLLBACK;

-- 提交事务(没出现问题)
COMMIT;

事务的提交方式

  • 提交方式

    • 自动提交(MySQL默认为自动提交)
    • 手动提交
  • 修改提交方式

    • 查看提交方式
-- 标准语法
SELECT @@AUTOCOMMIT;  -- 1代表自动提交    0代表手动提交

修改提交方式

-- 标准语法
SET @@AUTOCOMMIT=数字;

-- 修改为手动提交
SET @@AUTOCOMMIT=0;

-- 查看提交方式
SELECT @@AUTOCOMMIT;

事务的四大特征

  • 原子性
    • 事务包含的所有操作要么全部成功,要么全部失败回滚
  • 一致性
    • 事务必须使数据库从一个一致性状态变换到另一个一致性状态
  • 隔离性
    • 当多个用户并发访问数据库时,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离
  • 持久性
    • 事务一旦被提交了,那么对数据库中的数据的改变就是永久性的

事务的隔离级别

  • 隔离级别的概念

    • 多个客户端操作时 ,各个客户端的事务之间应该是隔离的,相互独立的 , 不受影响的。
    • 而如果多个事务操作同一批数据时,则需要设置不同的隔离级别 , 否则就会产生问题 。
  • 四种隔离级别

1 读未提交 read uncommitted
2 读已提交 read committed
3 可重复读 repeatable read
4 串行化 serializable
  • 可能引发的问题
问题 现象
脏读 是指在一个事务处理过程中读取了另一个未提交的事务中的数据 , 导致两次查询结果不一致
不可重复读 是指在一个事务处理过程中读取了另一个事务中修改并已提交的数据, 导致两次查询结果不一致
幻读 select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入。或不存在执行delete删除,却发现删除成功
  • 查询数据库隔离级别
-- 标准语法
SELECT @@TX_ISOLATION;

修改数据库隔离级别

-- 标准语法
SET GLOBAL TRANSACTION ISOLATION LEVEL 级别字符串;

-- 修改数据库隔离级别为read uncommitted
SET GLOBAL TRANSACTION ISOLATION LEVEL read uncommitted;

-- 查看隔离级别
SELECT @@TX_ISOLATION;   -- 修改后需要断开连接重新开

隔离级别总结

隔离级别 名称 出现脏读 出现不可重复读 出现幻读 数据库默认隔离级别
1 read uncommitted 读未提交
2 read committed 读已提交 Oracle / SQL Server
3 repeatable read 可重复读 MySQL
4 serializable 串行化

注意:隔离级别从小到大安全性越来越高,但是效率越来越低 , 所以不建议使用READ UNCOMMITTED 和 SERIALIZABLE 隔离级别.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

M ySql基础3 的相关文章

  • 无法使用 Django 应用程序从容器连接到 MySQL docker 容器

    当我尝试从运行 Django 应用程序的 docker 容器连接到运行 MySQL 的容器时 出现以下错误 django db utils OperationalError 2003 Can t connect to MySQL serve
  • 单行的总和值?

    我有一个 MySQL 查询 它返回由一系列 1 和 0 组成的单行 它用于进度条指示器 我现在在代码中对它进行求和 但我尝试对查询中的值求和 并意识到我无法使用 SUM 因为它们有很多列 但只有一行 有没有办法可以在查询中自动求和 就像这样
  • 如何以最少的查询次数获取帖子列表和关联标签

    我的表格结构如下 标签 更多的是一个类别 id 标签名称 描述 slug POSTS ID 标题 网址 邮戳 id idPost idTag USERS ID 用户名 userSlug VOTES id idPost idUser 每个帖子
  • 将记录分成两列

    我的数据库中有一个 学生 表 其中包含大约 5 000 条记录 我想将这些记录显示在two分区 如何在不执行查询两次的情况下做到这一点 仅使用单个查询 显示示例http www freeimagehosting net uploads f1
  • 当“修复表”查询在 mysql 中不起作用时该怎么办?

    我收到此错误 表的存储引擎不支持修复 当我尝试使用查询修复表时repair table tbl college master 表是 innodb 类型 但我不知道我收到此错误 See 手册 http dev mysql com doc re
  • 使 pdo::query 静态

    当我运行下面的代码时出现此错误 我通常使用 msql 函数 但我尝试使用 PDO 代替 怎么了 致命错误 第 14 行无法静态调用非静态方法 PDO query
  • 为什么 MySQL 创建带有 _seq 后缀的表?

    我创建了一个 InnoDB 表 名为foo在 MySQL 中 一旦我对表执行插入操作 我就会看到另一个表foo seq被建造 如果我删除自动生成的表 它会在下一次插入后出现 是什么原因造成的 听起来像是正在创建一个序列 您是否有自动生成的主
  • 为什么这会返回资源 id #2? [复制]

    这个问题在这里已经有答案了 可能的重复 我如何从 PHP 中的 MySql 响应中 回显 资源 id 6 https stackoverflow com questions 4290108 how do i echo a resource
  • Mysql 检索所有有限制的行

    我想检索特定用户的所有行 限制为 0 x 所以我只是想问是否有任何方法可以检索 mysql 中的所有行 而不调用返回 x 的 count id 的方法 而不重载现有函数 该函数在查询中根本没有限制 与我们的 string Relace 功能
  • 如何在Sequelize中设置查询超时?

    我想看看如何在 Sequelize 中设置查询的超时时间 我查看了 Sequelize 文档以获取一些信息 但我找不到我要找的东西 我发现的最接近的是 pools acquire 选项 但我不想设置传入连接的超时 而是设置正在进行的查询的超
  • Mysql 中 UNION 子句的替代方案

    我有两张桌子 表 a 表 b table a ID 1 2 3 4 5 7 table b ID 2 3 4 5 6 我必须得到这样的输出而无需UNION命令 ID 1 2 3 4 5 6 7 注意 我有一个联合解决方案 select fr
  • MySQL MIN/MAX 所有行

    我有桌子Races与行ID Name and TotalCP 我选择分钟 TotalCP FROM Races 但是我想选择具有最小值的整行 我如何在单个查询中做到这一点 从聚合值获取整行的一般形式是 SELECT FROM Races W
  • MySQL Connector C/C API - 使用特殊字符进行查询

    我是一个 C 程序 我有一个接受域名参数的函数 void db domains query char name 使用 mysql query 我测试数据库中是否存在域名 如果不是这种情况 我插入新域名 char query 400 spri
  • 如何使用 vitess 仅对特定表进行分片

    我创建了一个包含三个表的未分片键空间 现在我想对前两个表的键空间进行分片 但不想对第三个表进行分片 如何才能做到这一点 Vitess 文档不包含任何与此相关的信息或示例 请帮忙 Thanks vitess 中的垂直分片与水平分片类似 您应该
  • 在服务器上找不到本地主机或 phpMyAdmin:如何修复?

    我按照安装说明进行操作PHP MySQL and PHPMyAdmin 但是当我尝试访问时http localhost phpmyadmin 我收到此错误 未找到 在此找不到请求的 URL phpmyadmin 服务器 然后我尝试访问loc
  • 比特纳米。重置mysql根密码

    我如何重置 MySQL 中的 root 密码和帐户 因为我按照如何为其他服务器授予权限的说明操作 并且意外地将 root 用户 Mysql 绑定到其他 IP 地址 现在看来我无法在 localhost 上以管理员身份登录 Thanks 您有
  • 如何在 Play java 中创建数据库线程池并使用该池进行数据库查询

    我目前正在使用 play java 并使用默认线程池进行数据库查询 但了解使用数据库线程池进行数据库查询可以使我的系统更加高效 目前我的代码是 import play libs Akka import scala concurrent Ex
  • 在 PHP 字符串中格式化 MySQL 代码

    是否有任何程序 IDE 可以在 PHP 字符串中格式化 MySQL 代码 例如 我使用 PHPStorm IDE 但它无法做到这一点 它对 PHP 和 MYSQL 执行此操作 但不适用于 php 字符串内的 MYSQL 我已准备好使用新的
  • SQL不允许表中有重复记录

    如何使其不添加重复项 我想让它通过 ID 之外的所有其他列进行检查 我希望这个无效 ID col1 col2 col3 1 first middle last ID col1 col2 col3 2 first middle last 我希
  • 如何修改现有表以添加时区

    我有一个包含 500 多个表的大型应用程序 我必须将应用程序转换为时区感知 当前应用程序使用new java util Date GETDATE 与服务器的时区 即没有任何时区支持 我已将这项任务分为几个步骤 以便于开发 我确定的第一个步骤

随机推荐

  • DVWA 之 XSS(DOM)

    目录 1 级别 Low 2 级别 Medium 3 级别 High 4 级别 Impossible XSS 简介 XSS Cross Site Script 全称跨站脚本攻击 为了与 CSS Cascading Style Sheet 有所
  • 开发一个具有分页功能的web表格组件

    效果图 代码 index html table thead tr th ID th tr thead table
  • Web经典BS快速开发框架,强大后台+简洁UI一体化开发工具

    本框架旨在为 NET开发人员提供一个Web后台快速开发框架 采用本框架 能够极大的提高项目开发效率 整个框架包括三个版本 net net core java 开发中 以上三个版本中 NET为初始版本 开发时间最长 是目前老客户使用的主要产品
  • Hudi数据湖-基于Flink、Spark湖仓一体、实时入湖保姆级教学

    目录 Hudi源码编译 Hudi扫盲 基于Spark shell集成Hudi 基于Spark Hive集成Hudi手动创建HIVE表 基于SparkSQL集成Hudi自动创建HIVE表 基于FlinkSQL集成Hudi 基于FlinkSQL
  • 八大排序比较(时间复杂度,空间复杂度,稳定性的比较)

    排序算法的稳定性 含义 假定在待排序的记录序列中 存在多个具有相同的关键字的记录 若经过排序 这些记录的相对次序保持不变 即在原序列中 r i r j 且r i 在r j 之前 而在排序后的序列中 r i 仍在r j 之前 则称这种排序算法
  • python 使用前馈神经网络处理IrIs数据集(BP)

    本文章包含以下内容 数据 lris数据集 模型 前馈神经网络 激活函数 Logistic 损失函数 交叉嫡损失 优化器 梯度下降法 评价指标 准确率 输出层使用了Softmax分类 通过使用前馈神经网络实现BP学习算法 进一步理解前馈神经网
  • Java基础 --- 注解 Annotation

    Java基础 注解 Annotation Java注解 Java自带的标准注解 自定义注解 Java注解 Java注解它提供了一种安全的类似注释的机制 用来将任何的信息或元数据 metadata 与程序元素 类 方法 成员变量等 进行关联
  • Pytest-UnitTest

    2023暑期学习 Pytest Pytest pytest是python的一种单元测试框架 与python自带的unittest测试框架类似 但是比unittest框架使用起来更简洁 效率更高 pip install pytest pyte
  • JavaScript的三大组成部分(收藏)

    JavaScript是一种属于网络的脚本语言 已经被广泛用于Web应用开发 常用来为网页添加各式各样的动态功能 为用户提供更流畅美观的浏览效果 通常JavaScript脚本是通过嵌入在HTML中来实现自身的功能的 各位小伙伴在进阶的时候总会
  • 放个手机在单位自动打卡_几步神奇操作让你的钉钉自动打卡

    这款叫做 FreeDing 的 Android 应用 是一个钉钉自动打卡工具 只需要把手机放在办公室 就能实现每天自动定时打卡了 Appinn 这是一个程序员一不爽就造轮子的故事 diy0504 同学所在公司打卡要求越来越严格 忘记打卡不准
  • 实用mysql命令

    1 显示表中所有列的详细信息 show full columns table name 2 查看服务器版本 show version 3 查看当前登录用户 select current user 4 显示表的详细信息 show table
  • 聊聊 Docker 和 Dockerfile

    目录 一 前言 二 了解Dockerfile 三 Dockerfile 指令 四 多阶段构建 五 Dockerfile 高级用法 六 小结 一 前言 对于开发人员来说 会Docker而不知道Dockerfile等于不会Docker 上一篇文
  • This beta version of Typora is expired, please download and install a newer version.

    一 问题 打开typora软件提示如下图 翻译过来就是 此测试版Typora已过期 请下载并安装新版本 二 解决办法 1 win R调用运行窗口 输入regeedit 打开注册表 2 在注册表数据输入 计算机 HKEY CURRENT US
  • javaScript基础面试题 ---call、apply、bind三者的异同 - 改变this的方法,apply和call最初设计的时候为什么要设计这两个,为什么apply参数是数组call不是

    call apply bind三者的异同 call方法 apply方法 bind方法 call apply bind三者的异同 改变this的方法 apply和call最初设计的时候为什么要设计这两个 为什么apply参数是数组call不是
  • 如何使用JLINK在ADS1.2环境下调试硬件?

    注 虽然文章是对LPC2148而写的 但是对三星的44B0芯片同样适用 只需要在选择时将相应的CPU选择的S3C44B0就可以了 JLINK在ADS下调试心得 前两天一个客户用jlink 在ADS来调试LPC2148总报错 这个错误我之前在
  • 对象有多个字段新增日志执行的方法却只有几个?

    我以上代码 AssetCard对象有多个字段 最后执行新增却只有几个字段 我之前开发的项目都是公司自己开发的框架 是解决了字段可以包含下划线的 然后这次客户需要我写个插件 我就自己搭建了一个项目 让后发现字段包含下划线底层sql就会缺少字段
  • 根据地理位置多语言切换(3)-多语言切换

    在手机应用的实现中经常会遇到需要语言切换用于满足用户环境的多样性 可以根据所处地理位置信息进行经纬度及国家 城市 地区的获取 可以根据此内容进行多语言情况的推荐及切换 完成上述的想法需要进行几个功能的开发 需要通过手机进行地理位置信息获取
  • 【无监督】6、SimSiam

    文章目录 一 背景 二 方法 三 效果 论文 Exploring Simple Siamese Representation Learning 出处 FAIR 何恺明大佬 本文作者抛出了两个爆炸 性结论 结论一 基于孪生网络的对比的学习的成
  • 外盘国际期货招商

    100万在各省市能生活多久 按照去年人均消费支出计算 上海100万能花不到22年 西藏能花近63年 2021年我国居民人均预期寿命 78 2岁 2022年我国各省市人均消费支出 365日 西藏43 5 日 62年11个月 2022年我国各省
  • M ySql基础3

    一 MySql存储过程和函数 概念 好处 存储过程和函数的区别 创建存储过程 调用存储过程 查看存储过程 删除存储过程 存储过程语法 二 MySql触发器 创建触发器 查看触发器 删除触发器 触发器的总结 三 MySQL事务 事物的概念 未