查詢資料
當執行會傳回資料的 SQL 語句時,請使用 database/sql
套件中提供的其中一個 Query
方法。它們每個會傳回一個 Row
或 Rows
,你的程式碼可以使用 Scan
方法將其資料複製到變數。例如,你可以使用這些方法來執行 SELECT
語句。
當執行不會傳回資料的語句時,你可以改用 Exec
或 ExecContext
方法。如需更多資訊,請參閱 執行不會傳回資料的語句。
database/sql
套件提供兩種執行可取得結果的查詢的方法。
- 查詢單一列 –
QueryRow
最多會從資料庫傳回一列Row
。如需更多資訊,請參閱 查詢單一列。 - 查詢多列 –
Query
會將所有符合條件的列傳回,以供你的程式碼使用Rows
結構繞行。如需更多資訊,請參閱 查詢多列。
如果您的程式碼將重複執行相同的 SQL 語句,請考慮使用已準備好的語句。關於更多資訊,請參閱 使用已準備好的語句。
請注意:不要使用例如 fmt.Sprintf
之類的字串格式化函數組裝 SQL 語句!您可能會引入 SQL 內嵌風險。關於更多資訊,請參閱 避免 SQL 內嵌風險。
查詢單一列
QueryRow
最多擷取單一資料庫列,例如您想要透過唯一 ID 查詢資料時。如果查詢回傳多列,Scan
方法會捨棄第一列以外的所有資料。
QueryRowContext
的運作方式與 QueryRow
相同,但多一個 context.Context
參數。關於更多資訊,請參閱 取消進行中的作業。
下列範例使用查詢找出是否有足夠庫存來支援購買。如果庫存足夠,SQL 語句會回傳 true
,否則會回傳 false
。 Row.Scan
會透過指標將布林回傳值複製到 enough
變數中。
func canPurchase(id int, quantity int) (bool, error) {
var enough bool
// Query for a value based on a single row.
if err := db.QueryRow("SELECT (quantity >= ?) from album where id = ?",
quantity, id).Scan(&enough); err != nil {
if err == sql.ErrNoRows {
return false, fmt.Errorf("canPurchase %d: unknown album", id)
}
return false, fmt.Errorf("canPurchase %d: %v", id, err)
}
return enough, nil
}
請注意:已準備好語句中的參數佔位符會根據您使用的 DBMS 和驅動程式而有所不同。例如,pq 驅動程式 for Postgres 需要類似於 $1
而不是 ?
的佔位符。
處理錯誤
QueryRow
本身不會回傳錯誤。相反地,Scan
會回報結合查詢和掃描所得的任何錯誤。當查詢找不到列時,它會回傳 sql.ErrNoRows
。
用於回傳單一列的函數
函數 | 說明 |
---|---|
DB.QueryRow DB.QueryRowContext
|
獨立執行單一列查詢。 |
Tx.QueryRow Tx.QueryRowContext
|
在較大範圍的交易中執行單一列查詢。關於更多資訊,請參閱 執行交易。 |
Stmt.QueryRow Stmt.QueryRowContext
|
使用已準備好的語句執行單一列查詢。關於更多資訊,請參閱 使用已準備好的語句。 |
Conn.QueryRowContext
|
供保留的連線使用。關於更多資訊,請參閱 管理連線。 |
查詢多列
您可以使用 Query
或 QueryContext
來查詢多列,這兩個方法會回傳代表查詢結果的 Rows
。您的程式碼會使用 Rows.Next
來反覆執行回傳的列。每一次反覆執行都會呼叫 Scan
將欄位值複製到變數中。
QueryContext
的運作方式與 Query
相似,但附帶 context.Context
參數。更多資訊,請參閱 取消進行中的操作。
下列範例執行查詢,以傳回特定藝術家的專輯。專輯會傳回至 sql.Rows
中。程式碼使用 Rows.Scan
將欄位值複製到指標表示的變數中。
func albumsByArtist(artist string) ([]Album, error) {
rows, err := db.Query("SELECT * FROM album WHERE artist = ?", artist)
if err != nil {
return nil, err
}
defer rows.Close()
// An album slice to hold data from returned rows.
var albums []Album
// Loop through rows, using Scan to assign column data to struct fields.
for rows.Next() {
var alb Album
if err := rows.Scan(&alb.ID, &alb.Title, &alb.Artist,
&alb.Price, &alb.Quantity); err != nil {
return albums, err
}
albums = append(albums, alb)
}
if err = rows.Err(); err != nil {
return albums, err
}
return albums, nil
}
請注意遞延呼叫 rows.Close
。這會釋放由該列所保持的任何資源,不論函式如何傳回。循序巡覽所有列也會暗中關閉,但最好使用 defer
來確保 rows
確實會關閉。
請注意:已準備好語句中的參數佔位符會根據您使用的 DBMS 和驅動程式而有所不同。例如,pq 驅動程式 for Postgres 需要類似於 $1
而不是 ?
的佔位符。
處理錯誤
循序巡覽查詢結果後,請務必檢查 sql.Rows
的錯誤。如果查詢失敗,您的程式碼會透過此方式找出原因。
用於傳回多個列的函式
函數 | 說明 |
---|---|
DB.Query DB.QueryContext
|
獨立執行查詢。 |
Tx.Query Tx.QueryContext
|
在較大型交易內執行查詢。更多資訊,請參閱 執行交易。 |
Stmt.Query Stmt.QueryContext
|
使用已準備好的陳述式執行查詢。更多資訊,請參閱 使用已準備好的陳述式。 |
Conn.QueryContext
|
供保留的連線使用。關於更多資訊,請參閱 管理連線。 |
處理可為 Null 的欄位值
database/sql
套件提供許多特殊類型,可於欄位值可能為 Null 時,用作 Scan
函式的參數。每個類型都包含 Valid
欄位,顯示值是否非 Null,如果是非 Null 則包含該值。
下列範例中的程式碼查詢客戶名稱。如果名稱值為 Null,程式碼則以其他值取代,以供應用程式使用。
var s sql.NullString
err := db.QueryRow("SELECT name FROM customer WHERE id = ?", id).Scan(&s)
if err != nil {
log.Fatal(err)
}
// Find customer name, using placeholder if not present.
name := "Valued Customer"
if s.Valid {
name = s.String
}
在 sql
套件參考中,進一步了解各個類型
從欄位取得資料
在循序巡覽查詢傳回的列時,Rows.Scan
說明將列的欄位值複製到 Go 值中。
基本資料轉換設定由所有驅動程式支援,例如轉換 SQL INT
成 Go int
。部分驅動程式會延伸這個轉換設定;詳情請參閱各個別驅動程式的文件。
正如你所預期的那樣,Scan
會將資料欄類型轉換為類似的 Go 類型。例如,Scan
會將 SQL CHAR
、VARCHAR
和 TEXT
轉換為 Go string
。然而,Scan
也會轉換為另一個與資料欄值很吻合的 Go 類型。例如,如果資料欄是將永遠包含一個數字的 VARCHAR
,你可以指定一個數值 Go 類型,例如 int
,來接收值,而且 Scan
會使用 strconv.Atoi
為你轉換。
如需有關由 Scan
函式做出的轉換的更多資訊,請參閱 Rows.Scan
參考。
處理多個結果集合
當你的資料庫操作可能會傳回多個結果集合時,你可以使用 Rows.NextResultSet
來擷取這些集合。例如,當你傳送 SQL 分別查詢多個資料表並為每個資料表傳回結果集合時,會很有用。
Rows.NextResultSet
準備下一個結果集合,這樣就可以呼叫 Rows.Next
來擷取該下一個集合的第一列。它傳回一個布林值,指出是否還有任何下一個結果集合。
以下範例中的程式碼使用 DB.Query
來執行兩個 SQL 陳述式。第一個結果集合來自該程序中的第一個查詢,擷取 album
資料表中的所有列。下一個結果集合來自第二個查詢,擷取 song
資料表中的列。
rows, err := db.Query("SELECT * from album; SELECT * from song;")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
// Loop through the first result set.
for rows.Next() {
// Handle result set.
}
// Advance to next result set.
rows.NextResultSet()
// Loop through the second result set.
for rows.Next() {
// Handle second set.
}
// Check for any error in either result set.
if err := rows.Err(); err != nil {
log.Fatal(err)
}