Go Wiki: SQLInterface

引言

database/sql 包提供了圍繞 SQL(或類 SQL)資料庫的通用介面。有關詳細資訊,請參閱官方文件

本頁提供使用模式示例。

資料庫驅動程式

database/sql 包必須與資料庫驅動程式一起使用。請參閱https://golang.com.tw/s/sqldrivers 獲取驅動程式列表。

下面的文件假定已匯入驅動程式。

連線資料庫

Open 用於建立資料庫控制代碼

db, err := sql.Open(driver, dataSourceName)

其中 driver 指定資料庫驅動程式,dataSourceName 指定特定於資料庫的連線資訊,如資料庫名稱和身份驗證憑據。

請注意,Open 不會直接開啟資料庫連線:這會推遲到進行查詢時。為了在執行查詢之前驗證是否可以建立連線,請使用PingContext 方法。

if err := db.PingContext(ctx); err != nil {
  log.Fatal(err)
}

使用完畢後,資料庫使用 Close 關閉。

執行查詢

ExecContext 用於不返回行的查詢。

result, err := db.ExecContext(ctx,
    "INSERT INTO users (name, age) VALUES ($1, $2)",
    "gopher",
    27,
)

其中 result 包含最後插入的 ID 和受影響的行數。這些值的可用性取決於資料庫驅動程式。

QueryContext 用於檢索。

rows, err := db.QueryContext(ctx, "SELECT name FROM users WHERE age = $1", age)
if err != nil {
    log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
    var name string
    if err := rows.Scan(&name); err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%s is %d\n", name, age)
}
if err := rows.Err(); err != nil {
    log.Fatal(err)
}

QueryRowContext 用於只期望單行的場景。

var age int64
err := db.QueryRowContext(ctx, "SELECT age FROM users WHERE name = $1", name).Scan(&age)

可以使用 PrepareContext 建立預編譯語句。

age := 27
stmt, err := db.PrepareContext(ctx, "SELECT name FROM users WHERE age = $1")
if err != nil {
    log.Fatal(err)
}
rows, err := stmt.Query(age)
// process rows

可以在語句上呼叫 ExecContextQueryContextQueryRowContext。使用完畢後,應使用 Close 關閉語句。

事務

事務使用 BeginTx 啟動。

tx, err := db.BeginTx(ctx, nil)
if err != nil {
    log.Fatal(err)
}

前面介紹的 ExecContextQueryContextQueryRowContextPrepareContext 方法可以在事務中使用。

事務必須以呼叫 CommitRollback 結束。

處理 NULL

如果資料庫列允許為空,則應將支援 NULL 值的型別之一傳遞給 Scan。

例如,如果 names 表中的 name 列允許為空,

var name sql.NullString
err := db.QueryRowContext(ctx, "SELECT name FROM names WHERE id = $1", id).Scan(&name)
...
if name.Valid {
    // use name.String
} else {
    // value is NULL
}

database/sql 中僅實現了 NullByteNullBoolNullFloat64NullInt64NullInt32 NullInt16NullStringNullTime。資料庫特定 NULL 型別的實現留給資料庫驅動程式。可以透過實現 database/sql/driver.Valuerdatabase/sql.Scanner 介面來建立支援 NULL 的使用者型別。

您也可以傳遞指標型別。請注意效能問題,因為它需要額外的記憶體分配。

var name *string
err := db.QueryRowContext(ctx, "SELECT name FROM names WHERE id = $1", id).Scan(&name)

獲取表

如果您想從 SQL 查詢中獲取結構體陣列。

func getTable[T any](rows *sql.Rows) (out []T) {
    var table []T
    for rows.Next() {
        var data T
        s := reflect.ValueOf(&data).Elem()
        numCols := s.NumField()
        columns := make([]interface{}, numCols)

        for i := 0; i < numCols; i++ {
            field := s.Field(i)
            columns[i] = field.Addr().Interface()
        }

        if err := rows.Scan(columns...); err != nil {
            fmt.Println("Case Read Error ", err)
        }

        table = append(table, data)
    }
    return table
}

請確保處理來自資料庫的 NULL 值。

type User struct {
  UUID  sql.NullString
  Name  sql.NullString
}

rows, err := db.Query("SELECT * FROM Users")
cases := getTable[User](rows)

此內容是 Go Wiki 的一部分。