英文:
Go prepared statements vs querying directly
问题
常见的共识似乎是直接查询的语句不允许使用参数,而预编译的语句可以。
然而,在Go的database/sql包中,你可以使用ODBC参数,并将参数发送到诸如db.QueryRow()和db.Query()之类的函数中。因此,它们在功能上是等效的。
话虽如此,那么首先创建一个语句,然后再执行它有什么意义呢?假设语句首先编译到数据库中,那么这不会增加负载并降低性能吗?而且,由于可以从Query/QueryRow中获取参数,那么语句不就成了一个多余的东西吗?
英文:
The common consensus appears to be that directly queried statements don't allow parameters, and that prepared statements do.
However in Go's database/sql package, you are allowed to use the ODBC parameters and send parameters to such things as db.QueryRow() and db.Query(). So it appears that they are functionally equivalent.
That being said, what is the point then, of first creating a statement, and then executing it? Let's say statements compile against the database first -- doesn't that increase load and thereby reduce performance since you're adding an extra trip? And since you can get parameters from Query/QueryRow, wouldn't that make statements a bad thing?
答案1
得分: 4
请注意,database/sql
包是一个旨在覆盖所有理论上可能的 SQL 数据库系统功能的包,而不会妨碍特定平台的功能。
SQL driver 实例是通过 sql.Register
在运行时注入的。这些驱动程序不一定建立在 ODBC 上,可能是其他东西。
一些 SQL 数据库支持转义查询参数,一些不支持,有些支持预编译语句的“编译”,而其他一些不支持。
通过将其变为两个步骤的过程,driver 可以决定在查询参数中如何转义以及如何将其插入查询中。
> 你可以使用 ODBC 参数,并将参数发送到 db.QueryRow()
和 db.Query()
等函数中。
这些方法的签名仅为方便起见,在后台,驱动程序仍然会分别进行转义和查询。你可以在这里分别查看这两个过程的发生情况:这里 和 这里。
英文:
Keep in mind that the database/sql
package is a package designed to cover the functionality of all theoretically possible SQL database systems without getting in the way of platform-specifics.
SQL driver instances are injected at runtime through sql.Register
. These drivers do not necessarily build on ODBC, it could be something else.
Some SQL-databases support escaping query parameters, some don't – some support "compiling" of prepared statements, others don't.
By making it a 2-step process, the driver can decide where and how to escape query parameters and how they will be inserted into the query.
> you are allowed to use the ODBC parameters and send parameters to such things as db.QueryRow() and db.Query()
The methods have this signature for convenience only, in the background the driver still escapes and queries separately. You can see this happen here and here, respectively.
答案2
得分: 2
理论上,当数据库服务器接收到一个简单的SQL语句(或批处理)时,它必须对其进行编译:将其解析为一些内部形式,然后准备所谓的“查询计划”——执行实际请求所需的操作序列(如索引或表扫描、比较等)。显然,所有这些都需要一定的服务器资源。因此,许多数据库管理系统开始支持“预编译语句”:服务器只需进行一次解析/计划步骤,然后将结果交给你,你随后可以多次“调用”它,只需提供不同的参数。
现在让我们进入一个更复杂的领域。
首先要注意的是,随着内存和处理能力变得更便宜,运行在普通到高端服务器上的数据库管理系统能够在处理查询时花费更多的资源,因此其中一些系统会缓存用户查询。也就是说,当你执行一个简单的查询(一个SQL语句或批处理)时,服务器会进行所有常规的解析/查询计划,然后保存结果,如果以后遇到相同的查询,它会跳过处理部分,直接对数据执行操作。
第二点要注意的是,虽然编程语言/库倾向于为程序员提供一种特定的通用接口来访问数据库引擎(C
的ODBC是一个例子,Go
的database/sql
是另一个例子,还有无数其他接口),但实际访问服务器的传输协议或其他方式可能差异很大。例如,一个数据库服务器可能支持在其传输协议中传递参数,而另一个数据库服务器可能不支持,因此访问层必须将参数转换为转义的SQL文字,并将它们嵌入到查询中,然后发送给服务器。
你应该从中获得的主要思想是:数据库引擎是不同的,如果你计划通过程序对其施加大量负载,你必须了解它的内部情况。了解你的DMBS的传输协议的功能和限制,了解你的DBMS在建立连接时是否快速等等。查找有关你特定的DBMS性能优化的文档。
还可以参考这个链接和这个链接来了解不同数据库管理系统的一些讨论。
英文:
In theory, when a database server receives a simple SQL statement (or batch) it has to compile it: parse it into some internal form and then prepare the so-called "query plan" — the sequence of operations (such as index or table scans, comparisons etc) to carry out actual request. Doing all this obviously costs cerain server resources. So many DBMSes started to support "prepared statements": the server does the parsing/planning step exactly once and hands you off "a handle" onto the result which you subsequently "call" multiple times, just supplying different parameters.
Now let's move to a more complicated realm.
First thing to note is that when memory and processing power got cheaper, DBMSes running on commodity to high-end servers became able to afford to spend more resources when dealing with queries, so some of them cache user queries. That is, when you do a simple query (an SQL statement or a batch) the server does all the usual parsing/query planning but then it saves the results and if it later encounters the same query, it skips the processing part and just performs operations on the data.
The second thing to note is that while programming languages/libraries tend to present the programmer with a certain common interface for accessing database engines (ODBC for C
is one example, the database/sql
for Go
is another, and there are myriads others), the wire protocols or other means of actual accessing the servers may differ drastically. For instance, one database server might support passing parameters along with the query in its wire protocol while another one might not and so the access layer have to turn your parameters into escaped SQL literals and embed them into the query which is then sent to the server.
The main idea you should aquire from all this: the database engines are different and you have to know the ins and outs of yours if you are planning for putting intensive load on it by your programs. Know what the wire protocol of your DMBS can do and what it can't, know if your DBMS is fast at connection establishing or not etc etc etc. Look for documentation on performance optimization of your particular DBMS.
See also this and this for some discussions of different DBMSes.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论