日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > 数据库 >内容正文

数据库

go 获得 mysql 实际运行 SQL,Golang实践录:一个数据库迁移的代码记录

發(fā)布時(shí)間:2025/3/15 数据库 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 go 获得 mysql 实际运行 SQL,Golang实践录:一个数据库迁移的代码记录 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

實(shí)現(xiàn)一個(gè)數(shù)據(jù)庫(kù)遷移的案子。有些知識(shí)點(diǎn)值得記錄。

技術(shù)框架

github.com/go-xorm/xorm:數(shù)據(jù)庫(kù)操作

github.com/denisenkom/go-mssqldb:sqlserver驅(qū)動(dòng)

github.com/go-sql-driver/mysql:mysql驅(qū)動(dòng)

方案設(shè)計(jì)

使用 sql 語(yǔ)句查詢?cè)瓟?shù)據(jù)庫(kù)數(shù)據(jù),再插入新數(shù)據(jù)庫(kù)。

查詢新數(shù)據(jù)庫(kù)表最后一條記錄。

根據(jù)條件是否創(chuàng)建新數(shù)據(jù)表,再查詢新數(shù)據(jù)庫(kù)最后一條記錄的 ID 值,以此為起點(diǎn)查詢舊數(shù)據(jù)庫(kù),因?yàn)檫w移只需要從已導(dǎo)入的最后一條開始即可,如果表不存在,則從 0 開始。

使用回調(diào)函數(shù),獲取舊數(shù)據(jù)庫(kù),處理數(shù)據(jù)(或舍棄,或修改,等),再插入到新數(shù)據(jù)庫(kù),直接用 xorm 的結(jié)構(gòu)體即可。xorm 可批量插入,但是舊數(shù)據(jù)庫(kù)無(wú)法做到批量獲取,查詢并存儲(chǔ)到切片中,到達(dá)一定數(shù)量(如3000條),再插入新數(shù)據(jù)庫(kù)。

選擇記錄

原數(shù)據(jù)庫(kù)為 sqlserver,表和列部分有中文,不符合 xorm 要求。只能使用 sql 語(yǔ)句操作。

新數(shù)據(jù)庫(kù)為 mysql,全英文,可用 xorm 結(jié)構(gòu)體映射,查詢、插入較方便。

試過將 xorm 的結(jié)構(gòu)體成員變量改為中文,在 Golang 中使用中文作為變量名是可以的,只是反射不成功。

實(shí)踐過程及知識(shí)點(diǎn)

數(shù)據(jù)庫(kù)連接

// mysql

root:root@tcp(8.8.8.8:3305)/mydb?charset=utf8&interpolateParams=true&parseTime=true&loc=Local

// mssql

server=8.8.8.8;user id=latelee;password=123456;database=mydb;encrypt=disable;

注意,使用github.com/denisenkom/go-mssqldb時(shí),需要添加encrypt=disable;,否則加不上。

xorm小知識(shí)

設(shè)置時(shí)區(qū):

engine.DatabaseTZ = time.UTC // time.Local

engine.TZLocation = time.UTC

是否顯示 sql 語(yǔ)句:

//engine.ShowSQL(true)

engine.ShowSQL(false)

xorm結(jié)構(gòu)體映射

xorm 使用結(jié)構(gòu)體與數(shù)據(jù)庫(kù)字段映射,各種操作十分方便。

設(shè)置完全映射:

engine.SetMapper(core.SameMapper{})

定義示例:

type TheData struct {

Id int `xorm:"int pk not null autoincr 'id'"` // autoincr

Money sql.NullFloat64 `xorm:"float default null"`

}

第一成員帶 id,在數(shù)據(jù)庫(kù)即為此名,第二成員不帶,將映射為 Money。

結(jié)構(gòu)體成員需大寫,否則反射失敗。

新數(shù)據(jù)庫(kù)創(chuàng)建

一般創(chuàng)建表使用其它的方法,但為了方便使用,直接在代碼中創(chuàng)建。

var sqlstr string

//sqlstr = "show tables like 'xxx'"

sqlstr = fmt.Sprintf("show tables like '%s'", newTableName)

_, err = engine.SQL(sqlstr).Count()

if err != nil { // 沒有找到現(xiàn)成函數(shù)判斷,用返回值

log.Printf("table %v exist\n", newTableName)

} else {

log.Printf("create table for %v\n", newTableName)

err = engine.Sync2(vnewtable)

if err != nil {

log.Println("create table failed: ", err.Error(), "will exit")

return

}

}

代碼先判斷是否存在數(shù)據(jù)表,不存在再調(diào)用engine.Sync2創(chuàng)建,該函數(shù)可同時(shí)創(chuàng)建多個(gè)數(shù)據(jù)表。不過似乎沒有直接的 API 接口判斷是否存在數(shù)據(jù)表,只好用 sql 語(yǔ)句查詢,判斷其返回值。

數(shù)據(jù)庫(kù)為空值

如果讀取到數(shù)據(jù)庫(kù)中的空值,會(huì)返回錯(cuò)誤:

Scan failed: sql: Scan error on column index 26, name "測(cè)試空值": converting driver.Value type string ("NULL") to a float32: invalid syntax

此問題可用 sql 包提供的類型解決。如sql.NullString、sql.NullFloat64等。這些類型實(shí)際是結(jié)構(gòu)體,包括了Valid成員,通過該值可判斷。

代碼重用

由于遷移過程是完全一樣的,只是數(shù)據(jù)庫(kù)、表名、字段等不同。可以將共用部分抽象成函數(shù),具體操作使用回調(diào)函數(shù)解決。

至于不同之處,則由調(diào)用者將其傳遞到共用函數(shù)中即可。

回調(diào)函數(shù)定義和使用

type DataCb func (rows *sql.Rows, engine *xorm.Engine, totalCnt int64) int

func MigrateDB(olddb, oldtable, newdb string, vnewtable interface{}, datacb DataCb) {

...

datacb(rows, engin, cnt)

}

在回調(diào)函數(shù)中,根據(jù)舊數(shù)據(jù)庫(kù)字段掃描,示例如:

for rows.Next() {

err := rows.Scan(&a, &b, &c, &d)

}

注意,Scan 參數(shù)個(gè)數(shù)和數(shù)據(jù)庫(kù)的列數(shù)相同,否則出錯(cuò)。可直接用新數(shù)據(jù)庫(kù)結(jié)構(gòu)體成員,如果不需要,可以使用其它變量。

結(jié)構(gòu)體映射

結(jié)構(gòu)體用指針傳遞,這樣可進(jìn)行通用處理,即 MigrateDB 不再與具體結(jié)構(gòu)體關(guān)聯(lián)。

// 獲取新表結(jié)構(gòu)體名稱

atype := reflect.TypeOf(vnewtable)

if atype.Kind() == reflect.Ptr { // 如果是結(jié)構(gòu)體指針,再獲取結(jié)構(gòu)體

atype = atype.Elem()

}

// 如果還不是結(jié)構(gòu)體,出錯(cuò)

if atype.Kind() != reflect.Struct {

log.Println("Check type error not Struct")

return

}

newTableName := atype.Name()

// 取第N個(gè)字段及值

idx := 0

var newIDName string = atype.Field(idx).Name// 此處為ID值,如果獲取不到tag,選擇變量名

// 查找單引號(hào),得到tag

xormStr := atype.Field(idx).Tag.Get("xorm")

idx1 := strings.Index(xormStr, "'")

idx2 := strings.LastIndex(xormStr, "'")

if idx1 != -1 && idx2 != -1 && idx1 < idx2 {

newIDName = xormStr[idx1+1:idx2]

}

log.Println("debug new table: ", newTableName, newIDName)

功能有二:獲取結(jié)構(gòu)體名稱,因?yàn)?sql 語(yǔ)句需要使用該名稱。獲取第 0 個(gè)字段名稱,結(jié)合實(shí)際情況,數(shù)據(jù)庫(kù)第 0 個(gè)字段為 ID,故設(shè)計(jì)上,將結(jié)構(gòu)體第 0 個(gè)字段置為 ID。

注意,這里使用結(jié)構(gòu)體指針形式傳遞,不需要額外聲明結(jié)構(gòu)體變量,代碼較整潔。

總結(jié)

以上是生活随笔為你收集整理的go 获得 mysql 实际运行 SQL,Golang实践录:一个数据库迁移的代码记录的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。