web-dev-qa-db-ja.com

GolangのSQLトランザクション内で一括アップサート

私はトランザクションを伴うgolangのsqlパッケージをいじくり回しており、各行の「挿入ごと」の往復通信なしで一括アップサートを行う方法を理解しようとしています。 ここの例 は、バルククエリがどのように行われるかを実際には示していません。

updateMoney, err := db.Prepare("INSERT INTO balance set money=?, id=? ON DUPLICATE UPDATE balance SET money=money+? WHERE id=?")
...
tx, err := db.Begin()
...
res, err := tx.Stmt(updateMoney).Exec(123.45, 1, 123.45, 1)
res, err := tx.Stmt(updateMoney).Exec(67.89, 2, 67.89, 2)
res, err := tx.Stmt(updateMoney).Exec(10.23, 3, 10.23, 3)
...
tx.Commit()

理想的には、準備されたクエリを取得し、同時に送信されるアップサートのリストを作成できますが、ここでは、実行ごとにデータベースから結果を取得します。これに対処する方法について何か提案はありますか?

編集:私の同僚が発見した このオープンチケット 問題を説明しています...トランザクションのコンテキスト内よりも、それはより大きな懸念事項のようです。

19
tbischel

使用しているドライバーによって異なりますが、一部のドライバー/データベースはトランザクションをまったくサポートしていません。

たとえば、 go-sql-driver/mysql はトランザクションを正常にサポートします。

コードは動作するはずですが、次のように少し変更することもできます。

tx, err := db.Begin()
...
stmt, err := tx.Prepare(`INSERT INTO balance set money=?, id=? ON DUPLICATE UPDATE balance SET money=money+? WHERE id=?`)
res, err := stmt.Exec(123.45, 1, 123.45, 1)
res, err := stmt.Exec(67.89, 2, 67.89, 2)
res, err := stmt.Exec(10.23, 3, 10.23, 3)
...
tx.Commit()

また、これについてもチェックしてください answer これは、トランザクションに関する多くの詳細を説明しています。

8
OneOfOne

したがって、アップサートごとに個別のクエリを実行することはできませんでしたが、単一のステートメントで一括アップサート用の有効なmysqlを記述しました...

INSERT INTO balance (id, money) VALUES (1, 123.45), (2, 67.89), (3, 10.23)
ON DUPLICATE KEY UPDATE money = money + values(money)

したがって、大量のアップサートを実行したい人のために、mysqlドライバーの制限を回避するパターンがあります。

3
tbischel