type InsertItem struct { Name string Age int Address string } var insert_content = []InsertItem{ {"taro",15,"tokyo"}, {"jiro",22,"osaka"}, }
を
ret, err := db.Exec(`INSERT INTO table名 (name,age,address) VALUES (?),(?)`,insert_content)
のような感じで一度のinsertでまとめてDBに入れたいのですがどのようにすれば良いのでしょうか?
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答2件
0
ベストアンサー
https://github.com/t-tiger/gorm-bulk-insert
のバルクインサート?を試してみた所速くinsertができましたのでこれを使用してみようと思いました。
投稿2020/01/26 13:54
退会済みユーザー
総合スコア0
0
いくらかバルクインサートの事例はありますが、あまりオススメできません。
パラメータとプレースホルダとの帳尻合わせしつつ、SQLを動的に組み立てることになります。その記述はトリッキーであり、SQL記述ミスを誘発しやすいです。
GoではPreparedしたひとつインサートだけのステートメントを繰り返し呼ぶ方が確実だし、この場合SQLのパースコストが下げられて性能も良いかもしれません。
僕が複数レコードインサートを実装する場合は以下のサンプルと同じ方法を採ります。
https://golang.org/pkg/database/sql/#Tx.Prepare
go
1package main 2 3import ( 4 "context" 5 "database/sql" 6 "log" 7) 8 9var ( 10 ctx context.Context 11 db *sql.DB 12) 13 14func main() { 15 projects := []struct { 16 mascot string 17 release int 18 }{ 19 {"tux", 1991}, 20 {"duke", 1996}, 21 {"gopher", 2009}, 22 {"moby dock", 2013}, 23 } 24 25 tx, err := db.Begin() 26 if err != nil { 27 log.Fatal(err) 28 } 29 defer tx.Rollback() // The rollback will be ignored if the tx has been committed later in the function. 30 31 stmt, err := tx.Prepare("INSERT INTO projects(id, mascot, release, category) VALUES( ?, ?, ?, ? )") 32 if err != nil { 33 log.Fatal(err) 34 } 35 defer stmt.Close() // Prepared statements take up server resources and should be closed after use. 36 37 for id, project := range projects { 38 if _, err := stmt.Exec(id+1, project.mascot, project.release, "open source"); err != nil { 39 log.Fatal(err) 40 } 41 } 42 if err := tx.Commit(); err != nil { 43 log.Fatal(err) 44 } 45}
この方法の良いところ
- SQLの調整がしやすい
- 既にある項目をフラットなスライスに並べ直すオーバーヘッドがない
- reflectを使わないので性能劣化が無い
- PrepareサポートなDBではかなり性能が向上する
- ナイーブなエラーのハンドリング実装を追加しやすい
- コネクションプールによる実装(Go標準)でPrepareステートメントの消費量が抑えられる
- 他の複雑なクエリにも同様の記述で対応できる(select&updateなど)
例えばMySQLではPrepareした性能向上効果は大きいのですが、
デフォルトで16K個のPrepareステートメントを作ろうとした時点でエラーになります。
SQLビルダーで一つのことに対しN個のPrepareステートメントを作ると
その数にコネクション数Mを掛けたNxM個のPrepareステートメントを作成してしまう可能性があります。
コネクション数やPrepareステートメント数の上限を調整するという考えは
データベースへのアクセス規模に応じてどのみち必要なのですが
わざわざこれらの問題を呼び寄せてしまうバルク操作は
「 Goが並列に強いからこそ、お勧めしません。 」
(LL言語実装の多くは並列にDBアクセスをしないのでこの問題は起きにくいです)
投稿2020/01/24 14:06
編集2020/01/27 03:44総合スコア3367
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/01/25 01:17
2020/01/25 01:23
2020/01/25 01:28
退会済みユーザー
2020/01/25 04:08
2020/01/27 01:07 編集
2020/01/27 01:16 編集
退会済みユーザー
2020/01/27 10:11
2020/01/27 14:01
退会済みユーザー
2020/01/27 16:43
2020/01/28 00:36
退会済みユーザー
2020/01/28 08:57
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。