侧边栏壁纸
博主头像
Exering

It's not how much time you have, it's how you use it!

  • 累计撰写 11 篇文章
  • 累计创建 3 个标签
  • 累计收到 2 条评论
标签搜索

目 录CONTENT

文章目录

MySQL基础

Exering
2022-09-21 / 0 评论 / 0 点赞 / 1,025 阅读 / 22,657 字

MySQL基础

1. SELECT

  • 规范

    1. 字符串型和日期时间类型的数据建议使用单引号''
    2. 列的别名,尽量使用双引号""
  • 特性

    1. 字符串在必要时会进行隐式转换,转换不成功则会看作0
    2. NULL参与比较或运算,结果都为NULL
    3. ESCAPE能指定一个字符为转义字符,就不一定用\
  • 基础

    1. 关键字声明顺序:SELECT ... FROM ... WHERE ... ORDER BY ... LIMIT ...
    2. 别名:AS
    3. 去重:DESCTINCT
    4. NULL:解决方案之一IFNULL(filedname,0)
    5. 显示表中字段详细信息:DECRIBE(DESC)
    6. 查询常数:SELECT 'Hello World' FROM tablename,这样就有一列固定列Hello world
    7. 多级排序,直接在第一个ORDER BY后添加即可
    8. LIMIT start, length,分页公式start = (pageNumber - 1) * pageSizestart = 0时可省略,8.0新特性:LIMIT length OFFSET start
  • 运算符

    1. <=>安全等于符,与=唯一的区别就是<=>能判断NULL,当两边的表达式均为NULL时返回1,即NULL <=> NULL = true
    2. LIKE中,%代表0个或多个任意字符_代表1个任意字符,如果不想匹配可用\进行转义, [] 表示其中的某一个字符,[^] 表示除了其中的字符的所有字符
    3. REGEXP \ RLIKE,匹配正则表达式
    4. XOR,逻辑异或运算符
  • 多表连接

    1. 交叉连接:CROSS JOIN

    2. 建议多表查询时,所有字段前都指明其所在的表,一旦使用了表的别名,就只能使用别名,不能用原名

    3. 多表查询的分类:等值连接\非等值连接,非等值连接的连接条件不是=;自连接\非自连接,自连接是和自己连接;内连接\外连接。

      • 内连接:table_1 (INNER) JOIN table_2 ON ...,结果集中不包含一个表与另一个表不匹配的行。SQL92语法:SELECT ... FROM table_1, table_2 ... WHERE ...
      • 左(右)外连接: table_1 LEFT/RIGHT (OUTER) JOIN table_2 ON ...,查询结果还包含左(右)表中的不匹配项。SQL92语法:使用+符号,MySQL不支持
      • 满外连接:FULL OUTER JOINMySQL不支持
    4. UNION \ UNION ALLUNION去重UNION ALL去重,如果明确知道合并数据后的结果数据不存在重复数据则尽量使用UNION ALL

      • 替代满外连接:

        # 使用左外的 A union all 右外的 (B - A交B)
        SELECT ... FROM table_1 LEFT (OUTER) JOIN table_2 ON ... 
        UNION ALL 
        SELECT ... FROM table_1 RIGHT (OUTER) JOIN table_2 ON ... WHERE 关联字段 IS NULL
        
      • 实现A并B - A交B

        # 使用左外的 (A - A交B) union all 右外的 (B - A交B)
        SELECT ... FROM table_1 LEFT (OUTER) JOIN table_2 ON ... WHERE 关联字段 IS NULL
        UNION ALL 
        SELECT ... FROM table_1 RIGHT (OUTER) JOIN table_2 ON ... WHERE 关联字段 IS NULL
        
    5. SQL99语法新特性:NATURAL JOIN,不需要连接条件,会自动查找表中的相同字段;USING,可以简化等值条件的编写,如t1 JOIN t2 USING(field)等价于t1 JOIN t2 ON t1.field = t2.field,它要求字段名相同且只能做等值连接

  • GROUP BY

    • 如果过滤条件中使用了聚合函数,必须写在HAVING
    • HAVING必须声明在GROUP BY后面,与GROUP BY绑定
    • 当过滤条件中没有聚合函数时,声明在WHERE中,WHERE是先筛选数据再关联,执行效率更高,HAVING在执行的结果集中进行筛选,执行效率低
  • SQL执行原理

    • SQL92语法

      SELECT 字段1, 字段2, 字段3(存在聚合函数) 
      FROM 表1, 表2, 表3 
      WHERE 多表连接的条件 AND 不包含聚合函数的过滤条件 
      GROUP BY ..., ...
      HAVING 包含聚合函数的过滤条件
      ORDER BY ... 
      LIMIT ...
      
    • SQL99语法

      SELECT 字段1, 字段2, 字段3(存在聚合函数) 
      FROM 表1 (LEFT / RIGHT) JOIN 表2 ON 多表连接条件 (LEFT / RIGHT) JOIN 表3 ON 多表连接条件
      WHERE 不包含聚合函数的过滤条件 
      GROUP BY ..., ...
      HAVING 包含聚合函数的过滤条件
      ORDER BY ... 
      LIMIT ...
      
    • SQL执行过程

      FROM -> ON -> (LEFT / RIGHT) JOIN -> WHERE -> GROUP BY -> HAVING -> SELECT -> DISTINCT -> ORDER BY -> LIMIT
      
  • 子查询

    #题目:查询与141号员工的manager_id和department_id相同的其他员工
    #的employee_id,manager_id,department_id。
    SELECT employee_id,manager_id,department_id
    FROM employees
    WHERE (manager_id,department_id) = (
    				    SELECT manager_id,department_id
    			            FROM employees
    				    WHERE employee_id = 141
    				   )
    AND employee_id <> 141;
    

    称谓的规范:外查询(或主查询)、内查询(或子查询)。子查询(内查询)在主查询之前一次执行完成,子查询的结果被主查询(外查询)使用 。子查询要包含在括号内,建议将子查询放在比较条件的右侧,单行操作符对应单行子查询,多行操作符对应多行子查询。

    • 单行子查询,单行操作符: = != > >= < <=;多行子查询,多行子查询的操作符: IN ANY(任一) ALL SOME(同ANY)

    • SELECT中,除了GROUP BYLIMIT之外,其他位置都可以声明子查询,包括CASE WHEN

    • 如果子查询的执行依赖于外部查询,通常情况下都是因为子查询中的表用到了外部的表,并进行了条件关联,因此每执行一次外部查询,子查询都要重新计算一次,这样的子查询就称之为关联子查询

    • EXISTS \ NOT EXISTS

      • 关联子查询通常也会和EXISTS操作符一起来使用,用来检查在子查询中是否存在满足条件的行
        • 如果在子查询中不存在满足条件的行:条件返回FALSE,继续在子查询中查找;
        • 如果在子查询中存在满足条件的行:不在子查询中继续查找,条件返回TRUE
      # 题目:查询departments表中,不存在于employees表中的部门的department_id和department_name
      SELECT department_id, department_name
      FROM departments d
      WHERE NOT EXISTS (SELECT 'X'
      FROM employees
      WHERE department_id = d.department_id);
      

2. 函数

  • 流程控制函数:
    • IF(expr, value1,value2),表达式exprTRUE则值为value1,否则为value2
    • IFNULL(value1, value2)value1NULL则值为value2,否则为value1
    • CASE WHEN ... THEN ... [WHEN ... THEN ...] ELSE ... END,相当于if ... else if ... else
    • CASE field WHEN ... THEN ... [WHEN ... THEN ...] ELSE ... END,相当于switch case
  • 聚合函数
    • SELECT如果出现了表中的字段(非聚合函数)必须声明在GROUP BY
    • GROUP BY声明在 FROM之后 WHERE之后 ORDER BY之前 LIMIT之前
    • MySQLGROUP BY中使用WITH ROLLUP时不能同时使用ORDER BY对结果进行排序
    • WITH ROLLUP,在最后一行添加一个一行把所有数据看作一组后进行统计

3. DDL

DDL、DCL不可回滚

  • 创建和管理数据库

    # 创建数据库
    CREATE [IF NOT EXISTS] DATABASE 数据库名 [CHARACTER SET 字符集];
    # 查看正在使用的数据库
    SELECT DATABASES();
    # 查看所有数据库
    SHOW DATABASES;
    # 查看指定库中的所有表
    SHOW TABLES FROM 数据库名;
    # 查看数据库的创建信息
    SHOW CREATE DATABASE 数据库名;(\G)
    # 使用/切换数据库
    USE 数据库名;
    

    注意:DATABASE 不能改名。一些可视化工具可以改名,它是建新库,把所有表复制到新库,再删旧库完成的。

  • 修改和删除数据库

    # 更改数据库字符集
    ALTER DATABASE 数据库名 CHARACTER SET 字符集; # 比如:gbk, utf8等
    # 删除数据库
    DROP DATABASE [IF EXISTS] 数据库名
    

    注意:要操作表格和数据之前必须先说明是对哪个数据库进行操作,否则就要对所有对象加上“数据库名.”。

  • 创建表

    # 1
    CREATE TABLE [IF NOT EXISTS] 表名(
    字段1, 数据类型 [约束条件] [默认值],
    字段2, 数据类型 [约束条件] [默认值],
    字段3, 数据类型 [约束条件] [默认值],
    ……
    [表约束条件]
    );
    # 2
    CREATE TABLE dept80
    	[(column, column...)]
    AS
    SELECT employee_id, last_name, salary*12 ANNSAL, hire_date
    FROM employees
    WHERE department_id = 80;
    # 查看数据表的结构
    SHOW CREATE TABLE 表名\G
    
  • 修改表

    # 追加一个列
    ALTER TABLE 表名 ADD 【COLUMN】 字段名 字段类型 【DEFAULT 默认值】【FIRST|AFTER 字段名2】【FIRST|AFTER 字段名】;
    # 可以修改列的数据类型,长度、默认值和位置
    ALTER TABLE 表名 MODIFY 【COLUMN】 字段名1 字段类型 【DEFAULT 默认值】【FIRST|AFTER 字段名2】;
    # 重命名一个列
    ALTER TABLE 表名 CHANGE 【column】 列名 新列名 新数据类型 【DEFAULT 默认值】【FIRST|AFTER 字段名2】;
    # 删除一个列
    ALTER TABLE 表名 DROP 【COLUMN】字段名;
    
  • 重命名表

    RENAME TABLE emp
    TO myemp;
    
    ALTER table dept
    RENAME [TO] detail_dept; -- [TO]可以省略
    
  • 删除表与清空表

    DROP TABLE [IF EXISTS] 数据表1 [, 数据表2, …, 数据表n]
    
    TRUNCATE TABLE detail_dept;
    

    注意:

    • 删除表
      • 在MySQL中,当一张数据表 没有与其他任何数据表形成关联关系 时,可以将当前数据表直接删除
      • 数据和结构都被删除
      • 所有正在运行的相关事务被提交
      • 所有相关索引被删除
    • 清空表
      • 删除表中所有的数据
      • 释放表的存储空间
      • TRUNCATE语句不能回滚,而使用 DELETE 语句删除数据,可以回滚
      • TRUNCATE TABLE 在功能上与不带 WHERE 子句的 DELETE 语句相同
  • MySQL8新特性—DDL的原子化

    在MySQL 8.0版本中,InnoDB表的DDL支持事务完整性,即 DDL操作要么成功要么回滚 。DDL操作回滚日志写入到data dictionary数据字典表mysql.innodb_ddl_log(该表是隐藏的表,通过show tables无法看到)中,用于回滚操作。通过设置参数,可将DDL操作日志打印输出到MySQL错误日志中。

4. DML

DML可回滚,如果需要回滚数据,需要保证在DML前,进行设置:SET AUTOCOMMIT = FALSE;

  • 插入数据

    # 为表指定字段插入数据
    INSERT INTO table_name(column1 [, column2, …, columnn])
    VALUES
    (value1 [,value2, …, valuen]),
    (value1 [,value2, …, valuen]),
    ……
    (value1 [,value2, …, valuen]);
    # 为表的所有字段按默认顺序插入数据
    INSERT INTO table_name
    VALUES
    (value1 [,value2, …, valuen]),
    (value1 [,value2, …, valuen]),
    ……
    (value1 [,value2, …, valuen]);
    
    • 在 INSERT 子句中随意列出列名,但是一旦列出,VALUES中要插入的value1,…valuen需要与 column1,…columnn列一一对应。如果类型不同,将无法插入,并且MySQL会产生错误。

    • 一个同时插入多行记录的INSERT语句等同于多个单行插入的INSERT语句,但是多行的INSERT语句 在处理过程中 效率更高 。因为MySQL执行单条INSERT语句插入多行数据比使用多条INSERT语句 快,所以在插入多条记录时最好选择使用单条INSERT语句的方式插入。

    • VALUES 也可以写成 VALUE ,但是VALUES是标准写法,字符和日期型数据应包含在单引号中

    INSERT INTO 目标表名
    (tar_column1 [, tar_column2, …, tar_columnn])
    SELECT
    (src_column1 [, src_column2, …, src_columnn])
    FROM 源表名
    [WHERE condition];
    

    在 INSERT 语句中加入子查询,不必书写 VALUES 子句,子查询中的值列表应与 INSERT 子句中的列名对应

  • 更新数据

    UPDATE 表名
    SET column1=value1, column2=value2, ...
    [WHERE condition]
    
  • 删除数据

    DELETE FROM table_name [WHERE <condition>];
    
  • MySQL8新特性:计算列

    什么叫计算列呢?简单来说就是某一列的值是通过别的列计算得来的。例如,a列值为1、b列值为2,c列不需要手动插入,定义a+b的结果为c的值,那么c就是计算列,是通过别的列计算得来的。

    在MySQL 8.0中,CREATE TABLE 和 ALTER TABLE 中都支持增加计算列。下面以CREATE TABLE为例进行讲解。

    举例:定义数据表tb1,然后定义字段id、字段a、字段b和字段c,其中字段c为计算列,用于计算a+b的值。 首先创建测试表tb1,语句如下:

    CREATE TABLE tb1(
    id INT,
    a INT,
    b INT,
    c INT GENERATED ALWAYS AS (a + b) VIRTUAL
    );
    

5. 数据类型

选择建议:

  • 在定义数据类型时,如果确定是整数 ,就用INT; 如果是小数,一定用定点数类型DECIMAL(M,D); 如果是日期与时间,就用DATETIME
  • 阿里巴巴《Java开发手册》之MySQL数据库:
  • 任何字段如果为非负数,必须是UNSIGNED
  • 小数类型为DECIMAL,禁止使用FLOATDOUBLE
    • 说明:在存储的时候,FLOATDOUBLE都存在精度损失的问题,很可能在比较值的时候,得到不正确的结果。如果存储的数据范围超过DECIMAL的范围,建议将数据拆成整数和小数并分开存储。
  • 如果存储的字符串长度几乎相等,使用CHAR定长字符串类型
  • VARCHAR是可变长字符串,不预先分配存储空间,长度不要超过 5000。如果存储长度大于此值,定义字段类型为TEXT,独立出来一张表,用主键来对应,避免影响其它字段索引效率
  • 整数类型

    TINYINT :一般用于枚举数据,比如系统设定取值范围很小且固定的场景。

    SMALLINT :可以用于较小范围的统计数据,比如统计工厂的固定资产库存数量等。 MEDIUMINT :用于较大整数的计算,比如车站每日的客流量等。

    INT、INTEGER :取值范围足够大,一般情况下不用考虑超限问题,用得最多。比如商品编号。

    BIGINT :只有当你处理特别巨大的整数时才会用到。比如双十一的交易量、大型门户网站点击量、证券公司衍生产品持仓等。

    可选属性:

    • M,表示显示宽度,M的取值范围是(0, 255)。 显示宽度与类型可以存储的值范围无关,从MySQL 8.0.17开始,整数数据类型不推荐使用显示宽度属性
    • UNSIGNED,无符号类型(非负)
    • ZEROFILL,: 0填充,(如果某列是ZEROFILL,那么MySQL会自动为当前列添加UNSIGNED属性),如果指定了ZEROFILL只是表示不够M位时,用0在左边填充,如果超过M位,只要不超过数据存储范围即可

    注意:

    • int(M),必须和UNSIGNED ZEROFILL一起使用才有意义
    • 系统故障产生的成本远远超过增加几个字段存储空间所产生的成本。因此建议首先确保数据不会超过取值范围,在这个前提之下,再去考虑如何节省存储空间
  • 浮点类型

    FLOAT 表示单精度浮点数;

    DOUBLE 表示双精度浮点数;

    注意:

    • MySQL允许使用非标准语法 (其他数据库未必支持,因此如果涉及到数据迁移,则最好不要这么 用): FLOAT(M,D) 或 DOUBLE(M,D) 。这里,M称为 精度 ,D称为标度 。(M,D)中 M=整数位+小数位,D=小数位。 D<=M<=255,0<=D<=30。
    • 从MySQL 8.0.17开始,FLOAT(M,D) 和DOUBLE(M,D)用法在官方文档中已经明确不推荐使用,将来可能被移除。另外,关于浮点型FLOAT和DOUBLE的UNSIGNED也不推荐使用了,将来也可能被移除
  • 定点数类型

    数据类型 字节数 含义
    DECIMAL(M,D),DEC,NUMERIC M+2 有效范围由M和D决定

    使用 DECIMAL(M,D) 的方式表示高精度小数。其中M被称为精度,D被称为标度。0<=M<=65, 0<=D<=30,D<M。例如,定义DECIMAL(5,2)的类型,表示该列取值范围是-999.99~999.99

    说明:

    • DECIMAL(M,D)的最大取值范围与DOUBLE类型一样,但是有效的数据范围是由M和D决定的
    • 定点数在MySQL内部是以 字符串 的形式进行存储,这就决定了它一定是精准的
    • 浮点数 vs 定点数
    • 浮点数相对于定点数的优点是在长度一定的情况下,浮点类型取值范围大,但是不精准,适用 于需要取值范围大,又可以容忍微小误差的科学计算场景(比如计算化学、分子建模、流体动 力学等)
    • 定点数类型取值范围相对小,但是精准,没有误差,适合于对精度要求极高的场景 (比如涉及金额计算的场景)
  • 位类型:BIT

    二进制字符串类型 长度 长度范围 占用空间
    BIT(M) M 1 <= M <= 64 约为(M + 7)/8个字节
  • 日期与时间类型

    YEAR 类型通常用来表示年

    DATE 类型通常用来表示年、月、日

    TIME 类型通常用来表示时、分、秒

    DATETIME 类型通常用来表示年、月、日、时、分、秒 T

    IMESTAMP 类型通常用来表示带时区的年、月、日、时、分、秒

    注意:

    • 开发中用得最多的日期时间类型,就是DATETIME
    • 此外,一般存注册时间、商品发布时间等,不建议使用DATETIME存储,而是使用时间戳 ,因为DATETIME虽然直观,但不便于计算
    • 可以YYYY-MM-DD HH:MM:SSYYYYMMDDHHMMSS格式插入字段
    • TIMESTAMP类型,存储数据的时候需要对当前时间所在的时区进行转换,查询数据的时候再将时间转换回当前的时区。因此,使用TIMESTAMP存储的同一个时间值,在不同的时区查询时会显示不同的时间
    • 从MySQL5.5.27开始,2位格式的YEAR已经不推荐使用。YEAR默认格式就是“YYYY”,没必要写成YEAR(4),从MySQL 8.0.19开始,不推荐使用指定显示宽度的YEAR(4)数据类型。

    TIMESTAMPDATETIME的区别:

    • TIMESTAMP存储空间比较小,表示的日期时间范围也比较小
    • 底层存储方式不同,TIMESTAMP底层存储的是毫秒值,距离1970-1-1 0:0:0 0毫秒的毫秒值
    • 两个日期比较大小或日期计算时,TIMESTAMP更方便、更快
    • TIMESTAMP和时区有关。TIMESTAMP会根据用户的时区不同,显示不同的结果。而DATETIME则只能反映出插入时当地的时区,其他时区的人查看数据必然会有误差的
  • 文本字符串类型

    字符串(文本)类型 特点 长度 长度范围 占用的存储空间
    CHAR(M) 固定长度 M 0 <= M <= 255 M个字节
    VARCHAR(M) 可变长度 M 0 <= M <= 65535 (实际长度 + 1) 个字节

    CHAR类型:

    • CHAR(M) 类型一般需要预先定义字符串长度。如果不指定(M),则表示长度默认是1个字符
    • 如果保存时,数据的实际长度比CHAR类型声明的长度小,则会在右侧填充空格以达到指定的长 度。当MySQL检索CHAR类型的数据时,CHAR类型的字段会去除尾部的空格
    • 定义CHAR类型字段时,声明的字段长度即为CHAR类型字段所占的存储空间的字节数
    • 适用存储很短的、固定长度的

    VARCHAR类型:

    • VARCHAR(M) 定义时, 必须指定长度M,否则报错
    • MySQL4.0版本以下,varchar(20):指的是20字节,如果存放UTF8汉字时,只能存6个(每个汉字3字节) ;MySQL5.0版本以上,varchar(20):指的是20字符
    • 检索VARCHAR类型的字段数据时,会保留数据尾部的空格。VARCHAR类型的字段所占用的存储空间为字符串实际长度加1个字节

    TEXT类型:

    • 在MySQL中,TEXT用来保存文本类型的字符串,总共包含4种类型,分别为TINYTEXT、TEXT、MEDIUMTEXT 和 LONGTEXT 类型
    • 在向TEXT类型的字段保存和查询数据时,系统自动按照实际长度存储,不需要预先定义长度。这一点和VARCHAR类型相同
    • 由于实际存储的长度不确定,MySQL 不允许 TEXT 类型的字段做主键

    ENUM类型、SET类型:

    # ENUM
    CREATE TABLE test_enum(
    season ENUM('春','夏','秋','冬','unknow')
    );
    INSERT INTO test_enum
    VALUES('春'),('秋');
    # 忽略大小写
    INSERT INTO test_enum
    VALUES('UNKNOW');
    # 允许按照角标的方式获取指定索引位置的枚举值
    INSERT INTO test_enum
    VALUES('1'),(3);
    # Data truncated for column 'season' at row 1
    INSERT INTO test_enum
    VALUES('ab');
    # 当ENUM类型的字段没有声明为NOT NULL时,插入NULL也是有效的
    INSERT INTO test_enum
    VALUES(NULL);
    
    # SET
    CREATE TABLE test_set(
    s SET ('A', 'B', 'C')
    );
    INSERT INTO test_set (s) VALUES ('A'), ('A,B');
    #插入重复的SET类型成员时,MySQL会自动删除重复的成员
    INSERT INTO test_set (s) VALUES ('A,B,C,A');
    #向SET类型的字段插入SET成员中不存在的值时,MySQL会抛出错误。
    INSERT INTO test_set (s) VALUES ('A,B,C,D');
    
  • 二进制字符串类型

    MySQL中的二进制字符串类型主要存储一些二进制数据,比如可以存储图片、音频和视频等二进制数据

    BINARY与VARBINARY类型:

    • BINARY和VARBINARY类似于CHAR和VARCHAR,只是它们存储的是二进制字符串
    • VARBINARY (M)为可变长度的二进制字符串,M表示最多能存储的字节数

    BLOB类型:

    • 可以存储一个二进制的大对象,比如图片、音频和视频等
    • 在实际工作中,往往不会在MySQL数据库中使用BLOB类型存储大对象数据
  • JSON 类型

    CREATE TABLE test_json(
    js json
    );
    
    INSERT INTO test_json (js)
    VALUES ('{"name":"songhk", "age":18, "address":{"province":"beijing",
    "city":"beijing"}}');
    # 通过“->”和“->>”符号,从JSON字段中正确查询出了指定的JSON数据的值
    mysql> SELECT js -> '$.name' AS NAME,js -> '$.age' AS age ,js -> '$.address.province'
    AS province, js -> '$.address.city' AS city
    -> FROM test_json;
    +----------+------+-----------+-----------+
    | NAME | age | province | city |
    +----------+------+-----------+-----------+
    | "songhk" | 18 | "beijing" | "beijing" |
    +----------+------+-----------+-----------+
    1 row in set (0.00 sec
    
  • 空间类型

    MySQL 空间类型扩展支持地理特征的生成、存储和分析

6. 约束

可以在创建表时规定约束(通过 CREATE TABLE 语句),或者在表创建之后通过 ALTER TABLE 语句规定约束

  • 分类

    • 根据约束数据列的限制

      • 单列约束:每个约束只约束一列
      • 多列约束:每个约束可约束多列数据
    • 根据约束的作用范围

      • 列级约束:只能作用在一个列上,跟在列的定义后面
      • 表级约束:可以作用在多个列上,不与列一起,而是单独定义
      位置 支持的约束类型 是否可以起约束名
      列级约束 列的后面 语法都支持,但外键没有效果 不可以
      表级约束 所有列的下面 默认和非空不支持,其他支持 可以(主键没有效果)
    • 根据约束起的作用

      • NOT NULL 非空约束,规定某个字段不能为空
      • UNIQUE 唯一约束,规定某个字段在整个表中是唯一的
      • PRIMARY KEY 主键(非空且唯一)约束
      • **FOREIGN KEY 外键约束 **
      • CHECK 检查约束
      • DEFAULT 默认值约束

      注意: MySQL不支持check约束,但可以使用check约束,而没有任何效果

  • 查看某个表已有的约束

    #information_schema数据库名(系统库)
    #table_constraints表名称(专门存储各个表的约束)
    SELECT * FROM information_schema.table_constraints
    WHERE table_name = '表名称';
    

6.1 非空约束NOT NULL

  • 特点

    • 默认,所有的类型的值都可以是NULL,包括INT、FLOAT等数据类型

    • 非空约束只能出现在表对象的列上,只能某个列单独限定非空,不能组合非空

    • 一个表可以有很多列都分别限定了非空

    • 空字符串''不等于NULL,0也不等于NULL

  • 使用

    # 建表时添加
    CREATE TABLE 表名称(
    字段名 数据类型,
    字段名 数据类型 NOT NULL,
    字段名 数据类型 NOT NULL
    );
    # 建表后
    alter table 表名称 modify 字段名 数据类型 not null;
    # 删除约束
    alter table 表名称 modify 字段名 数据类型 NULL;#去掉not null,相当于修改某个非注解字段,该字段允许为空
    alter table 表名称 modify 字段名 数据类型;#去掉not null,相当于修改某个非注解字段,该字段允许为空
    

6.2 唯一性约束UNIQUE

  • 特点

    • 同一个表可以有多个唯一约束
    • 唯一约束可以是某一个列的值唯一,也可以多个列组合的值唯一
    • 唯一性约束允许列值为空
    • 在创建唯一约束的时候,如果不给唯一约束命名,就默认和列名相同
    • MySQL会给唯一约束的列上默认创建一个唯一索引
  • 使用

    # 建表前添加
    create table 表名称(
    字段名 数据类型,
    字段名 数据类型 unique,
    字段名 数据类型 unique [key],
    字段名 数据类型
    );
    create table 表名称(
    字段名 数据类型,
    字段名 数据类型,
    字段名 数据类型,
      -- 使用表级约束语法
    [constraint 约束名] unique [key](字段名)
    );、
    # 建表后添加
    #字段列表中如果是一个字段,表示该列的值唯一。如果是两个或更多个字段,那么复合唯一,即多个字段的组合是唯一的
    #方式1:
    alter table 表名称 add unique key(字段列表);
    #方式2:
    alter table 表名称 modify 字段名 字段类型 unique;
    
    # 删除唯一约束
    ALTER TABLE 表名称
    DROP INDEX 约束名;
    
    • 添加唯一性约束的列上也会自动创建唯一索引。
    • 删除唯一约束只能通过删除唯一索引的方式删除。
    • 删除时需要指定唯一索引名,唯一索引名就和唯一约束名一样。
    • 如果创建唯一约束时未指定名称,如果是单列,就默认和列名相同;如果是组合列,那么默认和(字段列表)中排在第一个的列名相同。也可以自定义唯一性约束名。
    • 注意:可以通过 show index from 表名称; 查看表的索引

6.3 主键约束PRIMARY KEY

  • 特点

    • 主键约束相当于唯一约束+非空约束的组合,主键约束列不允许重复,也不允许出现空值
    • 一个表最多只能有一个主键约束,建立主键约束可以在列级别创建,也可以在表级别上创建
    • 主键约束对应着表中的一列或者多列(复合主键)
    • 如果是多列组合的复合主键约束,那么这些列都不允许为空值,并且组合的值不允许重复
    • MySQL的主键名总是PRIMARY,就算自己命名了主键约束名也没用
    • 当创建主键约束时,系统默认会在所在的列或列组合上建立对应的主键索引(能够根据主键查询的,就根据主键查询,效率更高)。如果删除主键约束了,主键约束对应的索引就自动删除了
    • 需要注意的一点是,不要修改主键字段的值。因为主键是数据记录的唯一标识,如果修改了主键的值,就有可能会破坏数据的完整性
  • 使用

    create table 表名称(
    字段名 数据类型 primary key, #列级模式
    字段名 数据类型,
    字段名 数据类型
    );
    create table 表名称(
    字段名 数据类型,
    字段名 数据类型,
    字段名 数据类型,
    [constraint 约束名] primary key(字段名) #表级模式
    );
    
    ALTER TABLE 表名称 ADD PRIMARY KEY(字段列表); #字段列表可以是一个字段,也可以是多个字段,如果是多个字段的话,是复合主键
    
    alter table 表名称 drop primary key;
    

    说明:删除主键约束,不需要指定主键名,因为一个表只有一个主键,删除主键约束后,非空还存在。

6.4 自增AUTO_INCREMENT

  • 特点

    • 一个表最多只能有一个自增长列
    • 当需要产生唯一标识符或顺序值时,可设置自增长
    • 自增长列约束的列必须是键列(主键列,唯一键列)
    • 自增约束的列的数据类型必须是整数类型
    • 如果自增列指定了 0 和 null,会在当前最大值的基础上自增;如果自增列手动指定了具体值,直接赋值为具体值
  • 使用

    create table 表名称(
    字段名 数据类型 primary key auto_increment,
    字段名 数据类型 unique key not null,
    字段名 数据类型 unique key,
    字段名 数据类型 not null default 默认值,
    );
    create table 表名称(
    字段名 数据类型 default 默认值 ,
    字段名 数据类型 unique key auto_increment,
    字段名 数据类型 not null default 默认值,,
    primary key(字段名)
    );
    
    alter table 表名称 modify 字段名 数据类型 auto_increment;
    
    alter table 表名称 modify 字段名 数据类型; #去掉auto_increment相当于删除
    
  • 在MySQL 5.7系统中,对于自增主键的分配规则,是由InnoDB数据字典内部一个计数器来决定的,而该计数器只在内存中维护,并不会持久化到磁盘中。当数据库重启时,该计数器会被初始化
  • MySQL 8.0将自增主键的计数器持久化到 重做日志中。每次计数器发生改变,都会将其写入重做日志中。如果数据库重启,InnoDB会根据重做日志中的信息来初始化计数器的内存值

6.5 外键约束FOREIGN KEY

限定某个表的某个字段的引用完整性

  • 特点

    • 从表的外键列,必须引用/参考主表的主键或唯一约束的列
    • 在创建外键约束时,如果不给外键约束命名,默认名不是列名,而是自动产生一个外键名(例如student_ibfk_1;),也可以指定外键约束名
    • 创建(CREATE)表时就指定外键约束的话,先创建主表,再创建从表
    • 删表时,先删从表(或先删除外键约束),再删除主表
    • 当主表的记录被从表参照时,主表的记录将不允许删除,如果要删除数据,需要先删除从表中依赖该记录的数据,然后才可以删除主表的数据
    • 在“从表”中指定外键约束,并且一个表可以建立多个外键约束
    • 从表的外键列与主表被参照的列名字可以不相同,但是数据类型必须一样,逻辑意义一致
    • 当创建外键约束时,系统默认会在所在的列上建立对应的普通索引。但是索引名是外键的约束名(根据外键查询效率很高)
    • 删除外键约束后,必须 手动 删除对应的索引
  • 使用

    create table 主表名称(
    字段1 数据类型 primary key,
    字段2 数据类型
    );
    create table 从表名称(
    字段1 数据类型 primary key,
    字段2 数据类型,
    [CONSTRAINT <外键约束名称>] FOREIGN KEY(从表的某个字段) references 主表名(被参考字段)
    );
    #(从表的某个字段)的数据类型必须与主表名(被参考字段)的数据类型一致,逻辑意义也一样
    #(从表的某个字段)的字段名可以与主表名(被参考字段)的字段名一样,也可以不一样
    -- FOREIGN KEY: 在表级指定子表中的列
    -- REFERENCES: 标示在父表中的列
    
    # 表与表的关联都是提前设计好了的,因此,会在创建表的时候就把外键约束定义好
    ALTER TABLE 从表名 ADD [CONSTRAINT 约束名] FOREIGN KEY (从表的字段) REFERENCES 主表名(被引用字段) [on update 约束等级][on delete 约束等级];
    
    # (1)第一步先查看约束名和删除外键约束
    SELECT * FROM information_schema.table_constraints WHERE table_name = '表名称';#查看某个表的约束名
    ALTER TABLE 从表名 DROP FOREIGN KEY 外键约束名;
    #(2)第二步查看索引名和删除索引。(注意,只能手动删除)
    SHOW INDEX FROM 表名称; #查看某个表的索引名
    ALTER TABLE 从表名 DROP INDEX 索引名;
    
    • 添加了外键约束后,主表的修改和删除数据受约束
    • 添加了外键约束后,从表的添加和修改数据受约束
    • 在从表上建立外键,要求主表必须存在
    • 删除主表时,要求从表从表先删除,或将从表中外键引用该主表的关系先删除
  • 约束等级

    • Cascade方式 :在父表上update/delete记录时,同步update/delete掉子表的匹配记录
    • Set null方式 :在父表上update/delete记录时,将子表上匹配记录的列设为null,但是要注意子 表的外键列不能为not null
    • No action方式 :如果子表中有匹配的记录,则不允许对父表对应候选键进行update/delete操作
    • Restrict方式 :同no action, 都是立即检查外键约束
    • Set default方式 (在可视化工具SQLyog中可能显示空白):父表有变更时,子表将外键列设置 成一个默认的值,但Innodb不能识别

    如果没有指定等级,就相当于Restrict方式。对于外键约束,最好是采用: ON UPDATE CASCADE ON DELETE RESTRICT 的方式。

  • 开发经验

    • 在 MySQL 里,外键约束是有成本的,需要消耗系统资源。对于大并发的 SQL 操作,有可能会不适合。比如大型网站的中央数据库,可能会 因为外键约束的系统开销而变得非常慢 。所以, MySQL 允许你不使用系统自带的外键约束,在 应用层面 完成检查数据一致性的逻辑。也就是说,即使你不 用外键约束,也要想办法通过应用层面的附加逻辑,来实现外键约束的功能,确保数据的一致性。
    • 阿里开发规范:
    • 不得使用外键与级联,一切外键概念必须在应用层解决
    • 外键与级联更新适用于 单机低并发 ,不适合 分布式 、 高并发集群 ;级联更新是强阻塞,存在数据库 更新风暴 的风险;外键影响 数据库的 插入速度

6.6 检查约束CHECK

MySQL5.7 可以使用check约束,但check约束对数据验证没有任何作用。添加数据时,没有任何错误或警告。但是MySQL 8.0中可以使用check约束了。

create table employee(
eid int primary key,
ename varchar(5),
gender char check ('男' or '女')
);

6.7 默认值约束DEFAULT

create table 表名称(
字段名 数据类型 primary key,
字段名 数据类型 unique key not null,
字段名 数据类型 unique key,
字段名 数据类型 not null default 默认值,
);
create table 表名称(
再举例:
字段名 数据类型 default 默认值 ,
字段名 数据类型 not null default 默认值,
字段名 数据类型 not null default 默认值,
primary key(字段名),
unique key(字段名)
);
# 说明:默认值约束一般不在唯一键和主键列上加

alter table 表名称 modify 字段名 数据类型 default 默认值;
#如果这个字段原来有非空约束,你还保留非空约束,那么在加默认值约束时,还得保留非空约束,否则非空约束就被删除了
#同理,在给某个字段加非空约束也一样,如果这个字段原来有默认值约束,你想保留,也要在modify语句中保留默认值约束,否则就删除了
alter table 表名称 modify 字段名 数据类型 default 默认值 not null;

alter table 表名称 modify 字段名 数据类型 ;#删除默认值约束,也不保留非空约束
alter table 表名称 modify 字段名 数据类型 not null; #删除默认值约束,保留非空约束

7. 视图

  • 视图的创建和删除只影响视图本身,不影响对应的基表。但是当对视图中的数据进行增加、删除和 修改操作时,数据表中的数据会相应地发生变化,反之亦然。
  • 要使视图可更新,视图中的行和底层基本表中的行之间必须存在 一对一 的关系
  • 向视图提供数据内容的语句为 SELECT 语句, 可以将视图理解为存储起来的 SELECT 语句
  • 视图,是向用户提供基表数据的另一种表现形式。通常情况下,小型项目的数据库可以不使用视 图,但是在大型项目中,以及数据表比较复杂的情况下,视图的价值就凸显出来了,它可以帮助我 们把经常查询的结果集放到虚拟表中,提升使用效率。理解和使用起来都非常方便
  • 使用

    # 创建或更新
    CREATE [OR REPLACE]
    [ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}]
    VIEW 视图名称 [(字段列表)]
    AS 查询语句
    [WITH [CASCADED|LOCAL] CHECK OPTION]
    
    ALTER VIEW 视图名称
    AS
    查询语句
    
    DROP VIEW IF EXISTS 视图名称1,视图名称2,视图名称3,...;
    

8. 存储过程和存储函数

8.1 存储过程

  • 语法

    CREATE PROCEDURE 存储过程名(IN|OUT|INOUT 参数名 参数类型,...)
    [characteristics ...]
    BEGIN
    存储过程体
    END
    
    • IN :当前参数为输入参数,也就是表示入参; 存储过程只是读取这个参数的值。如果没有定义参数种类, 默认就是 IN ,表示输入参数。

    • OUT :当前参数为输出参数,也就是表示出参; 执行完成之后,调用这个存储过程的客户端或者应用程序就可以读取这个参数返回的值了。

    • INOUT :当前参数既可以为输入参数,也可以为输出参数

    • characteristics 表示创建存储过程时指定的对存储过程的约束条件

      LANGUAGE SQL
      | [NOT] DETERMINISTIC
      | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
      | SQL SECURITY { DEFINER | INVOKER }
      | COMMENT 'string'
      
      1. BEGIN… END: BEGIN… END 中间包含了多个语句,每个语句都以(;)号为结束符。
      2. DECLARE:DECLARE 用来声明变量,使用的位置在于 BEGIN…END 语句中间,而且需要在其他语句使用之前进
      行变量的声明。
      3. SET:赋值语句,用于对变量进行赋值。
      4. SELECT… INTO:把从数据表中查询的结果存放到变量中,也就是为变量赋值。
      
      DELIMITER 新的结束标记
      
  • 调用

    CALL 存储过程名(实参列表)
    # 调用in模式的参数
    CALL sp1('值');
    # 调用out模式的参数:
    SET @name;
    CALL sp1(@name);
    # 调用inout模式的参数:
    SET @name=值;
    CALL sp1(@name);
    SELECT @name;
    

8.2 存储函数

  • 语法

    CREATE FUNCTION 函数名(参数名 参数类型,...)
    RETURNS 返回值类型
    [characteristics ...]
    BEGIN
    函数体 #函数体中肯定有 RETURN 语句
    END
    # 调用
    SELECT 函数名(实参列表)
    
  • 对比

    存储函数可以放在查询语句中使用,存储过程不行。反之,存储过程的功能更加强大,包括能够执行对表的操作(比如创建表,删除表等)和事务操作,这些功能是存储函数不具备的

8.3 存储过程和函数的查看、修改、删除

  • 查看

    SHOW CREATE {PROCEDURE | FUNCTION} 存储过程名或函数名
    
    SHOW {PROCEDURE | FUNCTION} STATUS [LIKE 'pattern']
    
    SELECT * FROM information_schema.Routines
    WHERE ROUTINE_NAME='存储过程或函数的名' [AND ROUTINE_TYPE = {'PROCEDURE|FUNCTION'}];
    
  • 修改

    ALTER {PROCEDURE | FUNCTION} 存储过程或函数的名 [characteristic ...]
    
    { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
    | SQL SECURITY { DEFINER | INVOKER }
    | COMMENT 'string'
    
  • 删除

    DROP {PROCEDURE | FUNCTION} [IF EXISTS] 存储过程或函数的名
    

8.4 总结

阿里开发规范

【强制】禁止使用存储过程,存储过程难以调试和扩展,更没有移植性

9. 变量、流程控制与游标

9.1 系统变量

#查看所有全局变量
SHOW GLOBAL VARIABLES;
#查看所有会话变量
SHOW SESSION VARIABLES;
# 或
SHOW VARIABLES;
#查看满足条件的部分系统变量。
SHOW GLOBAL VARIABLES LIKE '%标识符%';
#查看满足条件的部分会话变量
SHOW SESSION VARIABLES LIKE '%标识符%';
#查看指定的系统变量的值
SELECT @@global.变量名;
#查看指定的会话变量的值
SELECT @@session.变量名;
#或者
SELECT @@变量名;

修改:

# 方式1:修改MySQL 配置文件 ,继而修改MySQL系统变量的值(该方法需要重启MySQL服务)
# 方式2:在MySQL服务运行期间,使用“set”命令重新设置系统变量的值
#为某个系统变量赋值
#方式1:
SET @@global.变量名=变量值;
#方式2:
SET GLOBAL 变量名=变量值;
#为某个会话变量赋值
#方式1:
SET @@session.变量名=变量值;
#方式2:
SET SESSION 变量名=变量值;

用户变量:

  • 会话用户变量:作用域和会话变量一样,只对 当前连接会话有效;
  • 局部变量:只在 BEGIN 和 END 语句块中有效。局部变量只能在 存储过程和函数中使用。
# 1.回话用户变量
#方式1:“=”或“:=”
SET @用户变量 = 值;
SET @用户变量 := 值;
#方式2:“:=” 或 INTO关键字
SELECT @用户变量 := 表达式 [FROM 等子句];
SELECT 表达式 INTO @用户变量 [FROM 等子句];

SELECT @用户变量
# 2.局部变量
BEGIN
#声明局部变量
DECLARE 变量名1 变量数据类型 [DEFAULT 变量默认值];
DECLARE 变量名2,变量名3,... 变量数据类型 [DEFAULT 变量默认值];
#为局部变量赋值
SET 变量名1 = 值;
SELECT 值 INTO 变量名2 [FROM 子句];
#查看局部变量的值
SELECT 变量1,变量2,变量3;
END
  • MySQL 8.0的新特性—全局变量的持久化

    在MySQL数据库中,全局变量可以通过SET GLOBAL语句来设置。例如,设置服务器语句超时的限制,可 以通过设置系统变量max_execution_time来实现:

    SET GLOBAL MAX_EXECUTION_TIME=2000;
    

    使用SET GLOBAL语句设置的变量值只会 临时生效 。 数据库重启 后,服务器又会从MySQL配置文件中读取 变量的默认值。 MySQL 8.0版本新增了 SET PERSIST 命令。例如,设置服务器的最大连接数为1000:

    SET PERSIST global max_connections = 1000;
    

    MySQL会将该命令的配置保存到数据目录下的 mysqld-auto.cnf 文件中,下次启动时会读取该文件,用 其中的配置来覆盖默认的配置文件。

9.2 定义条件与处理程序

条件 是事先定义程序执行过程中可能遇到的问题, 处理程序 定义了在遇到问题时应当采取的处理方式,并且保证存储过程或函数在遇到警告或错误时能继续执行。这样可以增强存储程序处理问题的能力,避免程序异常停止运行。

说明:定义条件和处理程序在存储过程、存储函数中都是支持的。

  • 定义条件
DECLARE 错误名称 CONDITION FOR 错误码(或错误条件)
# 举例
#使用MySQL_error_code
DECLARE Field_Not_Be_NULL CONDITION FOR 1048;
#使用sqlstate_value
DECLARE Field_Not_Be_NULL CONDITION FOR SQLSTATE '23000';
  • 定义处理程序

    DECLARE 处理方式 HANDLER FOR 错误类型 处理语句
    
    • 处理方式:处理方式有3个取值:CONTINUE、EXIT、UNDO。
      • CONTINUE :表示遇到错误不处理,继续执行。
      • EXIT :表示遇到错误马上退出。
      • UNDO :表示遇到错误后撤回之前的操作。MySQL中暂时不支持这样的操作。
    • 错误类型(即条件)可以有如下取值:
      • SQLSTATE '字符串错误码' :表示长度为5的sqlstate_value类型的错误代码;
      • MySQL_error_code :匹配数值类型错误代码;
      • 错误名称 :表示DECLARE … CONDITION定义的错误条件名称。
      • SQLWARNING :匹配所有以01开头的SQLSTATE错误代码;
      • NOT FOUND :匹配所有以02开头的SQLSTATE错误代码;
      • SQLEXCEPTION :匹配所有没有被SQLWARNING或NOT FOUND捕获的SQLSTATE错误代码;
    • 处理语句:如果出现上述条件之一,则采用对应的处理方式,并执行指定的处理语句。语句可以是像“ SET 变量 = 值 ”这样的简单语句,也可以是使用 BEGIN ... END 编写的复合语句。
  • 举例

    DELIMITER //
    CREATE PROCEDURE UpdateDataNoCondition()
    	BEGIN
    		#定义处理程序
    		DECLARE CONTINUE HANDLER FOR 1048 SET @proc_value = -1;
        SET @x = 1;
        UPDATE employees SET email = NULL WHERE last_name = 'Abel';
        SET @x = 2;
        UPDATE employees SET email = 'aabbel' WHERE last_name = 'Abel';
        SET @x = 3;
      END //
    DELIMITER ;
    
    mysql> CALL UpdateDataWithCondition();
    Query OK, 0 rows affected (0.01 sec)
    mysql> SELECT @x,@proc_value;
    +------+-------------+
    | @x | @proc_value |
    +------+-------------+
    | 3 | -1 |
    +------+-------------+
    1 row in set (0.00 sec)
    

9.3 流程控制

  • 分支结构之 IF

    IF 表达式1 THEN 操作1
    [ELSEIF 表达式2 THEN 操作2]……
    [ELSE 操作N]
    END IF
    
  • 分支结构之 CASE

    #情况一:类似于switch
    CASE 表达式
    WHEN 值1 THEN 结果1或语句1(如果是语句,需要加分号)
    WHEN 值2 THEN 结果2或语句2(如果是语句,需要加分号)
    ...
    ELSE 结果n或语句n(如果是语句,需要加分号)
    END [case](如果是放在begin end中需要加上case,如果放在select后面不需要)
    
    #情况二:类似于多重if
    CASE
    WHEN 条件1 THEN 结果1或语句1(如果是语句,需要加分号)
    WHEN 条件2 THEN 结果2或语句2(如果是语句,需要加分号)
    ...
    ELSE 结果n或语句n(如果是语句,需要加分号)
    END [case](如果是放在begin end中需要加上case,如果放在select后面不需要)
    
  • 循环结构之LOOP

    [loop_label:] LOOP
    循环执行的语句
    END LOOP [loop_label]
    # 其中,loop_label表示LOOP语句的标注名称,该参数可以省略。
    
  • 循环结构之WHILE

    [while_label:] WHILE 循环条件 DO
    循环体
    END WHILE [while_label];
    
  • 循环结构之REPEAT

    [repeat_label:] REPEAT
    循环体的语句
    UNTIL 结束循环的条件表达式
    END REPEAT [repeat_label]
    

    1、这三种循环都可以省略名称,但如果循环中添加了循环控制语句(LEAVE或ITERATE)则必须添加名称。

    2、 LOOP:一般用于实现简单的"死"循环 WHILE:先判断后执行 REPEAT:先执行后判断,无条件 至少执行一次

  • 跳转语句之LEAVE\ITERATE语句

    • LEAVE语句:可以用在循环语句内,或者以 BEGIN 和 END 包裹起来的程序体内,表示跳出循环或者跳出程序体的操作。如果你有面向过程的编程语言的使用经验,你可以把 LEAVE 理解为 break
    • ITERATE语句:只能用在循环语句(LOOP、REPEAT和WHILE语句)内,表示重新开始循环,将执行顺序 转到语句段开头处。如果你有面向过程的编程语言的使用经验,你可以把 ITERATE 理解为 continue,意思为“再次循环”。
    LEAVE label
    ITERATE label
    

9.4 游标

# 第一步,声明游标
# 这个语法适用于 MySQL,SQL Server,DB2 和 MariaDB
DECLARE cursor_name CURSOR FOR select_statement;
# 如果是用 Oracle 或者 PostgreSQL
DECLARE cursor_name CURSOR IS select_statement;

# 第二步,打开游标
OPEN cursor_name

# 第三步,使用游标
FETCH cursor_name INTO var_name [, var_name] ...

# 第四部,关闭游标
CLOSE cursor_name
  • 游标是 MySQL 的一个重要的功能,为 逐条读取 结果集中的数据,提供了完美的解决方案。跟在应用层 面实现相同的功能相比,游标可以在存储程序中使用,效率高,程序也更加简洁。
  • 但同时也会带来一些性能问题,比如在使用游标的过程中,会对数据行进行 加锁 ,这样在业务并发量大 的时候,不仅会影响业务之间的效率,还会 消耗系统资源 ,造成内存不足,这是因为游标是在内存中进 行的处理。
  • 当我们使用完游标后需要关闭掉该游标。因为游标会占用系统资源 ,如果不及时关闭,游标会一直保持到存储过程结束,影响系统运行的效率。而关闭游标的操作,会释放游标占用的系统资源
# 举例
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 ;

10. 触发器

  • 语法

    CREATE TRIGGER 触发器名称
    {BEFORE|AFTER} {INSERT|UPDATE|DELETE} ON 表名
    FOR EACH ROW
    触发器执行的语句块;
    
    SHOW TRIGGERS\G
    
    SHOW CREATE TRIGGER 触发器名
    
    SELECT * FROM information_schema.TRIGGERS;
    
    DROP TRIGGER IF EXISTS 触发器名称;
    
  • 优点

    • 触发器可以确保数据的完整性
    • 触发器可以帮助我们记录操作日志
    • 触发器还可以用在操作数据前,对数据进行合法性检查
  • 缺点

    • 触发器最大的一个问题就是可读性差
    • 相关数据的变更,可能会导致触发器出错

注意:

  • 如果在子表中定义了外键约束,并且外键指定了ON UPDATE/DELETE CASCADE/SET NULL子句,此 时修改父表被引用的键值或删除父表被引用的记录行时,也会引起子表的修改和删除操作,此时基于子 表的UPDATE和DELETE语句定义的触发器并不会被激活
0
博主关闭了所有页面的评论