最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Go語言操作金倉數(shù)據(jù)庫之SQL執(zhí)行,類型映射與超時控制詳解

 更新時間:2026年05月13日 08:24:16   作者:碼農(nóng)阿豪@新空間  
本文詳細介紹了使用Go語言操作金倉數(shù)據(jù)庫的關(guān)鍵技術(shù)點,主要包括 SQL執(zhí)行,通過Query方法執(zhí)行查詢并處理結(jié)果集,使用Exec執(zhí)行非查詢操作,以及事務(wù)處理多條SQL語句的方法,有需要的小伙伴可以了解下

接上篇,環(huán)境搭好了,連接也配好了。這篇講真正干活的部分——怎么執(zhí)行 SQL、怎么處理結(jié)果、怎么用預(yù)備語句防止注入、怎么處理存儲過程的 OUT 參數(shù)。

一、執(zhí)行 SQL 語句

1.1 查詢操作(SELECT)

查詢用 Query 方法,返回 Rows 對象。這個對象會占用一個數(shù)據(jù)庫連接,用完必須 Close。

package main

import (
    "database/sql"
    "fmt"
    _ "kingbase.com/gokb"
)

// 定義結(jié)構(gòu)體接收查詢結(jié)果
type User struct {
    Id   int
    Name string
    Age  int
}

func main() {
    connStr := "host=127.0.0.1 user=system password=123456 dbname=TEST sslmode=disable"
    db, err := sql.Open("kingbase", connStr)
    if err != nil {
        panic(err)
    }
    defer db.Close()

    // 執(zhí)行查詢
    rows, err := db.Query("SELECT id, name, age FROM users WHERE age > $1", 18)
    if err != nil {
        panic(err)
    }
    defer rows.Close()  // 重要:用完關(guān)閉

    var users []User
    for rows.Next() {
        var u User
        // Scan 按順序把列值賦給變量
        err = rows.Scan(&u.Id, &u.Name, &u.Age)
        if err != nil {
            panic(err)
        }
        users = append(users, u)
    }

    // 檢查遍歷過程中是否有錯誤
    if err = rows.Err(); err != nil {
        panic(err)
    }

    for _, u := range users {
        fmt.Printf("id=%d, name=%s, age=%d\n", u.Id, u.Name, u.Age)
    }
}

幾個要點:

  • $1、$2 作為占位符(不是 ?
  • defer rows.Close() 是必須的,不然連接會泄漏
  • rows.Err() 檢查循環(huán)中是否出錯

1.2 執(zhí)行非查詢(INSERT/UPDATE/DELETE)

不返回結(jié)果集的 SQL 用 Exec 方法:

// 插入數(shù)據(jù)
result, err := db.Exec("INSERT INTO users(name, age) VALUES($1, $2)", "張三", 25)
if err != nil {
    panic(err)
}

// 獲取插入的行數(shù)
rowsAffected, err := result.RowsAffected()
fmt.Printf("影響了 %d 行\(zhòng)n", rowsAffected)

// 獲取自增 ID(需要開啟 get_last_insert_id 參數(shù))
// lastId, err := result.LastInsertId()

1.3 一次執(zhí)行多條 SQL

Exec 一次只能執(zhí)行一條。多條 SQL 需要分別調(diào)用,或者用事務(wù)包裝:

// 開啟事務(wù)
tx, err := db.Begin()
if err != nil {
    panic(err)
}

// 執(zhí)行多條
_, err = tx.Exec("INSERT INTO users(name, age) VALUES($1, $2)", "李四", 30)
if err != nil {
    tx.Rollback()
    panic(err)
}

_, err = tx.Exec("UPDATE stats SET count = count + 1")
if err != nil {
    tx.Rollback()
    panic(err)
}

// 提交
err = tx.Commit()
if err != nil {
    panic(err)
}

二、預(yù)備語句(Prepared Statement)

預(yù)備語句有兩個好處:

  1. 防 SQL 注入:參數(shù)和 SQL 結(jié)構(gòu)分離,惡意輸入不會被當(dāng)作 SQL 執(zhí)行
  2. 提升性能:數(shù)據(jù)庫只解析一次,多次執(zhí)行時復(fù)用執(zhí)行計劃

2.1 基本用法

// 準(zhǔn)備 SQL
stmt, err := db.Prepare("INSERT INTO users(name, age) VALUES($1, $2)")
if err != nil {
    panic(err)
}
defer stmt.Close()  // 用完關(guān)閉

// 多次執(zhí)行
_, err = stmt.Exec("王五", 28)
_, err = stmt.Exec("趙六", 32)

2.2 查詢用預(yù)備語句

stmt, err := db.Prepare("SELECT id, name FROM users WHERE age > $1")
if err != nil {
    panic(err)
}
defer stmt.Close()

rows, err := stmt.Query(25)
// 處理 rows...

2.3 三種占位符風(fēng)格

Gokb 支持三種占位符,習(xí)慣用哪個都行:

// 1. 匿名占位符 $N(推薦)
db.Query("SELECT * FROM users WHERE id = $1 AND name = $2", 1, "張三")

// 2. 問號占位符
db.Query("SELECT * FROM users WHERE id = ? AND name = ?", 1, "張三")

// 3. 命名占位符 :NAME(適合參數(shù)多的情況)
db.Query("SELECT * FROM users WHERE id = :id AND name = :name", 
    sql.Named("id", 1), sql.Named("name", "張三"))

命名占位符在參數(shù)多的時候代碼更清晰,推薦使用。

三、類型映射

Go 類型和金倉數(shù)據(jù)庫類型的對應(yīng)關(guān)系:

Kingbase 類型Go 類型
smallint, integer, bigintint64
real, doublefloat64
char, varchar, text, clobstring
date, time, timestamptime.Time
booleanbool
bytea, blob[]byte
其他類型[]byte

示例:

type LogRecord struct {
    Id        int64
    Message   string
    CreatedAt time.Time
    IsActive  bool
    Data      []byte
}

rows, _ := db.Query("SELECT id, message, created_at, is_active, data FROM logs")
for rows.Next() {
    var r LogRecord
    rows.Scan(&r.Id, &r.Message, &r.CreatedAt, &r.IsActive, &r.Data)
    // 處理數(shù)據(jù)...
}

3.1 處理 NULL 值

數(shù)據(jù)庫字段可能是 NULL,Go 的基本類型不能表示 NULL。需要用 sql.NullString、sql.NullInt64 等:

type User struct {
    Id     int64
    Name   string
    Email  sql.NullString  // 可能為 NULL
    Age    sql.NullInt64   // 可能為 NULL
}

rows, _ := db.Query("SELECT id, name, email, age FROM users")
for rows.Next() {
    var u User
    rows.Scan(&u.Id, &u.Name, &u.Email, &u.Age)
    
    if u.Email.Valid {
        fmt.Println("email:", u.Email.String)
    } else {
        fmt.Println("email: NULL")
    }
}

四、調(diào)用存儲過程

4.1 無 OUT 參數(shù)的存儲過程

// 直接使用 Exec
_, err := db.Exec("CALL update_user_status(1, 'active')")

4.2 帶 OUT 參數(shù)的存儲過程

OUT 參數(shù)需要用 sql.Out 類型綁定:

// 假設(shè)存儲過程定義:
// CREATE OR REPLACE PROCEDURE get_user_name(
//     p_id IN INT,
//     p_name OUT VARCHAR
// ) AS BEGIN
//     SELECT name INTO p_name FROM users WHERE id = p_id;
// END;

var userName string
_, err := db.Exec(
    "CALL get_user_name(:id, :name)",
    sql.Named("id", 1),
    sql.Named("name", sql.Out{Dest: &userName}),
)
if err != nil {
    panic(err)
}
fmt.Println("用戶名:", userName)

4.3 帶返回值的存儲過程

Kingbase 的 SQL Server 模式下,存儲過程可以有返回值。用 gokb.ReturnStatus 接收:

import (
    "database/sql"
    "kingbase.com/gokb"
    _ "kingbase.com/gokb"
)

var ret gokb.ReturnStatus
_, err := db.Exec(
    "proc_name",
    &ret,  // 第一個參數(shù)放返回值
    sql.Named("p1", 100),
    sql.Named("p2", sql.Out{Dest: &outValue}),
)
fmt.Println("返回值:", ret)

五、超時控制

5.1 連接超時

在連接字符串中配置 connect_timeout

connStr := "host=127.0.0.1 user=system password=123456 dbname=TEST connect_timeout=10"

這個控制的是 TCP 連接建立的最長等待時間,單位秒。

5.2 執(zhí)行超時

對于慢查詢,可以用 Context 控制超時:

import "context"

// 設(shè)置 5 秒超時
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

// 使用 QueryContext 或 ExecContext
rows, err := db.QueryContext(ctx, "SELECT * FROM huge_table WHERE complex_condition")
if err != nil {
    if err == context.DeadlineExceeded {
        fmt.Println("查詢超時了")
    }
    panic(err)
}

這個超時依賴網(wǎng)絡(luò)和服務(wù)端正常運行。如果出現(xiàn)斷網(wǎng)、數(shù)據(jù)庫宕機,Context 超時可能不生效。那種場景需要用 TCP 層面的超時參數(shù):

connStr := "host=127.0.0.1 user=system password=123456 dbname=TEST tcp_user_timeout=30000 keepalive_interval=10 keepalive_count=3"
  • tcp_user_timeout:TCP 層面最長等待時間(毫秒)
  • keepalive_interval:?;钐綔y間隔(秒)
  • keepalive_count:連續(xù)多少次無響應(yīng)后斷開

六、獲取自增列值

如果表有自增列(SERIAL 或 IDENTITY),插入后想獲取自動生成的值,需要兩步:

  1. 連接字符串中開啟 get_last_insert_id=yes
  2. 確保自增列是表的第一個字段
connStr := "host=127.0.0.1 user=system password=123456 dbname=TEST get_last_insert_id=yes"

db, _ := sql.Open("kingbase", connStr)

// 假設(shè)表定義:id SERIAL PRIMARY KEY, name VARCHAR(100)
result, err := db.Exec("INSERT INTO users(name) VALUES($1)", "張三")
if err != nil {
    panic(err)
}

lastId, err := result.LastInsertId()
fmt.Printf("剛插入的 ID: %d\n", lastId)

限制

  • 自增列必須在第一列
  • INSERT IGNORE 不支持獲取自增值

七、完整示例

package main

import (
    "context"
    "database/sql"
    "fmt"
    "time"
    _ "kingbase.com/gokb"
)

type User struct {
    Id   int
    Name string
    Age  int
}

func main() {
    connStr := "host=127.0.0.1 user=system password=123456 dbname=TEST sslmode=disable connect_timeout=10"
    db, err := sql.Open("kingbase", connStr)
    if err != nil {
        panic(err)
    }
    defer db.Close()

    db.SetMaxOpenConns(10)

    // 1. 建表
    _, err = db.Exec(`CREATE TABLE IF NOT EXISTS go_users (
        id SERIAL PRIMARY KEY,
        name VARCHAR(100) NOT NULL,
        age INT
    )`)
    if err != nil {
        panic(err)
    }
    fmt.Println("建表成功")

    // 2. 插入數(shù)據(jù)(預(yù)備語句)
    stmt, err := db.Prepare("INSERT INTO go_users(name, age) VALUES($1, $2)")
    if err != nil {
        panic(err)
    }
    defer stmt.Close()

    users := []User{
        {Name: "張三", Age: 25},
        {Name: "李四", Age: 30},
        {Name: "王五", Age: 28},
    }
    for _, u := range users {
        _, err = stmt.Exec(u.Name, u.Age)
        if err != nil {
            panic(err)
        }
    }
    fmt.Println("插入數(shù)據(jù)成功")

    // 3. 查詢數(shù)據(jù)
    rows, err := db.Query("SELECT id, name, age FROM go_users WHERE age > $1", 20)
    if err != nil {
        panic(err)
    }
    defer rows.Close()

    var results []User
    for rows.Next() {
        var u User
        err = rows.Scan(&u.Id, &u.Name, &u.Age)
        if err != nil {
            panic(err)
        }
        results = append(results, u)
    }

    fmt.Println("查詢結(jié)果:")
    for _, u := range results {
        fmt.Printf("  id=%d, name=%s, age=%d\n", u.Id, u.Name, u.Age)
    }

    // 4. 更新數(shù)據(jù)
    result, err := db.Exec("UPDATE go_users SET age = $1 WHERE name = $2", 26, "張三")
    if err != nil {
        panic(err)
    }
    affected, _ := result.RowsAffected()
    fmt.Printf("更新了 %d 行\(zhòng)n", affected)

    // 5. 帶超時的查詢
    ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
    defer cancel()
    
    row := db.QueryRowContext(ctx, "SELECT COUNT(*) FROM go_users")
    var count int
    err = row.Scan(&count)
    if err != nil {
        if err == context.DeadlineExceeded {
            fmt.Println("查詢超時")
        } else {
            panic(err)
        }
    } else {
        fmt.Printf("總記錄數(shù): %d\n", count)
    }
}

八、常見問題

預(yù)備語句不釋放會怎樣?

每個 Prepare 都會在數(shù)據(jù)庫端創(chuàng)建一個預(yù)備語句對象,不 Close 會一直占用資源。務(wù)必加上 defer stmt.Close()。

連接數(shù)耗盡怎么辦?

檢查是否忘了 rows.Close(),或者事務(wù)沒有 Commit/Rollback??梢杂?SetMaxOpenConns 限制最大連接數(shù)。

怎么調(diào)試 SQL?

Go 標(biāo)準(zhǔn)庫沒有內(nèi)置 SQL 日志??梢宰约喊b一層:

func Query(db *sql.DB, query string, args ...interface{}) (*sql.Rows, error) {
    log.Printf("SQL: %s, args: %v", query, args)
    return db.Query(query, args...)
}

九、小結(jié)

下篇主要講了:

  1. SQL 執(zhí)行Query 查、Exec 寫,記住 defer rows.Close()
  2. 預(yù)備語句:防注入、提升性能,支持三種占位符($1、?:name
  3. 類型映射:NULL 值用 sql.NullXxx,時間用 time.Time
  4. 存儲過程:OUT 參數(shù)用 sql.Out,返回值用 gokb.ReturnStatus
  5. 超時控制connect_timeout 連不上超時,Context 查得慢超時

Gokb 驅(qū)動整體還算順手,遵循 Go 的 database/sql 標(biāo)準(zhǔn)接口,上手成本不高。遇到問題先檢查連接參數(shù)和預(yù)備語句是否正確釋放,大部分問題都能解決。

到此這篇關(guān)于Go語言操作金倉數(shù)據(jù)庫之SQL執(zhí)行,類型映射與超時控制詳解的文章就介紹到這了,更多相關(guān)Go語言操作金倉數(shù)據(jù)庫內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 重學(xué)Go語言之如何使用Context

    重學(xué)Go語言之如何使用Context

    Context,中文也叫做上下文,Go語言在1.7版本中新增的context包中定義了Context,下面我們就來一起看看如何在Go語言中使用Context吧
    2023-07-07
  • 一文帶你了解Go中跟蹤函數(shù)調(diào)用鏈的實現(xiàn)

    一文帶你了解Go中跟蹤函數(shù)調(diào)用鏈的實現(xiàn)

    這篇文章主要為大家詳細介紹了go如何實現(xiàn)一個自動注入跟蹤代碼,并輸出有層次感的函數(shù)調(diào)用鏈跟蹤命令行工具,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-11-11
  • Go 數(shù)據(jù)庫查詢與結(jié)構(gòu)體映射的示例詳解

    Go 數(shù)據(jù)庫查詢與結(jié)構(gòu)體映射的示例詳解

    本文主要介紹了如何使用Go語言進行數(shù)據(jù)庫查詢并將查詢結(jié)果映射到結(jié)構(gòu)體中,文章詳細講解了結(jié)構(gòu)體字段導(dǎo)出和db標(biāo)簽的使用方法,并通過示例代碼展示了如何正確地進行數(shù)據(jù)庫查詢和結(jié)果映射
    2024-11-11
  • 淺析Go語言的數(shù)據(jù)類型及數(shù)組

    淺析Go語言的數(shù)據(jù)類型及數(shù)組

    Golang是一種靜態(tài)強類型、編譯型語言。Go?語言語法與?C?相近,但功能上有:內(nèi)存安全,GC(垃圾回收),結(jié)構(gòu)形態(tài)及?CSP-style?并發(fā)計算。本文主要和大家聊聊Go語言的數(shù)據(jù)類型及數(shù)組,希望對大家有所幫助
    2022-11-11
  • Golang 類型斷言的具體使用

    Golang 類型斷言的具體使用

    本文主要介紹了Golang 類型斷言的具體使用,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03
  • go語言調(diào)用其他包中的函數(shù)簡單示例

    go語言調(diào)用其他包中的函數(shù)簡單示例

    這篇文章主要給大家介紹了關(guān)于go語言調(diào)用其他包中的函數(shù)的相關(guān)資料,文中還介紹了Go語言同一個包中不同文件之間函數(shù)調(diào)用的相關(guān)問題,需要的朋友可以參考下
    2023-01-01
  • Golang基于內(nèi)存的鍵值存儲緩存庫go-cache

    Golang基于內(nèi)存的鍵值存儲緩存庫go-cache

    go-cache是一個內(nèi)存中的key:value store/cache庫,適用于單機應(yīng)用程序,本文主要介紹了Golang基于內(nèi)存的鍵值存儲緩存庫go-cache,具有一定的參考價值,感興趣的可以了解一下
    2025-03-03
  • go實現(xiàn)for range迭代時修改值的操作

    go實現(xiàn)for range迭代時修改值的操作

    這篇文章主要介紹了go實現(xiàn)for range迭代時修改值的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • Go?實現(xiàn)?Nginx?加權(quán)輪詢算法的方法步驟

    Go?實現(xiàn)?Nginx?加權(quán)輪詢算法的方法步驟

    本文主要介紹了Go?實現(xiàn)?Nginx?加權(quán)輪詢算法的方法步驟,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • golang gopm get -g -v 無法獲取第三方庫的解決方案

    golang gopm get -g -v 無法獲取第三方庫的解決方案

    這篇文章主要介紹了golang gopm get -g -v 無法獲取第三方庫的解決方案,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-05-05

最新評論

华蓥市| 河北区| 新邵县| 广宁县| 即墨市| 景东| 宜阳县| 衡南县| 丰镇市| 舟山市| 云浮市| 旌德县| 木里| 汉寿县| 葫芦岛市| 高青县| 称多县| 舒城县| 萨嘎县| 道真| 曲周县| 巴东县| 双牌县| 玉田县| 邹平县| 泰安市| 桦南县| 迁西县| 大渡口区| 涪陵区| 棋牌| 泸州市| 广宁县| 保山市| 襄城县| 武邑县| 白水县| 和静县| 平山县| 商洛市| 泰州市|