英文:
How to pass parameters to a query written in PL/pgSQL?
问题
我想知道是否可以将参数传递给使用PL/pgSQL编写的查询?
我尝试了这个,但是出现了错误pq: got 1 parameters but the statement requires 0
package main
import (
"database/sql"
"fmt"
"log"
_ "github.com/lib/pq"
)
func main() {
db, err := sql.Open("postgres", "host=localhost dbname=db user=user sslmode=disable password=pw")
if err != nil {
log.Fatal(err)
}
row := db.QueryRow(`
DO $$
BEGIN
IF true THEN
SELECT $1;
END IF;
END$$
`, 1)
var num int
err = row.Scan(&num)
if err != nil {
log.Fatal(err)
}
fmt.Println(num)
}
另一个相关的问题是,我想使用事务,但是sql
包提供的API似乎在每次执行查询时都会连接到数据库。如果可能的话,我希望所有操作都在一个事务中执行。例如,使用Go语言,你应该像这样使用事务:
tx, err := db.Begin()
rows, err := tx.Query(sql1)
result, err := tx.Exec(sql2)
tx.Commit()
问题是,调用tx.Query
和tx.Exec
会导致两次与PostgreSQL服务器的通信,如果我没记错的话。我想要实现的是将sql1
和sql2
合并,将它们包装在BEGIN
和END
之间,并在一次通信中执行它们。我的问题是:
- 你认为这是必要的吗?我想象中,在足够的流量下,性能差异可能是明显的,但我不确定。
- 如果是必要的,最佳的执行合并事务的方法是什么?创建一个函数,在PL/pgSQL中运行事务(因为我可能需要使用条件语句等)?
英文:
I wonder if it's possible to pass parameters to a query written in PL/pgSQL?
I tried this, but it failed with pq: got 1 parameters but the statement requires 0
package main
import (
"database/sql"
"fmt"
"log"
_ "github.com/lib/pq"
)
func main() {
db, err := sql.Open("postgres", "host=localhost dbname=db user=user sslmode=disable password=pw")
if err != nil {
log.Fatal(err)
}
row := db.QueryRow(`
DO $$
BEGIN
IF true THEN
SELECT $1;
END IF;
END$$
`, 1)
var num int
err = row.Scan(&num)
if err != nil {
log.Fatal(err)
}
fmt.Println(num)
}
Another related question is that I want to use transactions, but the APIs provided by the sql
package seems to connect to db every time a query is executed in a tx. I'd like everything to be executed in one go if that's possible. For example, with go you are supposed to use transactions like this
tx, err := db.Begin()
rows, err := tx.Query(sql1)
result, err := tx.Exec(sql2)
tx.Commit()
The problem is that calling tx.Query
and tx.Exec
makes two trips to PostgreSQL server if I'm not wrong. What I want to achieve is to merge sql1
and sql2
, wrap them inside BEGIN
and END
and execute them in one trip. And my question is that:
- Do you think it's necessary? I imagine that with enough traffic, the performance difference might be obvious, but I'm not sure.
- If so, what's the best way to execute this merged transaction? Create a function and run the transaction inside PL/pgSQL (since I might need to use conditional statements, etc)?
答案1
得分: 3
你之所以出错是因为 PL/pgSQL 应该在服务器端定义为“函数”或“过程”,但在你的情况下,它是从客户端调用的。下面是一个简单的示例,展示了如何定义和调用带有参数的函数:
CREATE OR REPLACE FUNCTION myadd(a integer, b integer) RETURNS integer AS $$
BEGIN
RETURN a + b;
END;
$$ LANGUAGE plpgsql;
然后,你可以在客户端使用 SELECT
查询来调用带有参数的函数。请注意,即使你的函数包含 INSERT/UPDATE
操作,也必须使用 SELECT
语句来调用函数。
//...
a := 10
row := db.QueryRow(`SELECT * FROM myadd($1, $2)`, a, 130)
//...
关于事务和 PL/pgSQL 的问题。是的,使用 PL/pgSQL 可以减少网络流量。服务器端语言(PL/pgSQL)的几个优点包括:
- 消除客户端与服务器之间的往返通信。
- 不需要将中间结果传输到客户端,只传输最终结果。
- 避免多次解析查询(发送查询到服务器 -> 服务器解析查询 -> 执行数据库操作 -> 将结果返回给客户端,等等)。
在处理数据库(大数据)时,规则是“尽量避免在数据之间移动”,而 PL/pgSQL 符合这个规则。然而,有些情况下你不能(需要避免)使用 PL/pgSQL,例如数据库管理员/服务器所有者不允许服务器端编程(出于安全性/性能等原因)。
关于“函数”和“事务”的关系在手册中有明确说明:
不要将 PL/pgSQL 中的 BEGIN/END 用于与 SQL 命令中的事务控制相混淆。PL/pgSQL 的 BEGIN/END 只用于分组语句,它们不会开始或结束事务。函数和触发器过程总是在由外部查询建立的事务中执行 - 它们不能开始或提交该事务,因为它们没有执行的上下文。然而,包含 EXCEPTION 子句的块实际上形成了一个可以回滚的子事务,而不会影响外部事务。
总之,使用 PL/pgSQL 可能会提高性能。具体提升多少取决于情况。请记住,在使用 PL/pgSQL 后,你需要管理多个代码库,有时很难进行调试。
英文:
You got error because PL/pgSQL is supposed to be defined in server side as function
or procedure
, but in your case, its being called from client side. Below is a simple example on how to define and call the function with parameter(s):
CREATE OR REPLACE FUNCTION myadd(a integer, b integer) RETURNS integer AS $$
BEGIN
RETURN a + b;
END;
$$ LANGUAGE plpgsql;
Then, from client side you can call the function with parameters using SELECT
query. Please note, even though your function contains INSERT/UPDATE
, the function must be called using SELECT
statement.
//...
a := 10
row := db.QueryRow(`SELECT * FROM myadd($1, $2)`, a, 130)
//...
Next question, about transaction and PL/pgSQL. Yes, using PL/pgSQL you can reduce network traffic. Several advantages of server side language (PL/pgSQL) are:
- Eliminate client-server round trip
- No need to transfer intermediate result to client, only the final result will be transferred.
- Avoid parsing queries multiple times (Send query to server --> server parsing query --> perform database operation --> return result to client, etc...)
The rule when dealing with database (large data) is You need to avoid to move your data around
and PL/pgSQL fits this rule. However, there are some circumstances in which you can not (need to avoid) use PL/pgSQL, e.g. DB admin/server owner does not allow server side programming (security/performance reason etc).
Relation between function
and transaction
is clearly stated in the manual :
> It is important not to confuse the use of BEGIN/END for grouping statements in PL/pgSQL with the similarly-named SQL commands for transaction control. PL/pgSQL's BEGIN/END are only for grouping; they do not start or end a transaction. Functions and trigger procedures are always executed within a transaction established by an outer query — they cannot start or commit that transaction, since there would be no context for them to execute in. However, a block containing an EXCEPTION clause effectively forms a subtransaction that can be rolled back without affecting the outer transaction
In summary, using PL/pgSQL you may get performance improvement. How much? It's depend. Please keep in mind, after using PL/pgSQL you need to manage more than one codebase, and sometimes it's difficult to debug.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论