如何使用 MySQL 创建两个自增列?
How to create two auto increment columns using MySQL?
databaseChangeLog:
- changeSet:
id: ...
author: ...
preConditions:
- onError: MARK_RAN
- not:
- tableExists:
tableName: ORDERS
changes:
- createTable:
tableName: ORDERS
columns:
- column:
name: ID
type: INT
autoIncrement: true
constraints:
primaryKey: true
- column:
name: ID_USER
type: INT
...
- sql:
"DELIMITER $$
CREATE FUNCTION autoInc ()
RETURNS INT(10)
BEGIN
DECLARE getCount INT(10);
SET getCount = (
SELECT COUNT(USER_ID)
FROM ORDERS) + 1;
RETURN getCount;
END$$
DELIMITER ;"
我正在使用这种方法。当我在订单中插入新条目时,它也应该增加 user_id。因为我需要 user_id 作为另一个自动递增列。我正在使用 Liquibase 迁移来创建此 table,但出现错误:
Caused by: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DELIMITER $$ CREATE FUNCTION autoInc () RETURNS INT(10) BEGIN DECLARE getCount I' at line 1
...
也许,我做错了什么?
您的方法存在各种问题。
首先,DELIMITER
是一个built-in command of the mysql client, it is not recognized by the MySQL Server's SQL parser. Also, I wonder why you are not using https://docs.liquibase.com/change-types/pro/create-function.html
其次,你模拟二次自增的方法有问题。它会遇到竞争条件,因为如果多个并发事务正在插入一行,它们都会从您的 SELECT COUNT(*) 查询中获得相同的结果,因此它们都会尝试插入相同的值user_id.
MySQL 中真正的自动增量机制在事务范围之外工作,因此确保多个会话在每个 table 中读取相同的增量值。还有一个简短的 table 锁定,同时每个会话都会增加 table 的计数器。
为了使您的函数免受竞争条件的影响,您必须锁定 table,以确保一次只有一个会话可以读取该值。
第三,如果你要计算下一个更高的值,自动增量应该基于MAX(USER_ID)
,而不是COUNT(USER_ID)
。如果您要从 table 中删除几行,那么 COUNT(USER_ID)
将生成一个低于最大值的值。
最后,我无法理解您为什么要为订单 table 设置 user_id 自动递增。您真的希望 每个 订单都生成一个新的 user_id 吗? user_id 不应该引用当前存在的用户吗?
databaseChangeLog:
- changeSet:
id: ...
author: ...
preConditions:
- onError: MARK_RAN
- not:
- tableExists:
tableName: ORDERS
changes:
- createTable:
tableName: ORDERS
columns:
- column:
name: ID
type: INT
autoIncrement: true
constraints:
primaryKey: true
- column:
name: ID_USER
type: INT
...
- sql:
"DELIMITER $$
CREATE FUNCTION autoInc ()
RETURNS INT(10)
BEGIN
DECLARE getCount INT(10);
SET getCount = (
SELECT COUNT(USER_ID)
FROM ORDERS) + 1;
RETURN getCount;
END$$
DELIMITER ;"
我正在使用这种方法。当我在订单中插入新条目时,它也应该增加 user_id。因为我需要 user_id 作为另一个自动递增列。我正在使用 Liquibase 迁移来创建此 table,但出现错误:
Caused by: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DELIMITER $$ CREATE FUNCTION autoInc () RETURNS INT(10) BEGIN DECLARE getCount I' at line 1
...
也许,我做错了什么?
您的方法存在各种问题。
首先,DELIMITER
是一个built-in command of the mysql client, it is not recognized by the MySQL Server's SQL parser. Also, I wonder why you are not using https://docs.liquibase.com/change-types/pro/create-function.html
其次,你模拟二次自增的方法有问题。它会遇到竞争条件,因为如果多个并发事务正在插入一行,它们都会从您的 SELECT COUNT(*) 查询中获得相同的结果,因此它们都会尝试插入相同的值user_id.
MySQL 中真正的自动增量机制在事务范围之外工作,因此确保多个会话在每个 table 中读取相同的增量值。还有一个简短的 table 锁定,同时每个会话都会增加 table 的计数器。
为了使您的函数免受竞争条件的影响,您必须锁定 table,以确保一次只有一个会话可以读取该值。
第三,如果你要计算下一个更高的值,自动增量应该基于MAX(USER_ID)
,而不是COUNT(USER_ID)
。如果您要从 table 中删除几行,那么 COUNT(USER_ID)
将生成一个低于最大值的值。
最后,我无法理解您为什么要为订单 table 设置 user_id 自动递增。您真的希望 每个 订单都生成一个新的 user_id 吗? user_id 不应该引用当前存在的用户吗?