【问题标题】:golang sql driver's prepare statementgolang sql驱动的prepare语句
【发布时间】:2016-10-07 14:00:20
【问题描述】:

关于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()

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

更新

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

我找到了一个参考链接:http://go-database-sql.org/prepared.html
看来这两种方式都是使用prepared statement,有什么区别?

【问题讨论】:

    标签: mysql go


    【解决方案1】:

    这些差异可能很微妙,有时很重要,有时实际上根本不存在。

    一般来说,准备好的语句 1. 与服务器一起准备(SQL 解析,生成执行计划等),2. 使用附加参数执行,然后 3. 关闭。它可以让你重用相同的 SQL,每次传入不同的参数,它可以帮助防止 SQL 注入,可以提供一些性能增强(驱动程序/协议特定,YMMV)并防止重复步骤,如执行计划生成和 SQL 解析上面的准备步骤。

    对于编写源代码的人来说,准备好的语句可能比连接字符串并将它们发送到数据库服务器更方便。

    DB.Query() 方法将 SQL 作为字符串和零个或多个参数(Exec()QueryRow() 也是如此)。没有附加参数的 SQL 字符串将准确查询您编写的内容。但是,如果提供带有占位符和其他参数的 SQL 字符串,系统会在后台为您完成准备好的语句。

    DB.Prepare() 方法显式执行准备好的语句,然后您将参数传递给该语句,如:stmt.Exec(...args)

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

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

    您还可以在查询字符串中将其与附加参数和占位符一起使用,它会像我上面提到的那样在后台执行准备好的语句。这里的潜在问题是,当您进行大量查询时,每个查询都会产生一个幕后准备好的语句。由于涉及额外的步骤,因此每次执行该查询时都会重新准备、执行和关闭,因此效率可能相当低。

    使用显式准备好的语句,您可以避免这种低效率,因为您尝试重用之前准备的 SQL,但参数可能不同。

    但这并不总是如您所料...由于由 db/sql 管理的底层连接池,您的“数据库连接”是相当虚拟的。 DB.Prepare() 方法将针对特定连接准备语句,然后在需要执行时尝试恢复相同的连接,但如果该连接不可用,它将简单地抓住一个可用的连接并重新准备并执行那。如果您一遍又一遍地使用相同的准备好的语句,那么您可能会在不知不觉中一遍又一遍地准备它。显然,当您处理繁忙的交通时,这主要是暴露出来的。

    很明显,您在什么情况下使用哪种取决于您的具体用例,但我希望上面的详细信息足以帮助您澄清,以便您可以在每种情况下做出最佳决定。

    更新

    鉴于 OP 中的更新,当查询只需要执行一次时基本上没有区别,因为带有参数的查询在后台作为准备好的语句完成。

    使用直接方法,例如DB.Query() 及其类似物,而不是显式使用准备好的语句,因为它会使源代码更加简单。

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

    对于任何对准备好的语句和直接纯文本查询之间的区别的一些指标感兴趣的人,有一篇很好的文章 here(它也很好地解释了上述大部分内容)。

    【讨论】:

    【解决方案2】:

    这在一定程度上取决于您使用的驱动程序,以及您的数据库软件是否支持未准备好的查询。

    Prepare 连接到数据库并创建一个准备好的语句,绑定到该数据库连接。执行时,它会检查该连接是否仍然可用,如果不可用,则创建一个新连接,重新准备函数,然后运行它。

    编辑:注意,如果您一次只有一个例程连接到数据库,并且使用不同的参数一遍又一遍地运行相同的查询,那么 Prepare 可以更快,因为它将查询存储在数据库端。但是,其他例程的连接劫持会否定这一点,如果您只使用一次查询,则完全不会从 Prepare 中获得任何好处。

    查询会根据驱动程序执行以下两种操作之一。如果驱动程序从Open()返回的Conn类型也有Query()方法,因此支持直接查询,那么sql.Query()直接调用它,并返回其结果。如果Conn 确实没有Query() 方法,sql.Query() 准备一个语句,然后执行它。

    例如,PostgreSQL (github.com/lib/pq) 的pq 驱动程序有一个Query() 方法。这个方法准备和执行,就像sql 包会做的那样,但也有一个替代方案:如果查询没有参数,它使用simpleQuery 接口执行,包说明基本上比准备和执行周期更快。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-11-17
      • 1970-01-01
      • 2013-09-08
      • 1970-01-01
      • 1970-01-01
      • 2011-08-19
      • 1970-01-01
      • 2011-01-26
      相关资源
      最近更新 更多