如何在函数中动态运行带参数的准备好的语句?

问题描述 投票:0回答:1

我创建了

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 database postgresql plpgsql dynamic-sql
1个回答
0
投票

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
可以做到这一点),但没有人写它(可能没有太强大、太有趣的用例)。

© www.soinside.com 2019 - 2024. All rights reserved.