一尘不染

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

go

我在Gogre和Postgresql中使用。

在这里说,对于不返回行(插入,删除,更新)的操作,我们应该使用exec()

如果函数名称包含Query,则它被设计为询问数据库问题,并且即使它为空,也将返回一组行。不返回行的语句不应使用查询函数;他们应该使用Exec()。

然后它在这里说:

Go在幕后为您创建准备好的语句。例如,一个简单的db.Query(sql,param1,param2)的工作方式是:准备sql,然后使用参数执行该sql,最后关闭该语句。

如果 query() 在准备好的表述下使用,我为什么还要打扰使用准备好的表述呢?


阅读 874

收藏
2020-07-02

共1个答案

一尘不染

“为什么还要使用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)

有一个第3的方式,你可以用Postgres的热膨胀系数的组合做到这一点SELECT COUNTdb.QueryRowrow.Scan但我不认为一个例子是要说明如何不合理的做法相比时,这将是db.Exec

使用db.Execover的另一个原因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调用,因此最终导致打开的连接数增加,最终达到服务器的限制。


“或用Golang准备的语句?”:

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

例如,请参见queryDC(未调用by的方法的db.Query)这两个部分:不带准备语句带准备语句

不管书是否正确,除非关闭返回的对象,否则除非进行一些内部缓存,否则db.Stmt创建者都是正确的。如果改为手动调用,然后缓存并重用返回的内容,则可能会提高需要经常执行的查询的性能。db.Query``Rows``db.Prepare``db.Stmt

要了解如何使用准备好的语句来优化性能,可以看一下官方文档:https : //www.postgresql.org/docs/current/static/sql-
prepare.html

2020-07-02