查询数据
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表中检索记录。
| |