Skip to content

MySQL

安装

Arch Linux 二进制安装

下载 MySQL 对应系统的二进制包

创建 mysql 用户

bash
# -M 不创建用户登录目录
useradd -M -s /sbin/nologin mysql

解压

bash
xz -d mysql-8.3.0-linux-glibc2.28-x86_64.tar.xz
tar -xvf mysql-8.3.0-linux-glibc2.28-x86_64.tar

移动到 /usr/local/ 目录下

bash
mv mysql-8.3.0-linux-glibc2.28-x86_64 /usr/local/mysql

配置环境变量

shell
export PATH="/usr/local/mysql/bin:$PATH"

初始化

bash
# 会输出默认密码
./mysqld --initialize --user=mysql --basedir=/usr/local/mysql --datadir=/usr/local/mysql/data

创建配置文件

sudo vim /etc/my.cnf

ini
[mysqld]
user=mysql
basedir=/usr/local/mysql
datadir=/usr/local/mysql/data
socket=/usr/local/mysql/data/mysql.sock
log-error=/var/log/mysql/mysqld.log
pid-file=/usr/local/mysql/data/mysqld.pid
#pid-file=/run/mysqld/mysqld.pid

[client]
socket=/usr/local/mysql/data/mysql.sock

配置启动服务

bash
# 创建服务
cp /usr/local/mysql/support-files/mysql.server /etc/init.d/mysqld
# 设置开机启动
systemctl enable mysqld
# 启动服务
systemctl start mysqld

配置

修改用户密码

sql
alter user 'root'@localhost identified by '123456';
alter user 'root' identified by '123456'; # 默认的主机地址为 %

允许远程访问

sql
use mysql;
update user set host = '%' where user = 'root';
flush privileges;

修改为 % 后无效

需要检查 my.cnf 文件中是否配置了 bind-address127.0.0.1,需要修改为 0.0.0.0

修改默认的字符编码

修改 my.ini 或者 my.cnf 配置文件:

ini
character-set-server=utf8
collation-server=utf8_unicode_ci

登录数据库

执行 mysql 客户端命令

常用参数:

  • -u: 登录的用户名
  • -p: 登陆使用的密码,不显示指定则在回车后显示
  • -h: 主机名,不指定默认为 localhost
  • -P: 端口号,不指定默认为 3306

示例:

mysql -uroot -p test

使用 root 用户登录 test 数据库,会使用密码

SQL

数据库管理

显示所有数据库

sql
show databases;

切换到指定数据库

sql
use mysql; # 切换到 mysql 数据库

创建数据库

sql
create database db_test; # 创建 db_test 数据库
create database db_tset default character set utf8mb4; # 创建一个是用 utf8mb4 字符集的数据库

查看当前数据库的默认编码

SHOW VARIABLES LIKE 'character_%'; 中查看 character_set_database 的值。

查看创建数据库使用的编码

sql
show create database db_test;

查看数据库使用的编码

sql
status;

删除数据库

sql
drop database db_test;

表管理

查看所有表

sql
show tables;

显示表结构

sql
use mysql;
desc user; # 显示 user 表的结构信息
describe user; # 显示 user 表的结构信息
FieldTypeNullKeyDefaultExtra
字段名字段类型是否允许为 null字段的约束默认值扩展属性

字段类型

整型:

  • TINYINT: 8 位整数类型,支持的数据范围为:[27,271]
  • INTEGER: 32 位整数类型,支持的数据范围为:[231,2311]
  • BIGINT: 64 位整数类型,支持的数据范围为:[263,2631]

浮点型:

  • FLOAT: 32 位单精度浮点型
  • DOUBLE: 64 位双精度浮点型
  • DECIMAL(M,D): 可变的小数值,通过 M 指定整个字段支持的位数,通过 D 指定支持的小数位数,如 `DECIMAL(6, 4) 表示 2 位整数位 4 位小数位。
    • 使用 UNSINGED 使得不支持负数
    • 使用 ZOREFILL 使用 0 填充整数位,默认小数位会自动使用 0 填充

时间:

  • TIMESTAMP: 时间戳 指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数,是一个 INT 值,最大支持到 2038 年。百度百科
  • DATETIME: 存储日期和时间(8 bytes),格式 YYYY-MM-DD hh:mm:ss
  • DATE: 存储日期(3bytes),格式:YYYY-MM-DD
  • TIME: 存储时间(3bytes),格式:hh:mm:ss

字符串:

  • CHAR: 定长字符串,支持 [0,281]0 ~ 255 个字节。
  • VARCHAR: 变长字符串,支持 [0,2161]0 ~ 65535 个字节。
  • TEXT: 长文本数据,支持 [0,2161]0 ~ 65535 个字节。
  • BLOB: 二进制形式的长文本数据,支持 [0,2161]0 ~ 65535 个字节。

枚举和集合:

  • ENUM: 支持存储单个预定义的值
  • SET: 支持存储多个预定义的值

约束

MySQL 的约束有 主键约束外键约束非 null 约束唯一约束check 约束以及自增约束

一般来说自增约束其实约束什么,就是一个字段会自增,但是不会约束插入的值

check 约束是在 MySQL 8 中添加

主键约束

约束条件:

  • 主键值需要唯一
  • 一个表只能存在一个主键
  • 主键字段不能为 null

创建主键约束:

  1. 在创建表是为字段添加 primary key 描述

    通过这种方式创建主键约束不能指定多个字段

    sql
    create table tb_test_pk(
        id int primary key,
        name varchar(11)
    );
  2. 通过使用 [constraint 约束名] primary key 为单个或者多个字段添加主键约束

    创建联合主键时,则表示联合的字段不能一起重复,如下面的表示 id 和 name 不能同时存在

    sql
    create table tb_test_mpk(
        id int,
        name varchar(11),
        constraint constraint_name primary key(id, name)
    );

不推荐操作

对已有表最佳主键:

alter table 表名 add primary key(字段列表);

alter table 表名 字段名 字段数据类型 primary key [其它约束];

删除主键约束:

alter table 表名 drop primary key;

删除主键前需要清除自增约束

自增约束

关键字 auto_increment

  • 使用自增约束的字段必须是主键字段
  • 只能存在一个自增约束字段
  • MySQL 通过 information_schema 数据库的 TABLES 表中的 AUTO_INCREMENT 字段管理

创建自增约束:

只能在创建表是使用 auto_increment 关键字创建

查看表的下一个自增值:

  • show table status like '表名';
  • sql
    SELECT `AUTO_INCREMENT`
      FROM `information_schema`.`TABLES`
      WHERE `TABLE_SCHEMA` = '数据库名'
      AND `TABLE_NAME` = '表名';

设置自增值:

  • 将下一个自增值设置为指定值:alter table 表名 auto_incurment = 值;
  • 重置自增值:truncate table 表名

    会将自增值设置为 1,并清空表数据

非 null 约束

关键字:not null

  • 插入更新 时不允许插入 null 值
  • 为一个表添加 not null 约束时,原 null 值会转为类型默认值

查看相关操作:

唯一约束

关键字:unique

  • 唯一约束值需要唯一
  • 一个表可以存在 多个 唯一约束
  • 唯一字段可以为 null

TIP

当一个表中不存在主键,但是存在非 null 的唯一索引,则 MySQL 会将其当作注意。

创建唯一约束:

  • 在创建表是为字段添加 unique 描述
    sql
    create table tb_test_unique(
        id int primary key,
        phone varchar(11) unique
    );
  • 通过使用 [constraint 约束名] unique key(字段列表) 为单个或者多个字段添加唯一约束

    指定多个字段表示创建联合约束,不是表示每个字段唯一,而是组合在一起唯一

    sql
    create table tb_test_multi_unique(
        id int primary key,
        uid int,
        phone varchar(11),
        constraint unique key (uid, phone)
    );

添加唯一索引:

  • 为单个字段添加:alter table 表名 modify 字段名 字段类型 unique [其它约束];
  • 为单个或者多个字段:alter table tb_test_unique add unique key(字段列表);

删除唯一约束:

  1. 查询索引名称(Key_name 列)
    sql
    show index from 表名;
  2. 删除索引
    sql
    alter table 表名 drop index 索引名;
默认值约束

关键字:default

  • 插入数据时,如果没有指定字段值,则使用默认值填充数据

创建默认值约束:

在创建表时指定 default 默认值

添加字段时指定:

alter table 表名 add 字段名 字段类型 default 默认值 其它约束;

修改时指定:

alter table 表名 modify 字段名 字段类型 default 默认值 其它约束

外键约束

外键约束又成参照完整性

阿里开发规范(【强制】 不得使用外键与级联,一切外键概念必须在应用层解决)

(概念解释)学生表中的 student_id 是主键,那么成绩表中的 student_id 则为外键。 如果更新学生表中的 student_id,同时触发成绩表中的 student_id 更新,即为级联更新。

  • 外键与级联更新适用于单机低并发,不适合分布式、高并发集群;
  • 级联更新是强阻塞,存在数据库更新风暴的风险;
  • 外键影响数据库的插入速度。

《Java 开发手册》第 41 页

定义外键约束

sql
-- 在表级上使用
FOREIGN KEY (<属性>) REFERENCES <表名>(<属性>)

-- 可设置更新和删除操作
FOREIGN KEY (<属性>) REFERENCES <表名>(<属性>) ON UPDATE CASCADE ON DELETE CASCADE
-- 当更新或删除被参照表时,参照表也更新

-- 除了 CASCADE 还有 NO ACTION, SET NULL
-- NO ACTION 为默认,直接拒绝操作
  • 参照属性必须唯一(主键约束、唯一约束)
  • 具有外键约束的表不能插入被参照表不存在的数据
  • 没有设置更新删除操作的外键约束,不能直接更新删除被参照表
示例
sql
-- 定义学生表
create table tb_student
(
    id   int primary key auto_increment comment '学生 ID',
    name varchar(10) comment '学生姓名'
);
-- 定义成绩表
create table tb_score
(
    student_id int comment '成绩 ID',
    score      tinyint comment '成绩',
    foreign key (student_id) references tb_student (id)
);
-- 往学生表中插入数据
insert into tb_student(name)
values ('张三'),
       ('李四'),
       ('王五');
-- 往成绩表中插入存在的 student_id 数据
insert into tb_score(student_id, score)
values (1, 60),
       (2, 90),
       (3, 100);
-- 往成绩表中插入不存在的 student_id 数据
insert into tb_score(student_id, score)
values (4, 100);
-- 会出现如下错误
-- [1452] Cannot add or update a child row: a foreign key constraint fails (`db_test`.`tb_score`, CONSTRAINT `tb_score_ibfk_1` FOREIGN KEY (`student_id`) REFERENCES `tb_student` (`id`))

-- 直接删除学生表数据
delete from tb_student where id = 3;
-- 会出现如下错误
-- [1451] Cannot delete or update a parent row: a foreign key constraint fails (`db_test`.`tb_score`, CONSTRAINT `tb_score_ibfk_1` FOREIGN KEY (`student_id`) REFERENCES `tb_student` (`id`))

-- 先删除成绩表(参照表)的数据才能删除学生表(被参照表)的数据
delete from tb_score where student_id = 3;
delete from tb_student where id = 3;

-- 修改为级联更新和删除置 null
-- 1. 查找外键约束的名称 2. 删除外键约束 3. 添加外键约束
show create table tb_score;
alter table tb_score drop foreign key tb_score_ibfk_1;
alter table tb_score add foreign key (student_id) references tb_student(id) on update cascade on delete set null;

-- 通过上面的修改,更新参照表的数据还是需要包装被参照表的数据存在
-- 但是更新被参照表的数据则可以是的参照表的数据级联更新
select * from tb_student; -- 应该只存在 1,2 两条数据
select * from tb_score; -- 应该也是存在 1,2 两条数据
update tb_student set id = 3 where id = 2;
select * from tb_score; -- 应该只存在 1,3 两条数据

-- 上面的修改还设置了删除时的操作,允许在参数被参照表的设置参照表的值为 null
delete from tb_student where id = 1;
select * from tb_score; -- 应该也是存在 null,3 两条数据
check 约束

对插入数据进行检查

MySQL 8 才支持 check 约束

列级定义:

check (条件)

表级定义:

check (条件)

创建表

sql
create table [表名] (
  字段名 字段类型 [primary key] [auto_incrment] [not null] [default 默认值] [comment '字段说明'][,]
  ...
);
  • 使用 primary key 则必须是 not null 的,即不能显示指定 null
  • 使用 auto_increment 则必须是 primary key

删除表

sql
drop if exists table [表名];

修改表结构

sql
# 新增字段
alter table 表名 add [column] 字段名 字段类型 [其它约束] [FIRST | AFTER 在哪一列后];
# 修改字段名称和字段类型
alter table change 字段名 新字段名 字段类型 [其它约束] [FIRST | AFTER 在哪一列后];
# 修改字段类型和约束
alter table 表名 modify 字段名 字段类型 [其它约束] [FIRST | AFTER 在哪一列后];
# 删除字段
alter table drop 字段名;

CHANGE 与 MODIFY 的区别

  • CHANGE 不论是否修改字段名都需要传入新的字段名。
  • MODIFY 不能修改字段名

增删改查

示例表数据结构
sql
create table tb_test(
  id int primary key auto_increment,
  name varchar(10),
  gender enum('男', '女'),
  age int
);

插入数据

语法:

  • insert [ignore] [into] tb_test[(字段名1[,字段名2]...) value (值1[,值2]...);
  • insert [ignore] [into] tb_test[(字段名1[,字段名2]...) values (值1[,值2]...)[,(值1[,值2]...)]...];

可以不写 into 关键字(暂时没找到写于不写的区别)

ingore 表示忽略插入时的主键冲突,不插入数据,但是不报错,只显示警告

sql
insert tb_test(name, gender, age)
values ('张三', 1, 20), ('李四', '女', 24); # 1 表示定义枚举时的第一个

insert into tb_test(id, name, gender, age)
values (3, '王', 2, 50);

查询数据

单表查询语法:select [distinct] 字段列表 from 表名 [where 条件] [GROUP BY (字段列表)] [ORDER BY 列名 [ASC | DESC]] [LIMIT number [[OFFSET|,] number]];

LIMIT m, n 中 m 表示偏移量,n 表示取的行数,等价于 LIMIT n OFFSET m

distinct 表示去重,去除查询结果中重复的数据行(记录)

sql
select * from tb_test; # 查询所有数据
select name, age from tb_test; # 查询部分字段数据
where 子句

where 用于设置查询数据的过滤条件。

  • 支持比较值的大小关系:>>=<<=!=
  • 区间匹配:between value1 and value2,即 [value1, value2]
  • 支持逻辑关键字:andornot
  • 集合关系:innot in
  • 模糊查询:like,eg:字段 like '_值%'_ 匹配任意一个字符,% 匹配领个或者多个任意字符),一般推荐使用右模糊查询即 like '值%'
order 子句

order 子句用来设置数据的排序方式,通过哪些字段进行排序

  • ASC: 是升序排序
  • DESC: 是降序排序
group by 子句

对查询数据进行分组,使用 group by 的查询语句, 则 select 的字段只能存在在 group by 的字段中, 或者使用聚合函数计算分组数据的值。

子查询(又叫嵌套查询)

查询语句中的条件也是一个查询语句

子查询 的查询条件 不依赖父查询 的查询称为 不相关子查询
子查询 的查询条件 依赖父查询 的查询结果称为 相关子查询,整个查询语句称为相关嵌套查询
示例
sql
-- 定义表和插入数据
-- 建立一个学生表
CREATE TABLE Student
(
    Sno   CHAR(9) PRIMARY KEY, -- 列级完整性约束条件,Sno 是主码
    Sname CHAR(20) UNIQUE,     -- Sname 取唯一值
    Ssex  CHAR(2),
    Sage  SMALLINT,
    Sdept CHAR(20)
);
-- 建立一个课程表
CREATE TABLE Course
(
    Cno     CHAR(4) PRIMARY KEY,               -- 列级完整性约束条件,Cno 是主码
    Cname   CHAR(40) NOT NULL,                 -- 列级完整性约束条件,Cname 不能取空值
    Cpno    CHAR(4),
    Ccredit SMALLINT,
    FOREIGN KEY (Cpno) REFERENCES Course (Cno) -- 表级完整性约束条件,Cpno 是外码,被参照表是 Course,被参照列是 Cno
);
-- 建立一个学生选课表
CREATE TABLE SC
(
    Sno   CHAR(9),
    Cno   CHAR(4),
    Grade SMALLINT,
    FOREIGN KEY (Sno) REFERENCES Student (Sno), -- 表级完整性约束条件,Sno 是外码,被参照表是 Student,被参照列是 Sno
    FOREIGN KEY (Cno) REFERENCES Course (Cno)   -- 表级完整性约束条件,Cno 是外码,被参照表是 Course,被参照列是 Cno
);
INSERT INTO Student
VALUES ('201215121', '李勇', '男', 20, 'CS'),
       ('201215122', '刘晨', '女', 19, 'CS'),
       ('201215123', '王敏', '女', 18, 'MA'),
       ('201215124', '张立', '男', 19, 'IS');
INSERT INTO Course
VALUES ('2', '数学', null, 2),
       ('6', '数据处理', null, 2),
       ('7', 'PASCAL 语言', 6, 4),
       ('4', '操作系统', 6, 3),
       ('5', '数据结构', 7, 4),
       ('1', '数据库', 5, 4),
       ('3', '信息系统', 1, 4);
INSERT INTO SC
VALUES ('201215121', '1', 92),
       ('201215121', '2', 85),
       ('201215121', '3', 88),
       ('201215122', '2', 90),
       ('201215122', '3', 80);


-- === 不相关子查询 ===
-- 查询与刘晨在同一个系的学生
SELECT *
FROM Student
WHERE Sdept IN (SELECT Sdept
                FROM Student
                WHERE Sname = '刘晨');

-- 查询了选修了“信息系统”的学生序号和姓名
SELECT Sno, Sname
FROM Student
WHERE Sno IN (SELECT Sno
              FROM SC
              WHERE Cno IN (SELECT Cno
                            FROM Course
                            WHERE Cname = '信息系统'));

-- === 相关子查询 ===
-- 找出每个选项超过他自己选修课程平均成绩的课程号
SELECT Sno, Cno
FROM SC AS o
WHERE Grade > (SELECT AVG(Grade)
               FROM SC i
               WHERE o.Sno = i.Sno);

-- === 带有 ANY(SOME) 或 ALL 谓词的子查询
-- 查询非计算机科学系中比计算机科学系任意一个学生年龄小的学生姓名和年龄
SELECT Sname, Sage
FROM Student
WHERE Sdept != 'CS'
  AND Sage < ANY (SELECT Sage
                  FROM Student
                  WHERE Sdept = 'CS');
-- 查询非计算机科学系中比计算机科学系所有学生年龄小的学生姓名和年龄
SELECT Sname, Sage
FROM Student
WHERE Sdept != 'CS'
  AND Sage < ALL (SELECT Sage
                  FROM Student
                  WHERE Sdept = 'CS');

-- === 带有 EXISTS 谓词的子查询
-- 查询所有选修了 1 号课程的学生姓名
SELECT Sname
FROM Student
WHERE EXISTS (SELECT *
              FROM SC
              WHERE SC.Sno = Student.Sno
                AND Cno = '1');
-- 查询所有没有选修了 1 号课程的学生姓名
SELECT Sname
FROM Student
WHERE NOT EXISTS (SELECT *
                  FROM SC
                  WHERE SC.Sno = Student.Sno
                    AND Cno = '1');
多表查询
  • 笛卡尔积(隐式内连接):select ... from 表一,表二;
  • 内连接:select ... from 表一 [inner] join 表二 on 条件
  • 全外连接:保留自然连接的悬浮元组(共有属性值没有匹配的值的元组)

    MySQL 中没有直接的全外连接

  • 左外连接:只保留右侧表属性组为空的悬浮元组

    SELECT ... FROM table1 LEFT JOIN table2 ON table1.common_field = table2.common_field;

  • 右外连接:只保留左侧表属性组为空的悬浮元组

    SELECT ... FROM table1 RIGHT JOIN table2 ON table1.common_field = table2.common_field;

示例

使用的数据和子查询一致

sql
-- 使用自然连接查询学生选课情况
SELECT S.Sno, Sname, Ssex, Sdept, Cno, Grade
FROM Student AS S, SC
WHERE S.Sno = SC.Sno;
-- 查询课程的间接先行课
SELECT F.Cno, S.Cpno
FROM Course AS F, Course AS S
WHERE F.Cpno = S.Cno AND S.Cpno IS NOT NULL;

-- 查询学生的学生信息和其选课信息
SELECT S.Sno, Sname, Ssex, Sage, Sdept, Cno, Grade
FROM Student S LEFT OUTER JOIN SC ON (S.Sno = SC.Sno);

-- 查询每个学生的学号、姓名、选修课程名、成绩
SELECT S.Sno, Sname, Cname, Grade
FROM Student S, Course, SC
WHERE S.Sno = SC.Sno AND Course.Cno = SC.Cno;

更新数据

格式:`UPDATE 表名 SET 字段名=值[,字段名=值]... [WHERE 条件];

sql
update tb_test set name = '王五' where id = 3;

DANGER

更新数据时需要检查是否使用了有效的 WHERE 表达式,避免更新整个数据库。

删除数据

格式:`DELETE FROM 表名 [WHERE 条件];

sql
delete from tb_test where id = 3;

DANGER

删除数据时需要检查是否使用了有效的 WHERE 表达式,避免删除整个数据库。

常用函数

一般情况下聚合函数不能在 where 子句中使用,因为 where 用于过滤视图中的数据,在聚合和分组操作之前处理。

  • SUM: 聚合函数,总和
  • AVG: 聚合函数,平均值
  • MAX: 聚合函数,最大值
  • MIN: 聚合函数,最小值
  • FLOOR: 向下取整
  • CEIL: 向上取整
  • RAND: 生成 [0,1) 的随机值
  • CONCAT: 拼接多个值 SELECT CONCAT('value = ', 1);

显示警告信息

sql
show warnings;

函数和存储过程

定义语法

txt
CREATE
    [DEFINER = user]
    PROCEDURE [IF NOT EXISTS] sp_name ([proc_parameter[,...]])
    [characteristic ...] routine_body

CREATE
    [DEFINER = user]
    FUNCTION [IF NOT EXISTS] sp_name ([func_parameter[,...]])
    RETURNS type
    [characteristic ...] routine_body

proc_parameter:
    [ IN | OUT | INOUT ] param_name type

func_parameter:
    param_name type

param_name:
    参数名称

type:
    任何有效的 MySQL 数据类型

characteristic: {
    COMMENT 'string'
  | LANGUAGE SQL
  | [NOT] DETERMINISTIC
  | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
  | SQL SECURITY { DEFINER | INVOKER }
}

routine_body:
    存储过程或函数的主体部分,包含 SQL 代码逻辑

对公共部分进行解释:

  • [DEFINER = user]: 指定存储过程或者函数的创建者(默认是当前用户),可用来和 SQL SECURITY 特征配合。
  • characteristic: 存储过程或函数特征。
    • COMMENT 'string': 注释,描述存储过程或函数的作用。
    • LANGUAGE SQL: 指定是用 SQL 语言
    • [NOT] DETERMINISTIC: 指定函数的确定性,如果是 DETERMINISTIC 则表示是确定的,相同的输入会得到相同的输出结果。默认是 NOT DETERMINISTIC
    • { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }: SQL 行为。
      • CONTAINS SQL: 默认行为,表示存储过程或函数包含 SQL 语句,但这些语句不会读取或修改数据库数据
      • NO SQL: 表示存储过程或函数不包含任何 SQL 语句。它完全是逻辑运算或调用其他不包含 SQL 的过程或函数。
      • READS SQL DATA: 表示存储过程或函数读取数据库数据,但不修改数据。这意味着它可以执行 SELECT 语句或调用仅读取数据的其他存储过程或函数。
      • MONIFIES SQL DATA: 表示存储过程或函数读取并修改数据库数据。这意味着它可以执行 INSERTUPDATEDELETE 等修改数据的 SQL 操作。
    • SQL SECURITY { DEFINER | INVOKER }: 安全特征。
      • DEFINER: 存储过程或函数以 DEFINER 指定的用户权限执行,而不是调用者的权限。
      • INVOKER: 存储过程或函数以调用者的权限执行。

routine_body 可是简单的 SQL 语句也可以是复合语句,可以阅读复合语句的语法的官方文档,也可以查看复合语句部分。

复合语句

在 MySQL 客户端需要修改分隔符

sql
DELIMITER // -- 修改分隔符为两个斜杠
BEGIN
    SELECT NOW();
END // -- MYSQL 读取到两个斜杠后就会正常提交

DELIMITER ; -- 设置回分号

语法:

sql
[begin_label:] BEGIN
    [statement_list]
END [end_label]
  • statement_list 为要执行的语句列表,可以为空,并且可以嵌套复合语句。
  • begin_labelend_label 用于定义标签。可通过 ITERATELEAVE 引用这个标签。
    • 定义规则:
      • 开始标签后面要紧跟一个冒号 :
      • 可以只有开始标签没有结束标签,但是有结束标签一定要有开始标签。
      • 同一嵌套级的标签必须不同。
      • 标签不能超过 16 个字符。
    • ITERATE: 结束标签剩下语句,如果满足条件继续迭代。
    • LEAVE: 离开标签块。

声明语句(DECLARE Statement)

注意

  • 声明语句只能在 BEGIN...END 块中使用。
  • 声明语句必须位于任何其他语句之前。
  • 声明必须遵循如下顺序:变量(Variable)和条件(Condition) -> 游标(Cursors) -> 处理程序(Handler)
变量

语法:

sql
DECLARE var_name [, var_name] ... type [DEFAULT value]

修改变量的方法:

SET 语句
sql
SET variable = expr [, variable = expr] ...

variable: {
    user_var_name
  | param_name
  | local_var_name
  | {GLOBAL | @@GLOBAL.} system_var_name
  | {PERSIST | @@PERSIST.} system_var_name
  | {PERSIST_ONLY | @@PERSIST_ONLY.} system_var_name
  | [SESSION | @@SESSION. | @@] system_var_name
}
  • 用户定义变量:使用 @var_name 格式定义的变量会保存在会话上下文中。无须使用 DECLARE 进行声明。
  • 参数和局部变量:使用 var_name 格式。必须声明或者在参数列表中,才能使用 SET 语句。
  • 系统变量
    • 使用 @@var_name 或者 GLOBAL var_name 格式进行全局赋值,重启失效。
    • 使用 @@SESSION.var_name 或者 SESSION var_name 格式修改会话系统变量,在会话内有效。
    • 使用 @@LOCAL.var_name 或者 LOCAL var_name 格式修改系统变量,只在存储过程、触发器中临时起效。
    • 使用 @@PERSIST.var_name 或者 PERSIST var_name 格式修改系统变量,会持久化保存在配置文件中。

流程控制语句

MySQL 不支持 FOR 循环

CASE Statement

构建复杂的分支结构。

sql
CASE case_value
    WHEN when_value THEN statement_list
    [WHEN when_value THEN statement_list] ...
    [ELSE statement_list]
END CASE

--

CASE
    WHEN search_condition THEN statement_list
    [WHEN search_condition THEN statement_list] ...
    [ELSE statement_list]
END CASE
IF Statement

条件语句。

sql
IF search_condition THEN statement_list
    [ELSEIF search_condition THEN statement_list] ...
    [ELSE statement_list]
END IF
ITERATE Statement

ITERATE 只能出现在 LOOPREPEATWHILE 语句中。

ITERATE 的意思是“再次开始循环”,即其它编程语言的 continue

LEAVE Statement

语句用于退出具有给定标签的流控制结构。如果标签是最外层存储的程序块,则 LEAVE 退出程序。

LEAVE 可以在 BEGIN ... END 或循环结构(LOOPREPEATWHILE)中使用。

LOOP Statement

无限循环结构,需要使用 LEAVE 进行退出循环。所有一般需要和 IF 或者 CASE 语句配合。

sql
[begin_label:] LOOP
    statement_list
END LOOP [end_label]
REPEAT Statement

执行循环体的的语句,执行完成后判断条件,如果为 true 则结束循环。

DO WHILE 条件为 时结束。
DO UNTIL 条件为 时结束。

sql
[begin_label:] REPEAT
    statement_list
UNTIL search_condition
END REPEAT [end_label]
RETURN Statement

退出函数并返回数据,只在函数中使用,其它需要使用 LEAVE

RETURN expr

WHILE Statement

只要条件为真就一直执行循环体的语句。

sql
[begin_label:] WHILE search_condition DO
    statement_list
END WHILE [end_label]

存储过程

存储过程是一组 预编译的 SQL 语句集合,可接受 参数执行逻辑返回结果。 可简化复杂的数据库操作、提高性能和安全性。

参数类型:

  • IN: (只读、默认),存储过程内部对 IN 参数的修改不会影响调用者传入的值。IN 参数是默认参数类型。
  • OUT: (只写),存储过程内部对 OUT 参数赋值会覆盖其初始值。调用者在调用存储过程时必须提供一个变量来接收 OUT 参数的值。
  • INOUT: (读写),存储过程内部对 INOUT 参数的修改会影响调用者传入的值。调用者在调用存储过程时必须提供一个变量来传递 INOUT 参数的初始值,并接收存储过程返回的修改后的值。

函数

MySQL 函数是一段可重复使用的 SQL 代码,接受参数并返回一个值,函数可以用于计算、转换数据等操作,可以在 SQL 语句中 直接调用

创建一个计算两数之和的函数并使用:

sql
CREATE FUNCTION AddValue(VALUE1 INT, VALUE2 INT)
    RETURNS int
BEGIN
    DECLARE RES INT;
    SET RES = VALUE1 + VALUE2;
    RETURN RES;
END;

SELECT AddValue(1, 2);