Go 言語 dbr で BULK INSERT

More than 3 years have passed since last update.

Go 言語の O/R Mapper dbr を使った Bulk INSERT の例です。特に目新しいものではないですが、パッケージの用法紹介ということで記しておきます。

コード全文は末尾に掲載します。dbr 自体の解説は拙著ですが dbr – Go 言語 O/R Mapper の紹介 に詳しいです。


INSERT したいデータ


bulkInsert-1.go

type Member struct {

Number int64 `db:"number"`
Name string `db:"name"`
}

members := []Member{}
members = append(members, Member{10, "Lionel"})
members = append(members, Member{9, "Luis"})

fmt.Println(members) // [{101 Lionel true} {102 Luis false}]



Query の組み立て

上記のような構造の length = 2 のデータを Bulk INSERT 対象とします。一度に Bulk INSERT のための Query を組み立てることはできないので1 以下のようにします。


bulkInsert-2.go

// ここでは カラムの指定まで Query を組み立てます

stmt := sess.InsertInto("member").
Columns("number", "name")

// スライスの分だけ Record() を実行します
for _, value := range members {
stmt.Record(value)
}


これでもう終わったようなものですが、for 文で Record(value) を回しています。期待通りの Query になっているか確認します。


bulkInsert-3.go

buf := dbr.NewBuffer()

stmt.Build(dialect.MySQL, buf)
fmt.Println(buf.String()) // INSERT INTO `member` (`number`,`name`) VALUES (?,?), (?,?)
fmt.Println(buf.Value()) // [10 Lionel 9 Luis]

よさそうですね。


実行

実行します。


bulkInsert-4.go

result, err := stmt.Exec()

if err != nil {
fmt.Println(err)
} else {
count, _ := result.RowsAffected()
fmt.Println(count) // 2
}

2レコード分 INSERT できました。

(これはぜひパッケージ側でよしなにやってほしいですね。InsertInto() 自体がいまひとつなのでそこも含めて改善に貢献したひ)


ややはまりポイント

stmt をつくるさいに dbr パッケージから開始するとのちのちの Query 実行時に面倒なことになるので、Session 構造体経由で生成してください。

// これでも途中までは進むけど、Exec() できないのでよくない

stmt := dbr.InsertInto("member").
Columns("number", "name")

// これでOK
conn, _ := dbr.Open("mysql", USER+":"+PASSWORD+"@tcp("+HOST+":"+PORT+")/"+DB, nil)
sess := conn.NewSession(nil)

stmt := sess.InsertInto("member").
Columns("number", "name")
//


コード全文


bulkInsert.go

package main

import (
"fmt"
_ "github.com/go-sql-driver/mysql"
"github.com/gocraft/dbr"
"github.com/gocraft/dbr/dialect"
)

const (
// Database
USER string = "root"
PASSWORD string = "mysql01"
DB string = "dev"
HOST string = "172.16.1.168"
PORT string = "3306"
)

func main() {

type Member struct {
Number int64 `db:"number"`
Name string `db:"name"`
}

members := []Member{}
members = append(members, Member{10, "Lionel"})
members = append(members, Member{9, "Luis"})

fmt.Println(members) // [{101 Lionel true} {102 Luis false}]
// ここまではただの準備

conn, _ := dbr.Open("mysql", USER+":"+PASSWORD+"@tcp("+HOST+":"+PORT+")/"+DB, nil)
sess := conn.NewSession(nil)

// ここでは カラムの指定まで Query を組み立てます
stmt := sess.InsertInto("member").
Columns("number", "name")

// スライスの分だけ Record() を実行します
for _, value := range members {
stmt.Record(value)
}

// 組み立てた Query を確認してみます
buf := dbr.NewBuffer()
stmt.Build(dialect.MySQL, buf)
fmt.Println(buf.String()) // INSERT INTO `member` (`number`,`name`) VALUES (?,?), (?,?)
fmt.Println(buf.Value()) // [10 Lionel 9 Luis]

// Query を実行します
result, err := stmt.Exec()
if err != nil {
fmt.Println(err)
} else {
count, _ := result.RowsAffected()
fmt.Println(count) // 2
}
}


以上です。





  1. 一応、、対象データの length が固定かつ非常に小さいなら .Record(members[0]).Record(members[1]) とか...