美文网首页
Mysql使用序列

Mysql使用序列

作者: 子宇楚歌 | 来源:发表于2023-07-18 17:08 被阅读0次

    mysql官方目前仍不支持序列,当前docker镜像mysql最新版本为8.0.33,经测试不支持

    解决方案一:自定义序列

    通过创建一个序列表,定义有序列名,步长,当前值等,再创建自定义函数nextval, currval等

    解决方案二:选择非官方版本mysql

    1. mysql的阿里云版本alisql支持序列,对于使用阿里云RDS的数据库选择mysql即可

    2. 选择mysql的替代版mariadb,mariadb10.4开始支持序列

    不同版本的序列语法略有不同

    mariadb和alisql8.0创建和删除序列的语法基本一致

    ```

    -- 创建序列

    CREATE SEQUENCE student_seq [STARTWITH 1] [MINVALUE 1] [MAXVALUE 999999] [INCREMENTBY 1] [CACHE 100| NOCACHE] [CYCLE|NOCYCLE];

    -- 删除序列

    DROP SEQUENCE IF EXISTS student_seq;

    ```

    使用序列作为列默认值

    mariadb支持DDL直接指定nextval作为默认值

    ```

    CREATE TABLE `student` (

            `id` bigint(20) NOT NULL DEFAULT NEXTVAL(student_seq) COMMENT 'ID',

            `name` varchar(20) NOT NULL COMMENT '姓名',

            PRIMARY KEY (`id`),

    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学生表';

    ```

    mariadb也支持使用触发器的方式设置序列为默认值

    注意:mariadb对于非空数值列插入前会自动使用默认值0,判断的时候不是null

    ```

    CREATE TRIGGER student_sequence_trigger BEFORE INSERT

    ON student FOR EACH ROW

    IF new.id = 0 THEN

        SET new.id = nextval(student_seq);

    END IF;

    ```

    alisql8.0只支持使用触发器的方式设置序列为默认值

    ```

    CREATE TRIGGER student_sequence_trigger BEFORE INSERT

    ON student FOR EACH ROW

    IF new.id IS NULL THEN

        SET new.id = nextval(student_seq);

    END IF;

    ```

    为了兼容本地mariadb和阿里云服务器的兼容性写法

    ```

    CREATE TABLE `student` (

            `id` bigint(20) NOT NULL COMMENT 'ID',

            `name` varchar(20) NOT NULL COMMENT '姓名',

            PRIMARY KEY (`id`),

    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学生表';

    DELIMITER $$

    CREATE TRIGGER student_sequence_trigger BEFORE INSERT

    ON student FOR EACH ROW

    -- id不可为空时,alisql的new.id是null,mariadb的new.id是0

    IF (new.id IS NULL OR new.id = 0) THEN

        SET new.id = nextval(student_seq);

    END IF;

    $$

    DELIMITER ;

    ```

    本想使用alisql的docker镜像本地搭建数据库测试,但奈何alisql的docker镜像版本目前最高只有5.6

    另外,alisql还有诸多限制:

    1. alisql不支持动态创建sequence,使用prepare对创建sequence的动态sql预处理会导致mysql服务器挂掉,不知道什么原因,联系阿里云的技术支持也没个结果。如果创建sequence需要动态指定初始值可以分为两步,先直接创建sequence,指定初始值为1,再更新

    ```

    -- mariadb不支持update sequence,想要更新需要使用select setval(student_seq, maxId)

    SET @seqStr ='UPDATE student_seq SET start = @maxId';

    SET @sqlStr = REPLACE(@seqStr,'@maxId', maxId);

    PREPARE sqlStrFROM @sqlStr;

    EXECUTE sqlStr;

    ```

    2. alisql不支持动态创建触发器,而且也不能在存储过程中创建触发器,这也是官方mysql的限制。mariadb创建触发器支持CREATE TRIGGER IF NOT EXISTS或者CREATE OR REPLACE TRIGGER,alisql只支持直接CREATE TRIGGER。

    使用prepare动态创建触发器报错:

    This command is not supported in the prepared statement protocol yet

    在存储过程中创建触发器报错:

    Can't create a TRIGGER from within another stored routine

    想要使用动态sql创建触发器目前我想到的解决方案只有通过上层应用实现,如通过mybatis拼串后程序调用执行,又因为这是数据库结构变更,需要用flyway管理,采用java migration调用:

    ```

    // TriggerMapper.xml

    <select id="createTrigger">

        CREATE TRIGGER student${suffix}_sequence_trigger BEFORE INSERT

        ON student${suffix} FOR EACH ROW

        IF (new.id IS NULL OR new.id = 0) THEN

            SET new.id = nextval(student_seq);

        END IF;

    </select>

    // java migration,和sql文件migration效果相同

    @Component

    public class R__Create_trigger extends BaseJavaMigration {

        @Autowired

        private TriggerMapper triggerMapper;

        @Override

        public void migrate(Context context) throws Exception {

            // 获取动态参数后传给mapper拼串

            triggerMapper.createTrigger(suffix)

        }

    }

    ```

    测试环境:

    alisql8.0.28(阿里云)

    mariadb10.6.5

    参考链接:

    mariadb和对应的mysql版本功能对比

    阿里云sequence引擎

    相关文章

      网友评论

          本文标题:Mysql使用序列

          本文链接:https://www.haomeiwen.com/subject/bugnudtx.html