一尘不染

golang sql驱动程序的prepare语句

go

关于golang的sql驱动程序,以下两个语句之间有什么区别?

// store.DB is *sql.DB type
rows, err := store.DB.Query(SQL, args ...)
// err != nil
defer rows.Close()

// store.DB is *sql.DB type
stmt, err := store.DB.Prepare(SQL)
// err != nil
defer stmt.Close()

rows, err := stmt.Query(args ...)
// err != nil
defer rows.Close()

看来他们是一样的吗?有什么细微的区别吗?

更新

我们不需要执行许多stmt.Execstmt.Query之后db.Prepare,只有一个execquery每个之后执行prepare。当使用db.Queryor时db.Exec,我们将参数传递给方法,而不是使用原始SQL字符串(出于安全考虑)。

我发现了一个参考链接:http : //go-database-
sql.org/prepared.html
似乎两种方式都使用了 预处理语句 ,有什么区别?


阅读 1931

收藏
2020-07-02

共1个答案

一尘不染

差异可能是细微的,有时是重要的,有时实际上是不存在的。

通常,一条准备好的语句1.准备服务器(解析SQL,生成执行计划等),2.使用其他参数执行,然后3.关闭。它使您可以重复使用相同的SQL,并且每次都传递不同的参数,它可以帮助防止SQL注入,可以提供一些性能增强(特定于驱动程序/协议,YMMV)并防止重复步骤,例如在执行计划生成和SQL解析中上面的
准备 步骤。

对于编写源代码的人而言,准备好的语句可能比连接字符串并将其发送到DB服务器更为方便。

DB.Query()方法将SQL作为字符串,并使用零个或多个参数(Exec()QueryRow())。没有附加参数的SQL字符串将精确查询您编写的内容。但是,如果提供了一个带占位符和其他参数的SQL字符串,则会在幕后为您准备一条准备好的语句。

DB.Prepare()方法显式执行一个准备好的语句,然后将其传递给参数,如:中所示stmt.Exec(...args)

就两者之间的差异以及为什么使用一种或另一种而言,有几件事值得考虑。

您可以DB.Query()不带参数使用。这是非常有效的,因为它可以绕开 prepare 语句必须经过的 _prepare- > execute->
close_序列。

您还可以将其与其他参数一起使用,并在查询字符串中使用占位符,并且它将在我前面提到的情况下在其后执行准备好的语句。这里的潜在问题是,当您进行多个查询时,每个查询都会导致内部准备好的语句。由于涉及到额外的步骤,因此在每次执行该查询时,它都会重新准备,执行和关闭,因此效率很低。

使用显式的准备好的语句,当您尝试使用可能具有不同参数的重用先前准备的SQL时,可以避免这种效率低下的情况。

但这并不总是能按您预期的那样解决…由于由db /
sql管理的基础连接池,您的“数据库连接”是相当虚拟的。该DB.Prepare()方法将针对特定连接准备该语句,然后在需要执行该连接时尝试恢复该连接,但是如果该连接不可用,它将简单地获取一个可用的连接并重新准备并针对该连接执行。如果您一次又一次地使用相同的准备好的语句,那么您可能会在不知不觉中又一次又一次地准备它。当您处理繁忙的流量时,这显然很明显。

因此,很明显,您在哪种情况下使用哪种取决于您的特定用例,但是我希望上面的详细信息对您有所帮助,以便您可以在每种情况下做出最佳决策。

更新资料

给定OP中的更新,当查询只需要执行一次时,基本上没有什么区别,因为带有参数的查询是作为幕后的准备好的语句完成的。

与直接DB.Query()使用预处理语句相比,使用直接方法(例如,及其类似方法),因为这将导致源代码稍微简单一些。

由于在这种情况下,出于安全原因而使用了准备好的语句,因此值得通过其他方式来处理安全问题并改为使用纯文本查询,因为这将提高性能。但是,除非有足够的流量(或预计将来流量会大大增加)以减轻服务器上的负载,否则任何收益都是无关紧要的。再次归结为现实用例。

对于有兴趣在某些指标上准备的语句和直接明文查询之间的差别的人,有一个很好的文章在这里(这也很好地解释很多上述的一个出色的工作)。

2020-07-02