Mysql使用序列

2023-07-18  本文已影响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引擎

上一篇 下一篇

猜你喜欢

热点阅读