查询数据
6 分钟阅读
Querying for data - 查询数据
当执行返回数据的SQL语句时,使用database/sql包中提供的Query方法之一。这些方法中的每个都返回一个或多个Row,你可以使用Scan方法将这些数据复制到变量中。例如,你将使用这些方法来执行SELECT语句。
当执行不返回数据的语句时,可以使用Exec或ExecContext方法代替。更多信息,请参阅Executing statements that don’t return data(执行不返回数据的语句)。
database/sql包提供了两种执行查询以获取结果的方法。
- 查询单行 ——
QueryRow最多从数据库返回一个Row。更多信息,请参阅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包提供了几种特殊类型,你可以在Scan方法中使用它们作为参数,当列的值可能为null时。每个类型都包含一个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来检索它们。例如,当你分别查询多个表并返回每个表的结果集时,这可能会很有用。
Rows.NextResultSet准备下一个结果集,以便对Rows.Next的调用可以从下一个集中检索第一行。它返回一个布尔值,表示是否确实存在下一个结果集。
以下示例中的代码使用DB.Query执行两个SQL语句。第一个结果集来自存储过程的第一个查询,检索了album表中的所有行。下一个结果集来自第二个查询,从song表中检索行。
| |