查询数据
6 分钟阅读
Querying for data - 查询数据
当执行一个返回数据的SQL语句时,使用database/sql
包中提供的Query
方法之一。每个方法都会返回一个行(Row
)或多个行(Rows
),您可以使用Scan
方法将其数据复制到变量。您会使用这些方法,例如,执行SELECT
语句。
当执行一个不返回数据的语句时,您可以改用Exec
或ExecContext
方法。更多信息请参见 Executing statements that don’t return data(执行不返回数据的语句)。
database/sql
包提供了两种执行结果查询的方法:
- 查询单行 ——
QueryRow
最多只能从数据库中返回一个单行。更多信息请参见 Querying for a single row (查询单行)。 - 查询多行 ——
Query
将所有匹配的行作为一个Rows
结构体(您的代码可以循环遍历)返回,。更多信息,请参见 查询多行。
如果您的代码将重复执行相同的SQL语句,请考虑使用预处理语句。更多信息,请参见 Using prepared statements (使用预处理语句) 。
注意
不要使用字符串格式化函数,如
fmt.Sprintf
来组合一个SQL语句!您可能会引入一个SQL注入的风险。更多信息,请参见避免SQL注入风险。
查询单行
QueryRow
最多只能检索一条数据库记录,例如当您想通过一个唯一的ID来查询数据。如果查询返回多条记录,Scan
方法会丢弃除第一条以外的所有记录。
QueryRowContext
的工作方式与QueryRow
类似,但有一个context.Context
实参。更多信息请参见 Canceling in-progress operations(取消正在进行的操作)。
下面的例子使用一个查询来找出是否有足够的库存来支持购买。如果有足够的库存,该SQL语句返回true
,如果没有则返回false
。Row.Scan通过一个指针将布尔型的返回值复制到enough
变量中。
|
|
注意:准备预处理语句中的参数占位符根据您所使用的DBMS
和驱动而不同。例如,Postgres
的pq driver需要一个类似于$1
的占位符,而不是?
。
处理错误
QueryRow
本身不返回错误。相反,Scan
报告来自组合查询和扫描的任何错误。当查询没有找到记录时,它返回sql.ErrNoRows。
Functions for returning a single row 用于返回单行的函数
Function 函数 | Description 描述 |
---|---|
DB.QueryRow DB.QueryRowContext | 单独运行一个单行查询。 |
Tx.QueryRow Tx.QueryRowContext | 在较大的事务中运行一个单行查询。更多信息,请参阅Executing transactions (执行事务) 。 |
Stmt.QueryRow Stmt.QueryRowContext | 使用预处理语句运行一个单行查询。更多信息,请参见 Using prepared statements(使用预处理语句)。 |
Conn.QueryRowContext | 用于保留连接。更多信息,请参见Managing connections( 管理连接)。 |
查询多行
您可以使用Query
或QueryContext
查询多条记录,它们返回一个代表查询结果的Rows
。您的代码使用Rows.Next对返回的行进行迭代。每次迭代都会调用Scan
来将列值复制到变量中。
QueryContext
的工作方式与Query
类似,但有一个context.Context
实参。更多信息请参见 Canceling in-progress operations (取消正在进行的操作)。
下面的例子执行了一个查询,返回指定艺术家的专辑。这些专辑被返回到一个sql.Rows
中。该代码使用Rows.Scan将列值复制到由指针表示的变量中。
|
|
注意
对rows.Close的延迟调用。无论函数如何返回,这都会释放rows所持有的任何资源。循环处理所有的行也会隐式地关闭它,但最好使用
defer
来确保无论如何都会关闭rows
。
注意
预处理语句中的参数占位符根据您所使用的
DBMS
和驱动而不同。例如,Postgres
的pq driver需要一个类似于$1
的占位符,而不是?
。
处理错误
Be sure to check for an error from sql.Rows
after looping over query results. If the query failed, this is how your code finds out.
在循环查询结果后,一定要从sql.Rows
中检查是否有错误。如果查询失败了,代码就是这样查找的。
返回多行记录的函数
Function 函数 | Description 描述 |
---|---|
DB.Query DB.QueryContext | 单独运行一个查询。 |
Tx.Query Tx.QueryContext | 在较大的事务中运行一个查询。更多信息,请参阅Executing transactions (执行事务) 。 |
Stmt.Query Stmt.QueryContext | 使用预处理语句运行一个查询。更多信息,请参见Using prepared statements(使用预处理语句)。 |
Conn.QueryContext | 用于保留连接。更多信息,请参见Managing connections( 管理连接)。 |
处理可为null的列值
database/sql
包提供了几种特殊的类型,当一个列的值可能为null
时,您可以作为Scan
函数的实参使用。每种类型都包括一个Valid
字段,用于报告值是否为非null
,如果是的话,还包括一个持有该值的字段。
下面的例子中的代码查询了一个客户名称。如果名字的值是null
的,代码会替换另一个值在应用程序中使用。
|
|
在sql
包参考资料中可以看到更多关于每种类型的信息:
从列中获取数据
在循环查询返回的行时,您可以使用Scan
将行的列值复制到Go值,如Rows.Scan参考中所述。
所有驱动程序都支持一组基本的数据转换,例如将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
表中检索记录。
|
|