何时在 PL/pgSQL 函数中使用 `EXECUTE`?

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

我创建了

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);

然后使用 EXECUTE 语句,我使用

my_func()
my_age
参数创建了
my_id
,如下所示:

CREATE FUNCTION my_func(my_age INT, my_id INT) RETURNS VOID AS $$
BEGIN                -- ↑↑↑↑↑↑      ↑↑↑↑↑
  EXECUTE 'UPDATE person SET age = $1 WHERE id = $2' USING my_age, my_id;
END;
$$ LANGUAGE plpgsql;

或者:

CREATE FUNCTION my_func(my_age INT, my_id INT) RETURNS VOID AS $$
BEGIN
  EXECUTE 'UPDATE person SET age = $1 WHERE id = $2' USING $1, $2;
END;
$$ LANGUAGE plpgsql;

然后,调用

my_func()
可以将
age
David
更新为
56
,如下所示:

postgres=# SELECT my_func(56, 2);
 my_func
---------

(1 row)

postgres=# SELECT * FROM person;
 id | name  | age
----+-------+-----
  1 | John  |  27
  2 | David |  56
(2 rows)

接下来,在没有

EXECUTE
语句的情况下,我使用
my_func()
my_age
参数创建了
my_id
,如下所示:

CREATE FUNCTION my_func(my_age INT, my_id INT) RETURNS VOID AS $$
BEGIN                -- ↑↑↑↑↑↑      ↑↑↑↑↑
  UPDATE person SET age = my_age WHERE id = my_id;
END;
$$ LANGUAGE plpgsql;

或者:

CREATE FUNCTION my_func(my_age INT, my_id INT) RETURNS VOID AS $$
BEGIN
  UPDATE person SET age = $1 WHERE id = $2;
END;
$$ LANGUAGE plpgsql;

然后,调用

my_func()
可以将
age
David
更新为
56
,如下所示:

postgres=# SELECT my_func(56, 2);
 my_func
---------

(1 row)

postgres=# SELECT * FROM person;
 id | name  | age
----+-------+-----
  1 | John  |  27
  2 | David |  56
(2 rows)

那么,有和没有

EXECUTE
语句的函数有什么区别?

sql database postgresql plpgsql dynamic-sql
2个回答
0
投票

使用 EXECUTE 语句,您可以使用

my_func()
age
参数创建
id
,它们与
age
id
列同名,如下所示:

CREATE FUNCTION my_func(age INT, id INT) RETURNS VOID AS $$
BEGIN                -- ↑↑↑      ↑↑
  EXECUTE 'UPDATE person SET age = $1 WHERE id = $2' USING age, id;
END;                      -- ↑↑↑            ↑↑
$$ LANGUAGE plpgsql;

或者:

CREATE FUNCTION my_func(age INT, id INT) RETURNS VOID AS $$
BEGIN                -- ↑↑↑      ↑↑
  EXECUTE 'UPDATE person SET age = $1 WHERE id = $2' USING $1, $2;
END;                      -- ↑↑↑            ↑↑
$$ LANGUAGE plpgsql;

然后,调用

my_func()
可以将
age
David
更新为
56
,如下所示:

postgres=# SELECT my_func(56, 2);
 my_func
---------

(1 row)

postgres=# SELECT * FROM person;
 id | name  | age
----+-------+-----
  1 | John  |  27
  2 | David |  56
(2 rows)

如果没有

EXECUTE
语句,您仍然可以使用
my_func()
age
参数创建
id
,它们与
age
id
列同名,如下所示:

CREATE FUNCTION my_func(age INT, id INT) RETURNS VOID AS $$
BEGIN                -- ↑↑↑      ↑↑
  UPDATE person SET age = age WHERE id = id;
END;                   -- ↑↑↑       ↑↑
$$ LANGUAGE plpgsql;

或者:

CREATE FUNCTION my_func(age INT, id INT) RETURNS VOID AS $$
BEGIN                -- ↑↑↑      ↑↑
  UPDATE person SET age = $1 WHERE id = $2;
END;             -- ↑↑↑            ↑↑
$$ LANGUAGE plpgsql;

但是,调用上面的

my_func()
会得到下面的错误

错误:列引用“id”不明确


0
投票

有和没有

EXECUTE
语句的函数有什么区别?

PL/pgSQL 中的

EXECUTE
用于动态 SQL 用于动态构建和执行 SQL 语句。主要涉及来自其他角色的用户或系统目录的输入,而不仅仅是 values - 即 identifiers
 或 SQL 
code
 元素。您通常不会使用 
EXECUTE
,除非您需要 
that

一个例外:虽然 PL/pgSQL 可以保存和重用 SQL DML 语句的通用查询计划(很像准备好的语句,但只有在执行几次之后,才会有一些复杂性),

EXECUTE

 可以防止这种情况发生并强制执行
每次执行都有一个新的查询计划。这可以(ab)用于高度不规则的数据分布,其中查询计划针对给定的输入进行了更好的优化(Postgres 本身并没有意识到这一点)。

很难想象这可以适用于您的简单示例。如果

person.id

是PK的话,那肯定不是。基本上,带有 
EXECUTE
 的版本是(更昂贵、更复杂、更容易出错)废话。

相关:

  • 功能表现
  • PostgreSQL 函数中的语言 SQL 与语言 plpgsql
另一个(不重要)副作用:查询字符串在单独的作用域中执行,因此周围代码块的变量和参数在那里不可见。您可以使用

USING

 子句传递值。但您当然不需要这种副作用来隐藏命名冲突。只需表限定列名即可。参见:

  • 如何在 PostgreSQL 中的函数内返回 SELECT 的结果?
始终小心连接动态 SQL,以防御可能的

SQL 注入!参见:

  • Postgres 函数中的 SQL 注入与准备好的查询
  • 在 PL/pgSQL 中演示 SQL 注入
此外,

age

作为表格列通常是无意义的。而是存储生日。

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