我创建了
person
表,然后在其中插入了 2 行,如下所示:
CREATE TABLE person (
id INT,
name VARCHAR(20),
age INT
);
INSERT INTO person (id, name, age)
VALUES (1, 'John', 27), (2, 'David', 32);
然后,我使用
PREPARE 语句创建了准备好的语句
my_pre
,它可以用 age
表中的 id
更新 person
,如下所示:
PREPARE my_pre(INT, INT) AS
UPDATE person SET age = $1 WHERE id = $2;
然后,我使用
EXECUTE 语句创建了
my_func()
,其中包含 EXECUTE
语句来动态运行 my_pre($1, $2)
,如下所示:
CREATE FUNCTION my_func(age INT, id INT) RETURNS VOID
AS $$
BEGIN
EXECUTE 'EXECUTE my_pre($1, $2)' USING age, id;
END;
$$ LANGUAGE plpgsql;
但是,调用
my_func()
时出现如下错误:
postgres=# SELECT my_func(45, 2);
ERROR: there is no parameter $1
LINE 1: EXECUTE my_pre($1, $2)
^
QUERY: EXECUTE my_pre($1, $2)
CONTEXT: PL/pgSQL function my_func(integer,integer) line 3 at EXECUTE
其实,当我直接将
45
中的2
和my_pre
设置为my_func()
时如下图:
CREATE FUNCTION my_func() RETURNS VOID
AS $$
BEGIN
EXECUTE 'EXECUTE my_pre(45, 2)';
END; -- ↑↑ ↑
$$ LANGUAGE plpgsql;
然后,我可以毫无错误地调用
my_func()
,然后age
的David
更新为45
,如下所示:
postgres=# SELECT my_func();
my_func
---------
(1 row)
postgres=# SELECT * FROM person;
id | name | age
----+-------+-----
1 | John | 27
2 | David | 45
(2 rows)
或者,我创建了
my_func1()
,它可以在age
表中用id
更新person
,如下所示:
CREATE FUNCTION my_func1(age INT, id INT) RETURNS VOID
AS $$
BEGIN
EXECUTE 'UPDATE person SET age = $1 WHERE id = $2' USING age, id;
END;
$$ LANGUAGE plpgsql;
然后,我使用
my_func2()
语句创建了 EXECUTE
,该语句具有 SELECT 语句来动态调用 my_func1($1, $2)
,如下所示:
CREATE FUNCTION my_func2(age INT, id INT) RETURNS VOID
AS $$
BEGIN
EXECUTE 'SELECT my_func1($1, $2)' USING age, id;
END;
$$ LANGUAGE plpgsql;
然后,我可以毫无错误地调用
my_func2()
,然后age
的David
更新为45
,如下所示:
postgres=# SELECT my_func2(45, 2);
my_func2
----------
(1 row)
postgres=# SELECT * FROM person;
id | name | age
----+-------+-----
1 | John | 27
2 | David | 45
(2 rows)
那么,如何在
my_pre($1, $2)
中动态运行准备好的语句 my_func()
?
SQL 准备好的语句不应从 PL/pgSQL 执行。每个嵌入式 SQL 都是默认准备的。 PL/pgSQL 中的
EXECUTE
命令与 SQL EXECUTE
命令不同(其他一些数据库使用不同的访问权限,但 Postgres 使用类似于 Oracle 的 API)。
不幸的是,SQL语句
EXECUTE
不允许使用参数(只有SELECT
和CALL
语句允许使用参数):
(2024-02-18 06:27:01) postgres=# execute x($1,$2) \bind 10 20 \g
ERROR: 08P01: bind message supplies 2 parameters, but prepared statement "" requires 0
LOCATION: exec_bind_message, postgres.c:1716
所以你不能从 PL/pgSQL 传递参数
EXECUTE
。您可以使用 format
函数在查询级别执行此操作:
EXECUTE format('execute x(%L, %L)', age, id);
这也很安全。
动态 SQL 的模型与其他一些数据库使用的模型不同。原因很简单。 PL/pgSQL 语句同时准备和执行,然后不执行,因此它将被称为已准备好的语句。也许Oracle的设计不那么混乱,因为他们使用语法
EXECUTE IMMEDIATELY
,但现在改变它已经太晚了。今天可以增强 SQL EXECUTE
以支持参数化(SQL 语句 CALL
可以做到这一点),但没有人写它(可能没有太强大、太有趣的用例)。