|
@@ -0,0 +1,879 @@
|
|
|
+package sqlite_test
|
|
|
+
|
|
|
+import (
|
|
|
+ "database/sql"
|
|
|
+ "encoding/json"
|
|
|
+ "fmt"
|
|
|
+ "math/rand"
|
|
|
+ "reflect"
|
|
|
+ "runtime"
|
|
|
+ "strconv"
|
|
|
+ "strings"
|
|
|
+ "testing"
|
|
|
+ "time"
|
|
|
+
|
|
|
+ "github.com/spf13/cast"
|
|
|
+)
|
|
|
+
|
|
|
+func TestSQLiteBasic(t *testing.T) {
|
|
|
+ // sqlite 内存表结构中字段类型只有 int64 float64 string, string没有长度限制
|
|
|
+ // create table 中指定的类型没有意义,不影响数据插入
|
|
|
+ // 实际数据类型是 由 第一次插入的数据的类型 决定的
|
|
|
+ // 查询时,后续数据会强制转换为该类型,转不过去会报错,如将非数字字符串转换为数值
|
|
|
+ mdb, err := sql.Open("sqlite", ":memory:")
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ stmt, err := mdb.Prepare(`CREATE TABLE TEST(
|
|
|
+ ID INT PRIMARY KEY NOT NULL,
|
|
|
+ NAME TEXT NOT NULL,
|
|
|
+ VALUE REAL NOT NULL,
|
|
|
+ INTVALUE INT NOT NULL,
|
|
|
+ DESC CHAR(1)
|
|
|
+ )`)
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ rslt, err := stmt.Exec()
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ println("rslt", fmt.Sprint(rslt.LastInsertId()), fmt.Sprint(rslt.RowsAffected()))
|
|
|
+ stmt, err = mdb.Prepare("insert into test(id,name,intvalue,value,desc) values(?,?,?,?,?)")
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ for i := 1; i < 10; i++ {
|
|
|
+ id := string([]byte{})
|
|
|
+ for n := 0; n < 16; n++ {
|
|
|
+ id = id + string('a'+byte(n+i-1))
|
|
|
+ }
|
|
|
+ name := int64(30 + i)
|
|
|
+ floatvalue := int64(12345)
|
|
|
+ var intvalue interface{}
|
|
|
+ intvalue = 1.2345 * float32(i)
|
|
|
+ if i%3 == 0 {
|
|
|
+ intvalue = "--12345--"
|
|
|
+ } else if i%3 == 2 {
|
|
|
+ intvalue = 123450 + i
|
|
|
+ }
|
|
|
+ desc := string("........................................................................................")
|
|
|
+ rslt, err = stmt.Exec(id, name, intvalue, floatvalue, desc)
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ }
|
|
|
+ println("rslt", fmt.Sprint(rslt.LastInsertId()), fmt.Sprint(rslt.RowsAffected()))
|
|
|
+ }
|
|
|
+ stmt, err = mdb.Prepare("select * from test")
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ rows, err := stmt.Query()
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ defer func() { _ = rows.Close() }()
|
|
|
+ cols, err := rows.Columns()
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ colTypes, err := rows.ColumnTypes()
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ types := make([]reflect.Type, len(colTypes))
|
|
|
+ for i, tp := range colTypes {
|
|
|
+ st := tp.ScanType()
|
|
|
+ if st == nil {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ types[i] = st
|
|
|
+ }
|
|
|
+ for rows.Next() {
|
|
|
+ values := make([]interface{}, len(cols))
|
|
|
+ for i := range values {
|
|
|
+ values[i] = reflect.New(types[i]).Interface()
|
|
|
+ }
|
|
|
+ if err = rows.Scan(values...); err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ m := make(map[string]interface{})
|
|
|
+ for i, v := range values {
|
|
|
+ if v == nil {
|
|
|
+ //values[i] = nil
|
|
|
+ m[colTypes[i].Name()] = nil
|
|
|
+ } else {
|
|
|
+ n, ok := colTypes[i].Length()
|
|
|
+ m[colTypes[i].Name()] = fmt.Sprint(types[i], "[", n, "]", ok, ":", reflect.ValueOf(v).Elem().Interface())
|
|
|
+ }
|
|
|
+ }
|
|
|
+ bs, _ := json.MarshalIndent(m, "", " ")
|
|
|
+ println("values", string(bs))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestSQLiteProfmance(t *testing.T) {
|
|
|
+ runtime.GOMAXPROCS(1)
|
|
|
+ println("GOMAXPROCS ", runtime.GOMAXPROCS(0))
|
|
|
+ varfieldscount := 100
|
|
|
+ println("Fields count ", varfieldscount+4)
|
|
|
+ emptyfieldscount := 100
|
|
|
+ println("Empty Fields count ", emptyfieldscount)
|
|
|
+ vardatalength := 32
|
|
|
+ println("VarFields data length ", vardatalength)
|
|
|
+ datacount := 10000
|
|
|
+ println("Data count ", datacount)
|
|
|
+ st := time.Now()
|
|
|
+ mdb, err := sql.Open("sqlite", ":memory:")
|
|
|
+ // mdb, err := sql.Open("sqlite", "file::memory:?cache=shared")
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ }
|
|
|
+ fields := "id,name,value,desc"
|
|
|
+ askmarks := "?,?,?,?"
|
|
|
+ for i := 1; i <= varfieldscount; i++ {
|
|
|
+ fields += ",var" + strconv.Itoa(i)
|
|
|
+ askmarks += ",?"
|
|
|
+ }
|
|
|
+ allfields := fields
|
|
|
+ for i := varfieldscount + 1; i <= varfieldscount+emptyfieldscount; i++ {
|
|
|
+ allfields += ",var" + strconv.Itoa(i)
|
|
|
+ }
|
|
|
+ stmt, err := mdb.Prepare("create table if not exists test(" + allfields + ")")
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ rslt, err := stmt.Exec()
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if rslt == nil {
|
|
|
+ println("sqlite init table return nil")
|
|
|
+ } else {
|
|
|
+ id, err := rslt.LastInsertId()
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ ra, err := rslt.RowsAffected()
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if id != 0 || ra != 0 {
|
|
|
+ println("sqlite init table return ", id, ra)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ println("sqlite create table use time ", time.Since(st).String())
|
|
|
+ st = time.Now()
|
|
|
+ stmt, err = mdb.Prepare("create index test_id_1 on test (id)")
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ rslt, err = stmt.Exec()
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if rslt == nil {
|
|
|
+ println("sqlite init table index return nil")
|
|
|
+ } else {
|
|
|
+ id, err := rslt.LastInsertId()
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ ra, err := rslt.RowsAffected()
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if id != 0 || ra != 0 {
|
|
|
+ println("sqlite init table index return ", id, ra)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ println("sqlite create index use time ", time.Since(st).String())
|
|
|
+ st = time.Now()
|
|
|
+ data := []map[string]interface{}{}
|
|
|
+ for i := 1; i <= datacount; i++ {
|
|
|
+ m := map[string]interface{}{}
|
|
|
+ m["id"] = i
|
|
|
+ m["name"] = fmt.Sprint("name", i)
|
|
|
+ m["value"] = 1.23456789 * float64(i)
|
|
|
+ m["desc"] = strings.Repeat(fmt.Sprint("desc", i, "."), 10)
|
|
|
+ for vi := 1; vi <= varfieldscount; vi++ {
|
|
|
+ bs := make([]byte, vardatalength+rand.Intn(10)-5)
|
|
|
+ for bsi := 0; bsi < len(bs); bsi++ {
|
|
|
+ bs[bsi] = byte(32 + rand.Intn(95))
|
|
|
+ }
|
|
|
+ m["var"+strconv.Itoa(vi)] = string(bs)
|
|
|
+ }
|
|
|
+ data = append(data, m)
|
|
|
+ // if i%10000 == 0 {
|
|
|
+ // println("generate maps ", i, " use time ", time.Since(st).String())
|
|
|
+ // }
|
|
|
+ }
|
|
|
+ println("generate maps ", datacount, " use time ", time.Since(st).String())
|
|
|
+ st = time.Now()
|
|
|
+ {
|
|
|
+ mdb.Exec("PRAGMA synchronous = OFF") // 这个跟写文件相关,内存模式没用
|
|
|
+ mdb.Exec("begin")
|
|
|
+ stmt, err = mdb.Prepare("insert into test(" + fields + ") values(" + askmarks + ")")
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, d := range data {
|
|
|
+ args := []interface{}{
|
|
|
+ d["id"],
|
|
|
+ d["name"],
|
|
|
+ d["value"],
|
|
|
+ d["desc"]}
|
|
|
+ for vi := 1; vi <= varfieldscount; vi++ {
|
|
|
+ args = append(args, d["var"+strconv.Itoa(vi)])
|
|
|
+ }
|
|
|
+ rslt, err = stmt.Exec(args...)
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ }
|
|
|
+ // println("rslt", fmt.Sprint(rslt.LastInsertId()), fmt.Sprint(rslt.RowsAffected()))
|
|
|
+ // if (i+1)%10000 == 0 {
|
|
|
+ // println("sqlite insert ", (i + 1), " use time ", time.Since(st).String())
|
|
|
+ // }
|
|
|
+ }
|
|
|
+ mdb.Exec("commit")
|
|
|
+ mdb.Exec("PRAGMA synchronous = NORMAL") // 这个跟写文件相关,内存模式没用
|
|
|
+ println("sqlite insert with transaction ", len(data), " use time ", time.Since(st).String())
|
|
|
+ }
|
|
|
+ st = time.Now()
|
|
|
+ tx, err := mdb.Begin()
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ stmt, err = tx.Prepare("insert into test(" + fields + ") values(" + askmarks + ")")
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, d := range data {
|
|
|
+ args := []interface{}{
|
|
|
+ d["id"],
|
|
|
+ d["name"],
|
|
|
+ d["value"],
|
|
|
+ d["desc"]}
|
|
|
+ for vi := 1; vi <= varfieldscount; vi++ {
|
|
|
+ args = append(args, d["var"+strconv.Itoa(vi)])
|
|
|
+ }
|
|
|
+ rslt, err = stmt.Exec(args...)
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ }
|
|
|
+ // println("rslt", fmt.Sprint(rslt.LastInsertId()), fmt.Sprint(rslt.RowsAffected()))
|
|
|
+ // if (i+1)%10000 == 0 {
|
|
|
+ // println("sqlite insert ", (i + 1), " use time ", time.Since(st).String())
|
|
|
+ // }
|
|
|
+ }
|
|
|
+ tx.Commit()
|
|
|
+ println("sqlite insert with transaction ", len(data), " use time ", time.Since(st).String())
|
|
|
+ st = time.Now()
|
|
|
+ stmt, err = mdb.Prepare("insert into test(" + fields + ") values(" + askmarks + ")")
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, d := range data {
|
|
|
+ args := []interface{}{
|
|
|
+ d["id"],
|
|
|
+ d["name"],
|
|
|
+ d["value"],
|
|
|
+ d["desc"]}
|
|
|
+ for vi := 1; vi <= varfieldscount; vi++ {
|
|
|
+ args = append(args, d["var"+strconv.Itoa(vi)])
|
|
|
+ }
|
|
|
+ rslt, err = stmt.Exec(args...)
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ }
|
|
|
+ // println("rslt", fmt.Sprint(rslt.LastInsertId()), fmt.Sprint(rslt.RowsAffected()))
|
|
|
+ // if (i+1)%10000 == 0 {
|
|
|
+ // println("sqlite insert ", (i + 1), " use time ", time.Since(st).String())
|
|
|
+ // }
|
|
|
+ }
|
|
|
+ println("sqlite insert immediately prepare ", len(data), " use time ", time.Since(st).String())
|
|
|
+
|
|
|
+ st = time.Now()
|
|
|
+
|
|
|
+ for _, d := range data {
|
|
|
+ args := []interface{}{
|
|
|
+ d["id"],
|
|
|
+ d["name"],
|
|
|
+ d["value"],
|
|
|
+ d["desc"]}
|
|
|
+ for vi := 1; vi <= varfieldscount; vi++ {
|
|
|
+ args = append(args, d["var"+strconv.Itoa(vi)])
|
|
|
+ }
|
|
|
+ sql := "insert into test(" + fields + ") values("
|
|
|
+ for i, arg := range args {
|
|
|
+ if i > 0 {
|
|
|
+ sql += ","
|
|
|
+ }
|
|
|
+ switch a := arg.(type) {
|
|
|
+ case string:
|
|
|
+ sql += "'" + strings.ReplaceAll(a, "'", "''") + "'"
|
|
|
+ default:
|
|
|
+ sql += fmt.Sprint(arg)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ sql += ")"
|
|
|
+ rslt, err = mdb.Exec(sql)
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ }
|
|
|
+ // println("rslt", fmt.Sprint(rslt.LastInsertId()), fmt.Sprint(rslt.RowsAffected()))
|
|
|
+ // if (i+1)%10000 == 0 {
|
|
|
+ // println("sqlite insert ", (i + 1), " use time ", time.Since(st).String())
|
|
|
+ // }
|
|
|
+ }
|
|
|
+ println("sqlite insert immediately ", len(data), " use time ", time.Since(st).String())
|
|
|
+ st = time.Now()
|
|
|
+ stmt, err = mdb.Prepare("select * from test")
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ rows, err := stmt.Query()
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ println("sqlite select query all", " use time ", time.Since(st).String())
|
|
|
+ defer func() { _ = rows.Close() }()
|
|
|
+ cols, err := rows.Columns()
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ colTypes, err := rows.ColumnTypes()
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ types := make([]reflect.Type, len(colTypes))
|
|
|
+ for i, tp := range colTypes {
|
|
|
+ st := tp.ScanType()
|
|
|
+ if st == nil {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ types[i] = st
|
|
|
+ }
|
|
|
+ data = []map[string]interface{}{}
|
|
|
+ for rows.Next() {
|
|
|
+ values := make([]interface{}, len(cols))
|
|
|
+ for i := range values {
|
|
|
+ if types[i] == nil {
|
|
|
+ values[i] = &[]byte{}
|
|
|
+ } else {
|
|
|
+ values[i] = reflect.New(types[i]).Interface()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if err = rows.Scan(values...); err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ m := make(map[string]interface{})
|
|
|
+ for i, v := range values {
|
|
|
+ if v == nil {
|
|
|
+ //values[i] = nil
|
|
|
+ m[colTypes[i].Name()] = nil
|
|
|
+ } else {
|
|
|
+ n, ok := colTypes[i].Length()
|
|
|
+ m[colTypes[i].Name()] = fmt.Sprint(types[i], "[", n, "]", ok, ":", reflect.ValueOf(v).Elem().Interface())
|
|
|
+ }
|
|
|
+ }
|
|
|
+ data = append(data, m)
|
|
|
+ // if len(data)%10000 == 0 {
|
|
|
+ // println("sqlite fetch data ", len(data), " use time ", time.Since(st).String())
|
|
|
+ // }
|
|
|
+ }
|
|
|
+ println("sqlite fetch data ", len(data), " use time ", time.Since(st).String())
|
|
|
+ st = time.Now()
|
|
|
+ memdataset := map[string]map[string]interface{}{}
|
|
|
+ for _, d := range data {
|
|
|
+ id := cast.ToString(d["id"])
|
|
|
+ memdataset[id] = d
|
|
|
+ }
|
|
|
+ println("put id-data maps ", len(memdataset), " use time ", time.Since(st).String())
|
|
|
+ st = time.Now()
|
|
|
+ ids := []string{}
|
|
|
+ for id, d := range memdataset {
|
|
|
+ if id == d["id"] {
|
|
|
+ ids = append(ids, id)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ println("fetch maps data ids ", len(ids), " use time ", time.Since(st).String())
|
|
|
+ st = time.Now()
|
|
|
+ data = []map[string]interface{}{}
|
|
|
+ for _, id := range ids {
|
|
|
+ d := memdataset[id]
|
|
|
+ data = append(data, d)
|
|
|
+ }
|
|
|
+ println("fetch maps data ", len(data), " use time ", time.Since(st).String())
|
|
|
+}
|
|
|
+
|
|
|
+func TestSQLiteJSON(t *testing.T) {
|
|
|
+ // sqlite 内存表结构中字段类型只有 int64 float64 string, string没有长度限制
|
|
|
+ // create table 中指定的类型没有意义,不影响数据插入
|
|
|
+ // 实际数据类型是 由 第一次插入的数据的类型 决定的
|
|
|
+ // 查询时,后续数据会强制转换为该类型,转不过去会报错,如将非数字字符串转换为数值
|
|
|
+ mdb, err := sql.Open("sqlite", ":memory:")
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ stmt, err := mdb.Prepare(`CREATE TABLE TEST(
|
|
|
+ ID INT PRIMARY KEY NOT NULL,
|
|
|
+ NAME TEXT NOT NULL,
|
|
|
+ VALUE REAL NOT NULL,
|
|
|
+ INTVALUE INT NOT NULL,
|
|
|
+ DESC CHAR(1)
|
|
|
+ )`)
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ rslt, err := stmt.Exec()
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ println("rslt", fmt.Sprint(rslt.LastInsertId()), fmt.Sprint(rslt.RowsAffected()))
|
|
|
+ stmt, err = mdb.Prepare("insert into test(id,name,intvalue,value,desc) values(?,?,?,?,?)")
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ for i := 1; i < 10; i++ {
|
|
|
+ id := string([]byte{})
|
|
|
+ for n := 0; n < 16; n++ {
|
|
|
+ id = id + string('a'+byte(n+i-1))
|
|
|
+ }
|
|
|
+ name := int64(30 + i)
|
|
|
+ floatvalue := 12345 + (i-1)*10000
|
|
|
+ var intvalue interface{}
|
|
|
+ intvalue = 1.2345 * float32(i)
|
|
|
+ if i%3 == 1 {
|
|
|
+ intvalue = "--12345--"
|
|
|
+ } else if i%3 == 2 {
|
|
|
+ intvalue = 123450 + i
|
|
|
+ }
|
|
|
+ bs, _ := json.MarshalIndent(map[string]interface{}{
|
|
|
+ "value": floatvalue,
|
|
|
+ }, "", " ")
|
|
|
+ desc := string(bs)
|
|
|
+ rslt, err = stmt.Exec(id, name, intvalue, floatvalue, desc)
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ }
|
|
|
+ println("rslt", fmt.Sprint(rslt.LastInsertId()), fmt.Sprint(rslt.RowsAffected()))
|
|
|
+ }
|
|
|
+ {
|
|
|
+ stmt, err = mdb.Prepare(`select * from test`)
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ rows, err := stmt.Query()
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ defer func() { _ = rows.Close() }()
|
|
|
+ cols, err := rows.Columns()
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ colTypes, err := rows.ColumnTypes()
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ types := make([]reflect.Type, len(colTypes))
|
|
|
+ for i, tp := range colTypes {
|
|
|
+ st := tp.ScanType()
|
|
|
+ if st == nil {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ types[i] = st
|
|
|
+ }
|
|
|
+ for rows.Next() {
|
|
|
+ values := make([]interface{}, len(cols))
|
|
|
+ for i := range values {
|
|
|
+ values[i] = reflect.New(types[i]).Interface()
|
|
|
+ }
|
|
|
+ if err = rows.Scan(values...); err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ m := make(map[string]interface{})
|
|
|
+ for i, v := range values {
|
|
|
+ if v == nil {
|
|
|
+ //values[i] = nil
|
|
|
+ m[colTypes[i].Name()] = nil
|
|
|
+ } else {
|
|
|
+ n, ok := colTypes[i].Length()
|
|
|
+ m[colTypes[i].Name()] = fmt.Sprint(types[i], "[", n, "]", ok, ":", reflect.ValueOf(v).Elem().Interface())
|
|
|
+ }
|
|
|
+ }
|
|
|
+ bs, _ := json.MarshalIndent(m, "", " ")
|
|
|
+ println("values", string(bs))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ stmt, err = mdb.Prepare(`select json_group_array(json_object(
|
|
|
+ 'id', id,
|
|
|
+ 'name', name,
|
|
|
+ 'value', value)) as json
|
|
|
+ from test
|
|
|
+ where json_extract(desc, '$.value') > 20000 and json_extract(desc, '$.value') < 50000`)
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ rows, err := stmt.Query()
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ defer func() { _ = rows.Close() }()
|
|
|
+ cols, err := rows.Columns()
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ colTypes, err := rows.ColumnTypes()
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ types := make([]reflect.Type, len(colTypes))
|
|
|
+ for i, tp := range colTypes {
|
|
|
+ st := tp.ScanType()
|
|
|
+ if st == nil {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ types[i] = st
|
|
|
+ }
|
|
|
+ for rows.Next() {
|
|
|
+ values := make([]interface{}, len(cols))
|
|
|
+ for i := range values {
|
|
|
+ values[i] = reflect.New(types[i]).Interface()
|
|
|
+ }
|
|
|
+ if err = rows.Scan(values...); err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ m := make(map[string]interface{})
|
|
|
+ for i, v := range values {
|
|
|
+ if v == nil {
|
|
|
+ //values[i] = nil
|
|
|
+ m[colTypes[i].Name()] = nil
|
|
|
+ } else {
|
|
|
+ n, ok := colTypes[i].Length()
|
|
|
+ m[colTypes[i].Name()] = fmt.Sprint(types[i], "[", n, "]", ok, ":", reflect.ValueOf(v).Elem().Interface())
|
|
|
+ }
|
|
|
+ }
|
|
|
+ bs, _ := json.MarshalIndent(m, "", " ")
|
|
|
+ println("values", string(bs))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestSQLiteProfmanceMultiTable(t *testing.T) {
|
|
|
+ runtime.GOMAXPROCS(4)
|
|
|
+ println("GOMAXPROCS ", runtime.GOMAXPROCS(0))
|
|
|
+ tablecount := 10000
|
|
|
+ println("Tables count ", tablecount)
|
|
|
+ varfieldscount := 100
|
|
|
+ println("VarFields count ", varfieldscount+4)
|
|
|
+ emptyfieldscount := 100
|
|
|
+ println("EmptyFields count ", emptyfieldscount)
|
|
|
+ vardatalength := 32
|
|
|
+ println("VarFields data length ", vardatalength)
|
|
|
+ tableindexcount := 100
|
|
|
+ println("TableIndex count ", tableindexcount)
|
|
|
+ datacount := 10000
|
|
|
+ println("Data count ", datacount)
|
|
|
+ mdb, err := sql.Open("sqlite", ":memory:")
|
|
|
+ // mdb, err := sql.Open("sqlite", "file::memory:?cache=shared")
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ }
|
|
|
+ st := time.Now()
|
|
|
+ for ti := 1; ti < tablecount; ti++ {
|
|
|
+ tablename := fmt.Sprint("test", ti)
|
|
|
+ fields := []string{"id", "name", "value", "desc"}
|
|
|
+ varfields := strings.Join(fields, ",")
|
|
|
+ askmarks := "?" + strings.Repeat(",?", len(fields)-1)
|
|
|
+ for i := 1; i <= varfieldscount; i++ {
|
|
|
+ fields = append(fields, "var"+strconv.Itoa(i))
|
|
|
+ varfields += ",var" + strconv.Itoa(i)
|
|
|
+ askmarks += ",?"
|
|
|
+ }
|
|
|
+ allfields := varfields
|
|
|
+ for i := varfieldscount + 1; i <= varfieldscount+emptyfieldscount; i++ {
|
|
|
+ fields = append(fields, "var"+strconv.Itoa(i))
|
|
|
+ allfields += ",var" + strconv.Itoa(i)
|
|
|
+ }
|
|
|
+ st := time.Now()
|
|
|
+ stmt, err := mdb.Prepare("create table if not exists " + tablename + "(" + allfields + ")")
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ rslt, err := stmt.Exec()
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if rslt == nil {
|
|
|
+ println("sqlite init table return nil")
|
|
|
+ } else {
|
|
|
+ id, err := rslt.LastInsertId()
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ ra, err := rslt.RowsAffected()
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if id != 0 || ra != 0 {
|
|
|
+ println("sqlite init table return ", id, ra)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ println("sqlite create table", tablename, "use time ", time.Since(st).String())
|
|
|
+ }
|
|
|
+ println("sqlite create table", tablecount, "done, use time ", time.Since(st).String())
|
|
|
+ st = time.Now()
|
|
|
+ for ti := 1; ti < tablecount; ti++ {
|
|
|
+ tablename := fmt.Sprint("test", ti)
|
|
|
+ fields := []string{"id", "name", "value", "desc"}
|
|
|
+ varfields := strings.Join(fields, ",")
|
|
|
+ askmarks := "?" + strings.Repeat(",?", len(fields)-1)
|
|
|
+ for i := 1; i <= varfieldscount; i++ {
|
|
|
+ fields = append(fields, "var"+strconv.Itoa(i))
|
|
|
+ varfields += ",var" + strconv.Itoa(i)
|
|
|
+ askmarks += ",?"
|
|
|
+ }
|
|
|
+ allfields := varfields
|
|
|
+ for i := varfieldscount + 1; i <= varfieldscount+emptyfieldscount; i++ {
|
|
|
+ fields = append(fields, "var"+strconv.Itoa(i))
|
|
|
+ allfields += ",var" + strconv.Itoa(i)
|
|
|
+ }
|
|
|
+ st = time.Now()
|
|
|
+ for i, field := range fields {
|
|
|
+ if i > tableindexcount {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ stmt, err := mdb.Prepare("create index " + tablename + "_" + field + " on " + tablename + " (" + field + ")")
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ rslt, err := stmt.Exec()
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if rslt == nil {
|
|
|
+ println("sqlite init table index return nil")
|
|
|
+ } else {
|
|
|
+ id, err := rslt.LastInsertId()
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ ra, err := rslt.RowsAffected()
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if id != 0 || ra != 0 {
|
|
|
+ println("sqlite init table index return ", id, ra)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ println("sqlite create index for table "+tablename+" use time ", time.Since(st).String())
|
|
|
+ }
|
|
|
+ println("sqlite create table index", tablecount, "done, use time ", time.Since(st).String())
|
|
|
+ st = time.Now()
|
|
|
+ data := []map[string]interface{}{}
|
|
|
+ {
|
|
|
+ st = time.Now()
|
|
|
+ for i := 1; i <= datacount; i++ {
|
|
|
+ m := map[string]interface{}{}
|
|
|
+ m["id"] = i
|
|
|
+ m["name"] = fmt.Sprint("name", i)
|
|
|
+ m["value"] = 1.23456789 * float64(i)
|
|
|
+ m["desc"] = strings.Repeat(fmt.Sprint("desc", i, "."), 10)
|
|
|
+ for vi := 1; vi <= varfieldscount; vi++ {
|
|
|
+ bs := make([]byte, vardatalength+rand.Intn(10)-5)
|
|
|
+ for bsi := 0; bsi < len(bs); bsi++ {
|
|
|
+ bs[bsi] = byte(32 + rand.Intn(95))
|
|
|
+ }
|
|
|
+ m["var"+strconv.Itoa(vi)] = string(bs)
|
|
|
+ }
|
|
|
+ data = append(data, m)
|
|
|
+ if i%1000 == 0 {
|
|
|
+ println("generate maps ", i, " use time ", time.Since(st).String())
|
|
|
+ }
|
|
|
+ }
|
|
|
+ println("generate maps ", datacount, " use time ", time.Since(st).String())
|
|
|
+ st = time.Now()
|
|
|
+ memdataset := map[string]map[string]interface{}{}
|
|
|
+ for _, d := range data {
|
|
|
+ id := cast.ToString(d["id"])
|
|
|
+ memdataset[id] = d
|
|
|
+ }
|
|
|
+ println("put id-data maps ", len(memdataset), " use time ", time.Since(st).String())
|
|
|
+ st = time.Now()
|
|
|
+ ids := []string{}
|
|
|
+ for id, d := range memdataset {
|
|
|
+ if id == cast.ToString(d["id"]) {
|
|
|
+ ids = append(ids, id)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ println("fetch maps data ids ", len(ids), " use time ", time.Since(st).String())
|
|
|
+ st = time.Now()
|
|
|
+ data = []map[string]interface{}{}
|
|
|
+ for _, id := range ids {
|
|
|
+ d := memdataset[id]
|
|
|
+ data = append(data, d)
|
|
|
+ }
|
|
|
+ println("fetch maps data ", len(data), " use time ", time.Since(st).String())
|
|
|
+ }
|
|
|
+ for ti := 1; ti < tablecount; ti++ {
|
|
|
+ tablename := fmt.Sprint("test", ti)
|
|
|
+ fields := []string{"id", "name", "value", "desc"}
|
|
|
+ varfields := strings.Join(fields, ",")
|
|
|
+ askmarks := "?" + strings.Repeat(",?", len(fields)-1)
|
|
|
+ for i := 1; i <= varfieldscount; i++ {
|
|
|
+ fields = append(fields, "var"+strconv.Itoa(i))
|
|
|
+ varfields += ",var" + strconv.Itoa(i)
|
|
|
+ askmarks += ",?"
|
|
|
+ }
|
|
|
+ allfields := varfields
|
|
|
+ for i := varfieldscount + 1; i <= varfieldscount+emptyfieldscount; i++ {
|
|
|
+ fields = append(fields, "var"+strconv.Itoa(i))
|
|
|
+ allfields += ",var" + strconv.Itoa(i)
|
|
|
+ }
|
|
|
+ { // insert
|
|
|
+ st = time.Now()
|
|
|
+ stmt, err := mdb.Prepare("insert into " + tablename + "(" + varfields + ") values(" + askmarks + ")")
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ i := 0
|
|
|
+ for _, d := range data {
|
|
|
+ i = i + 1
|
|
|
+ args := []interface{}{
|
|
|
+ d["id"],
|
|
|
+ d["name"],
|
|
|
+ d["value"],
|
|
|
+ d["desc"]}
|
|
|
+ for vi := 1; vi <= varfieldscount; vi++ {
|
|
|
+ args = append(args, d["var"+strconv.Itoa(vi)])
|
|
|
+ }
|
|
|
+ _, err := stmt.Exec(args...)
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ }
|
|
|
+ // println("rslt", fmt.Sprint(rslt.LastInsertId()), fmt.Sprint(rslt.RowsAffected()))
|
|
|
+ if i%1000 == 0 {
|
|
|
+ println("sqlite insert ", i, " use time ", time.Since(st).String())
|
|
|
+ }
|
|
|
+ }
|
|
|
+ println("sqlite insert ", tablename, len(data), " use time ", time.Since(st).String())
|
|
|
+ }
|
|
|
+ { // select
|
|
|
+ st = time.Now()
|
|
|
+ stmt, err := mdb.Prepare("select " + varfields + " from " + tablename)
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ rows, err := stmt.Query()
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ println("sqlite select query all", " use time ", time.Since(st).String())
|
|
|
+ defer func() { _ = rows.Close() }()
|
|
|
+ cols, err := rows.Columns()
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ colTypes, err := rows.ColumnTypes()
|
|
|
+ if err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ types := make([]reflect.Type, len(colTypes))
|
|
|
+ for i, tp := range colTypes {
|
|
|
+ st := tp.ScanType()
|
|
|
+ if st == nil {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ types[i] = st
|
|
|
+ }
|
|
|
+ data := []map[string]interface{}{}
|
|
|
+ for rows.Next() {
|
|
|
+ values := make([]interface{}, len(cols))
|
|
|
+ for i := range values {
|
|
|
+ if types[i] == nil {
|
|
|
+ values[i] = &[]byte{}
|
|
|
+ } else {
|
|
|
+ values[i] = reflect.New(types[i]).Interface()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if err = rows.Scan(values...); err != nil {
|
|
|
+ println("err", fmt.Sprint(err))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ m := make(map[string]interface{})
|
|
|
+ for i, v := range values {
|
|
|
+ if v == nil {
|
|
|
+ //values[i] = nil
|
|
|
+ m[colTypes[i].Name()] = nil
|
|
|
+ } else {
|
|
|
+ n, ok := colTypes[i].Length()
|
|
|
+ m[colTypes[i].Name()] = fmt.Sprint(types[i], "[", n, "]", ok, ":", reflect.ValueOf(v).Elem().Interface())
|
|
|
+ }
|
|
|
+ }
|
|
|
+ data = append(data, m)
|
|
|
+ // if len(data)%10000 == 0 {
|
|
|
+ // println("sqlite fetch data ", len(data), " use time ", time.Since(st).String())
|
|
|
+ // }
|
|
|
+ }
|
|
|
+ println("sqlite fetch data ", len(data), " use time ", time.Since(st).String())
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|