diff --git a/contrib/drivers/mysql/mysql_z_unit_core_bench_test.go b/contrib/drivers/mysql/mysql_z_unit_core_bench_test.go new file mode 100644 index 000000000..6c1b2fc53 --- /dev/null +++ b/contrib/drivers/mysql/mysql_z_unit_core_bench_test.go @@ -0,0 +1,46 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +// go test *.go -bench=".*" + +package mysql_test + +import ( + "testing" + + "github.com/gogf/gf/v2/os/gtime" + "github.com/gogf/gf/v2/util/grand" +) + +func Benchmark_BatchInsert(b *testing.B) { + table := createTable() + defer dropTable(table) + type User struct { + Id int `c:"id"` + Passport string `c:"passport"` + Password string `c:"password"` + NickName string `c:"nickname"` + CreateTime *gtime.Time `c:"create_time"` + } + var users []*User + for i := 0; i < 10000; i++ { + users = append(users, &User{ + Passport: grand.S(10), + Password: grand.S(10), + NickName: grand.S(10), + CreateTime: gtime.Now(), + }) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + result, err := db.Insert(ctx, table, users) + if err != nil { + b.Fatalf("insert error: %v", err) + } + n, _ := result.RowsAffected() + b.Logf("insert %d rows", n) + } +} diff --git a/contrib/drivers/mysql/mysql_z_unit_core_test.go b/contrib/drivers/mysql/mysql_z_unit_core_test.go index 604740606..b9334e445 100644 --- a/contrib/drivers/mysql/mysql_z_unit_core_test.go +++ b/contrib/drivers/mysql/mysql_z_unit_core_test.go @@ -466,6 +466,31 @@ func Test_DB_BatchInsert(t *testing.T) { n, _ := result.RowsAffected() t.Assert(n, 1) }) + + // Batch insert with different fields + gtest.C(t, func(t *gtest.T) { + table := createTable() + defer dropTable(table) + r, err := db.Insert(ctx, table, g.List{ + { + "id": 2, + "passport": "t2", + "password": "25d55ad283aa400af464c76d713c07ac", + "create_time": gtime.Now().String(), + }, + { + "id": 3, + "passport": "user_3", + "password": "25d55ad283aa400af464c76d713c07ad", + "nickname": "name_3", + "create_time": gtime.Now().String(), + }, + }, 1) + t.AssertNil(err) + n, err := r.RowsAffected() + t.AssertNil(err) + t.Assert(n, 2) + }) } func Test_DB_BatchInsert_Struct(t *testing.T) { diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 725935265..7520e42ab 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -12,6 +12,7 @@ import ( "database/sql" "fmt" "reflect" + "sort" "strings" "github.com/gogf/gf/v2/container/gmap" @@ -446,25 +447,30 @@ func (c *Core) DoInsert(ctx context.Context, link Link, table string, list List, // It here uses ListMap to keep sequence for data inserting. // ============================================================================================ var keyListMap = gmap.NewListMap() + var tmpkeyListMap = make(map[string]List) for _, item := range list { - var ( - tmpKeys = make([]string, 0) - tmpKeysInSequenceStr string - ) + mapLen := len(item) + if mapLen == 0 { + continue + } + tmpKeys := make([]string, 0, mapLen) for k := range item { tmpKeys = append(tmpKeys, k) } - keys, err = c.fieldsToSequence(ctx, table, tmpKeys) - if err != nil { - return nil, err + if mapLen > 1 { + sort.Strings(tmpKeys) } - tmpKeysInSequenceStr = gstr.Join(keys, ",") - if !keyListMap.Contains(tmpKeysInSequenceStr) { - keyListMap.Set(tmpKeysInSequenceStr, make(List, 0)) + keys = tmpKeys // for fieldsToSequence + + tmpKeysInSequenceStr := gstr.Join(tmpKeys, ",") + if tmpkeyListMapItem, ok := tmpkeyListMap[tmpKeysInSequenceStr]; ok { + tmpkeyListMap[tmpKeysInSequenceStr] = append(tmpkeyListMapItem, item) + } else { + tmpkeyListMap[tmpKeysInSequenceStr] = List{item} } - tmpKeysInSequenceList := keyListMap.Get(tmpKeysInSequenceStr).(List) - tmpKeysInSequenceList = append(tmpKeysInSequenceList, item) - keyListMap.Set(tmpKeysInSequenceStr, tmpKeysInSequenceList) + } + for tmpKeysInSequenceStr, itemList := range tmpkeyListMap { + keyListMap.Set(tmpKeysInSequenceStr, itemList) } if keyListMap.Size() > 1 { var ( @@ -488,6 +494,15 @@ func (c *Core) DoInsert(ctx context.Context, link Link, table string, list List, return &sqlResult, err } + keys, err = c.fieldsToSequence(ctx, table, keys) + if err != nil { + return nil, err + } + + if len(keys) == 0 { + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "no valid data fields found in table") + } + // Prepare the batch result pointer. var ( charL, charR = c.db.GetChars()