我在数据库中有一个表:
create table store (
...
n_status integer not null,
t_tag varchar(4)
t_name varchar,
t_description varchar,
dt_modified timestamp not null,
...
);
在我的存储函数中,我需要对该表多次执行相同的
select
:
select * from store
where n_place_id = [different values]
and t_tag is not null
and n_status > 0
and (t_name ~* t_search or t_description ~* t_search)
order by dt_modified desc
limit n_max;
这里,
t_search
和n_max
是存储函数的参数。我认为为此使用准备好的语句是有意义的,但我遇到了奇怪的问题。这是我所拥有的:
create or replace function fn_get_data(t_search varchar, n_max integer)
returns setof store as
$body$
declare
resulter store%rowtype;
mid integer;
begin
prepare statement prep_stmt(integer) as
select *
from store
where n_place_id = $1
and (t_name ~* t_search or t_description ~* t_search)
order by dt_modified
limit n_max;
for mid in
(select n_place_id from ... where ...)
loop
for resulter in
execute prep_stmt(mid)
loop
return next resulter;
end loop;
end loop;
end;$body$
language 'plpgsql' volatile;
但是当我实际运行该函数时
select * from fn_get_data('', 30)
我收到此错误:
ERROR: column "t_search" does not exist
LINE 3: and (t_name ~* t_search or t_description ~* t_search)
^
QUERY: prepare prep_stmt(integer) as
select * from store where n_status > 0 and t_tag is not null and n_museum = $1
and (t_name ~* t_search or t_description ~* t_search)
order by dt_modified desc limit maxres_free
好吧,也许它不喜欢准备好的语句中的外部变量,所以我将其更改为
prepare prep_stmt(integer, varchar, integer) as
select * from store where n_status > 0 and t_tag is not null and n_museum = $1
and (t_name ~* $2 or t_description ~* $2)
order by dt_modified desc limit $3
...
for resulter in
execute prep_stmt(mid, t_search, n_max)
...
这次我得到了一个不同的错误:
ERROR: function prep_stmt(integer, character varying, integer) does not exist
LINE 1: SELECT prep_stmt(mid, t_search, n_max)
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
QUERY: SELECT prep_stmt(mid, t_search, n_max)
我在这里缺少什么?
编辑我在顶部添加了相关的表结构。
在我看来,动态 SQL 的 PL/PgSQL
EXECUTE
胜过预准备语句的常规 SQL EXECUTE
。
代码:
create or replace function prep_test() returns void as $$
begin
PREPARE do_something AS SELECT 1;
EXECUTE do_something;
end;
$$ LANGUAGE 'plpgsql';
测试:
regress=# select prep_test(1);
ERROR: column "do_something" does not exist
LINE 1: SELECT do_something
^
QUERY: SELECT do_something
CONTEXT: PL/pgSQL function "prep_test" line 4 at EXECUTE statement
在 PL/PgSQL 之外它工作正常:
regress=# EXECUTE do_something;
?column?
----------
1
(1 row)
我不确定如何在 PL/PgSQL 中执行准备好的语句。
出于兴趣,为什么您尝试在 PL/PgSQL 中使用准备好的语句?无论如何,计划都是为 PL/PgSQL 准备和缓存的,它会自动发生。
有一种方法可以在函数中
EXECUTE
准备好语句,但就像接受的答案所说的那样,您通常不想在函数中执行此操作,因为该函数已经存储了其计划。
话虽如此,仍然存在需要在函数中使用准备好的语句的用例。我的用例是为不同用户使用多个架构,其中架构包含名称相似的表,并且您希望根据
search_path
设置的内容使用相同的函数来访问这些表之一。在这种情况下,由于函数存储其计划的方式,在更改 search_path
后使用相同的函数会导致事情中断。我已经说过这个问题有两种解决方案。第一个是使用EXECUTE '<Your query as a string here>'
。但对于大型查询来说,这可能会变得非常难看,因此使用第二种方法的原因,其中涉及 PREPARE
。
因此,了解了“为什么”您想要这样做的背景后,以下是方法:
CREATE OR REPLACE FUNCTION prep_test()
RETURNS void AS $$
BEGIN
PREPARE do_something AS SELECT 1;
EXECUTE 'EXECUTE do_something;';
END;
$$ LANGUAGE plpgsql;
尽管添加一些保护措施以防止其损坏可能符合您的最大利益。比如:
CREATE OR REPLACE FUNCTION prep_test()
RETURNS void AS $$
BEGIN
IF (SELECT count(*) FROM pg_prepared_statements WHERE name ilike 'do_something') > 0 THEN
DEALLOCATE do_something;
END IF;
PREPARE do_something AS SELECT 1;
EXECUTE 'EXECUTE do_something;';
DEALLOCATE do_something;
END;
$$ LANGUAGE plpgsql;
再次强调,那些认为自己想要这样做的人通常可能不应该这样做,但对于那些需要这样做的情况,这就是你这样做的方式。
您可以在 PLPGSQL 中使用这样的 EXECUTE 语句:
select magicvalue into str_execute from magicvalues where magickey = ar_requestData[2];
EXECUTE str_execute into str_label USING ar_requestData[3], ar_requestData[4]::boolean, ar_requestData[5]::int, ar_requestData[6];
这是我在应用程序中使用的代码。 ar_requestData 是一个包含文本值的数组。 在表 magicvalues 中,我存储诸如准备好的语句之类的东西。 select语句例如:
insert into classtypes(label, usenow, ranking, description) values($1,$2,$3,$4) returning label'
谨致问候,
洛克·伯格曼
plpgsql 中不允许使用 PREPARE 语句。您可以拼接函数内的所有语句,然后使用动态执行。这是一个例子。
create or replace function sp_test(f_total int) returns void as $ytt$
declare v_sql text;
declare i int;
begin
v_sql:='prepare ytt_s1 (int,timestamp) as select * from tbl1 where id = $1 and log_time = $2;';
while i < f_total
loop
v_sql:=v_sql||'execute ytt_s1('||i||',now());';
i := i + 1;
end loop;
v_sql:=v_sql||'deallocate ytt_s1;';
execute v_sql;
end;
$ytt$ language plpgsql;
我创建了
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);
然后,我使用
my_pre()
语句创建了准备好的语句 PREPARE
,该语句可以动态更新 age
与 id
表中的 person
,如下所示:
PREPARE my_pre(INT, INT) AS
UPDATE person SET age = $1 WHERE id = $2;
然后,我创建并调用了这些
my_func()
函数,这些函数尝试使用 EXECUTE 语句运行
my_pre()
,如下所示:
CREATE FUNCTION my_func() RETURNS VOID
AS $$
BEGIN
EXECUTE my_pre(45, 2); -- Here
END;
$$ LANGUAGE plpgsql;
SELECT my_func();
CREATE FUNCTION my_func(my_age INT, my_id INT) RETURNS VOID
AS $$
BEGIN
EXECUTE my_pre(my_age, my_id); -- Here
END;
$$ LANGUAGE plpgsql;
SELECT my_func(45, 2);
CREATE FUNCTION my_func(my_age INT, my_id INT) RETURNS VOID
AS $$
BEGIN
EXECUTE my_pre($1, $2) USING my_age, my_id; -- Here
END;
$$ LANGUAGE plpgsql;
SELECT my_func(45, 2);
但是我遇到了下面同样的错误:
错误:函数my_pre(整数,整数)不存在
此外,我创建并调用了
my_func()
函数,该函数尝试使用 my_pre()
语句运行 EXECUTE
,如下所示:
CREATE FUNCTION my_func(my_age INT, my_id INT) RETURNS VOID
AS $$
BEGIN
EXECUTE 'EXECUTE my_pre($1, $2)' USING my_age, my_id; -- Here
END;
$$ LANGUAGE plpgsql;
SELECT my_func(45, 2);
但是我收到以下错误:
错误:没有参数$1
但是最后,我可以创建
my_func()
函数,该函数使用 my_pre()
语句成功运行 EXECUTE
,如下所示:
CREATE FUNCTION my_func() RETURNS VOID
AS $$
BEGIN
EXECUTE 'EXECUTE my_pre(45, 2)'; -- Here
END;
$$ LANGUAGE plpgsql;
SELECT my_func();
如果没有
my_pre()
,直接使用 'UPDATE ...'
语句运行 EXECUTE
也可以,如下所示。 *UPDATE ...
没有 ''
会出现语法错误:
CREATE FUNCTION my_func() RETURNS VOID
AS $$
BEGIN
EXECUTE 'UPDATE person SET age = 45 WHERE id = 2'; -- Here
END;
$$ LANGUAGE plpgsql;
SELECT my_func();
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; -- Here
END;
$$ LANGUAGE plpgsql;
SELECT my_func(45, 2);
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; -- Here
END;
$$ LANGUAGE plpgsql;
SELECT my_func(45, 2);
此外,如果没有
my_pre()
和函数或过程,直接使用 'UPDATE ...'
语句运行 EXECUTE
会出现语法错误:
EXECUTE 'UPDATE person SET age = 45 WHERE id = 2';
而且,我发现 PL/pgSQL 函数或过程的 EXECUTE 语句的语法(语言 plpgsql) 如下所示。 *
EXECUTE
语句不能在SQL函数或过程中使用(LANGUAGE SQL
)否则会出现错误:
EXECUTE command-string [ INTO [STRICT] target ] [ USING expression [, ... ] ];
并且,我发现带有准备好的语句且没有 PL/pgSQL 函数或过程的 EXECUTE 语句的语法,如下所示:
EXECUTE name [ ( parameter [, ...] ) ]