go写mysql取得自增字段值
场景:有多张表,依据其中一张表的自增字段取得 id 值作为对象ID,然后使用这个Id插入到其他它表中。
如下一张 MySQL 的 innodb 表 X,用 go 编写程序,不指定 a 的值,指定 b 和 c 的值,往表 X 插入数据,如果数据已经存在则执行更新操作,成功后取得该笔插入或者更新的记录对应的 a 字段的值。
create table X (
a int unsigned AUTO_INCREMENT,
b int,
c int,
PRIMARY KEY (a),
UNIQUE KEY(b)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
非事务版本:
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
func main() {
db, err := sql.Open("mysql", "user:password@tcp(localhost:3306)/dbname")
if err != nil {
panic(err)
}
defer db.Close()
// 插入或更新记录
query := `INSERT INTO X (b, c) VALUES (?, ?)
ON DUPLICATE KEY UPDATE c = VALUES(c), a = LAST_INSERT_ID(a)`
result, err := db.Exec(query, "value1", "value2")
if err != nil {
panic(err)
}
// 获取自增ID
lastInsertID, err := result.LastInsertId()
if err != nil {
panic(err)
}
fmt.Printf("LastInsertID: %d\n", lastInsertID)
}
非事务版本不能保持 a 值的连续性。
事务版本:
package main
import (
"database/sql"
"fmt"
"sync"
_ "github.com/go-sql-driver/mysql"
)
func upsertRecord(db *sql.DB, b, c int) (int, error) {
// 开始事务
tx, err := db.Begin()
if err != nil {
return 0, err
}
defer tx.Rollback()
// 尝试查找记录
var id int
err = tx.QueryRow("SELECT a FROM X WHERE b = ?", b).Scan(&id)
if err != nil {
if err == sql.ErrNoRows {
// 记录不存在,插入新的记录
result, err := tx.Exec("INSERT INTO X (b, c) VALUES (?, ?)", b, c)
if err != nil {
return 0, err
}
lastInsertID, err := result.LastInsertId()
if err != nil {
return 0, err
}
id = int(lastInsertID)
} else {
// 其他错误
return 0, err
}
} else {
// 记录存在,更新记录
_, err = tx.Exec("UPDATE X SET c = ? WHERE a = ?", c, id)
if err != nil {
return 0, err
}
}
// 提交事务
err = tx.Commit()
if err != nil {
return 0, err
}
return id, nil
}
func main() {
db, err := sql.Open("mysql", "dswrite:ds#582701@tcp(9.134.177.242:3306)/dsdb")
if err != nil {
panic(err)
}
defer db.Close()
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
id, err := upsertRecord(db, 20+i, 100+i)
if err != nil {
fmt.Printf("Error: %v\n", err)
} else {
fmt.Printf("ID: %d\n", id)
}
}(i)
}
wg.Wait()
}
该版本可以保持 a 值的连续性。实际可以再优化,去掉 select,先 insert 或者 update 即可。