为什么甚至在Golang中使用* DB.exec()或预处理语句?

时间:2018-06-03 08:38:28

标签: sql database postgresql go prepared-statement

我在Postgresql中使用golang。

它表示here对于不返回行(插入,删除,更新)的操作,我们应该使用exec()

  

如果函数名称包含Query,则它旨在询问数据库的问题,并返回一组行,即使它是空的。不返回行的语句不应使用Query函数;他们应该使用Exec()。

然后它说here

  

Go为您创建准备好的陈述。例如,一个简单的db.Query(sql,param1,param2)通过准备sql,然后使用参数执行它并最终关闭语句来工作。

如果 query()使用了封面准备好的陈述,为什么我还要打扰使用准备好的陈述?

1 个答案:

答案 0 :(得分:11)

“为什么甚至使用db.Exec()”:

确实可以交替使用db.Execdb.Query来执行相同的sql语句,但这两种方法会返回不同类型的结果。如果由驱动程序实现,则从db.Exec返回的结果可以告诉您查询影响了多少行,而db.Query将返回行对象。

例如,假设您要执行DELETE语句,并且想知道它删除了多少行。你可以采用正确的方式:

res, err := db.Exec(`DELETE FROM my_table WHERE expires_at = $1`, time.Now())
if err != nil {
    panic(err)
}

numDeleted, err := res.RowsAffected()
if err != nil {
    panic(err)
}
print(numDeleted)

或者更冗长,更客观更昂贵的方式:

rows, err := db.Query(`DELETE FROM my_table WHERE expires_at = $1 RETURNING *`, time.Now())
if err != nil {
    panic(err)
}
defer rows.Close()

var numDelete int
for rows.Next() {
    numDeleted += 1
}
if err := rows.Err(); err != nil {
    panic(err)
}
print(numDeleted)

你可以通过postgres CTE,SELECT COUNTdb.QueryRowrow.Scan来实现这一目标,但我不认为有必要展示一个不合理的例子。与db.Exec相比时的方法。

使用db.Exec超过db.Query的另一个原因是,当您不关心返回的结果时,您需要的只是执行查询并检查是否存在错误。在这种情况下,您可以这样做:

if _, err := db.Exec(`<my_sql_query>`); err != nil {
    panic(err)
}

另一方面,你不能(你可以,但你不应该)这样做:

if _, err := db.Query(`<my_sql_query>`); err != nil {
    panic(err)
}

这样做,片刻之后,您的程序会因为出现与too many connections open类似的错误而惊慌失措。这是因为您在没有首先对其进行强制db.Rows调用的情况下丢弃返回的Close值,因此您最终会打开连接数并最终达到服务器限制。< / p>

“或Golang中的预处理语句?”:

我不认为你引用的那本书是正确的。至少在我看来,db.Query调用是否每次创建一个新的预准备语句都取决于您使用的驱动程序。

例如,请参阅queryDC的两个部分(由db.Query调用的未导出方法):without prepared statementwith prepared statement

无论本书是否正确,db.Stmt创建的db.Query都是,除非有一些内部缓存,否则在关闭返回的Rows对象后会被丢弃。如果您改为手动调用db.Prepare,然后缓存并重新使用返回的db.Stmt,则可以提高需要经常执行的查询的性能。

要了解准备好的声明如何用于优化性能,您可以查看官方文档:https://www.postgresql.org/docs/current/static/sql-prepare.html