蓝天

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 即可。

posted on 2024-03-01 21:40  #蓝天  阅读(25)  评论(0编辑  收藏  举报

导航