原始的PL / pgSQL匿名块,要执行的代码如下:
do $$
declare
tt integer[];
minRowNum integer;
maxRowNum integer;
MIN_TEMS constant integer := 1;
MAX_TEMS constant integer := 15;
LAST_ARR_IDX constant integer := MAX_TEMS * 2;
NUM_FILAS constant integer := 1000;
begin
create temp table NTematica(rownum, tematica_id) as
select
S.n, (S.n * 841)::integer
from
generate_series(1,357) S(n);
select
min(X.rownum), max(X.rownum) into minRowNum, maxRowNum
from
NTematica X;
prepare selectTematicasPlan(integer, integer, integer, integer) as
select
array_agg(X.tematica_id)
from
NTematica X
where
X.rownum in
(
select
trunc(random() * ($2 - $1 + 1) + $1) :: integer
from
generate_series($3, trunc(random() * ($4 - $3 + 1) + $3) :: integer)
);
for i in 1..NUM_FILAS loop
execute selectTematicasPlan(minRowNum, maxRowNum, MIN_TEMS, MAX_TEMS);
raise notice 'First is % and % are the others', tt[1], tt[2:LAST_ARR_IDX];
end loop;
drop table NTematica cascade;
deallocate selectTematicasPlan;
end$$;
然后,执行失败并出现错误:
ERROR: syntax error at or near "("
LINE 34: tt := execute selectTematicasPlan(minRowNum, maxRowNum...
然后,为了测试,我消除了" tt:="并使用此结果再次运行它:
ERROR: function selecttematicasplan(integer, integer, integer, integer) does not exist
LINE 1: SELECT selectTematicasPlan(minRowNum, maxRowNum, MIN_TEMS, M...
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
更新
先谢谢大家。我将澄清一些问题:
我的初始代码是"来自select" (包括CTE)填写一张包含1000万条记录的表格。我可以确认它非常慢。因此,我决定选择一个IMPERATIVE解决方案:创建一个索引的未记录表(像地图数据结构一样工作)并循环1000万次以执行"执行"数据"选择"来自"地图"接着是"插入"数据。我退出"选择"和"插入"循环到准备好的语句中,因为这样可以避免对PG进行1000万次解析工作。发布的代码仅涉及我准备好的陈述的问题。 是否可以使用"准备" +"执行"在pl / pgsql代码块中? PG doc(http://www.postgresql.org/docs/9.4/static/sql-prepare.html,http://www.postgresql.org/docs/9.4/static/sql-execute.html)没有谈论(反对或赞成)pl / pgsql。
注意:我的数据库版本("选择版本()")是: 在x86_64-unknown-linux-gnu上的PostgreSQL 9.4.4,由gcc编译(Ubuntu 4.9.2-10ubuntu13)4.9.2,64位
答案 0 :(得分:1)
在PL / pgSQL函数中,您不使用预准备语句。实际上,函数中的每个语句都是“准备好的”,就像带有PREPARE
命令的语句一样。在一个看起来有点奇怪的匿名代码块(PL / pgSQL)中,因为块只被使用一次然后被丢弃,但实际上它的工作原理是一样的。 The principle is documented here并且引用了PREPARE
语句,它是逻辑,因为行为本质上是相同的:代码块中的语句被解析和缓存以供将来使用。也许有些多余,但考虑到这个问题的各种答案中的无知陈述,我想强调一下上面链接的文件中的句子:
首先在函数中执行每个表达式和SQL命令,PL / pgSQL解释器解析并分析命令以创建预准备语句
简而言之:在plpgsql代码块中显式创建一个预准备语句,然后动态执行该预准备语句是无用的,效率低下且在概念上存在缺陷。
您要做的是准备一个语句,然后由后端准备该准备,然后您动态执行执行预准备语句的语句。这里没有讽刺意味:这就是发生的事情。另请注意,PL / pgSQL EXECUTE
语句无任何缓存:每次调用SQL语句EXECUTE selectTematicasPlan(minRowNum, maxRowNum, MIN_TEMS, MAX_TEMS)
时都会对其进行解析。这是一个相当简单的陈述,但是(如在Klin's answer中)参数值必须每次都传递,即使它们在每次调用时都是相同的。我希望你能看到这种方法效率低下(如果没有,我会休息一下)。
回到你的例子,你的功能应该是这样的:
DO $$
DECLARE
tt integer[];
minRowNum integer := 1;
maxRowNum integer := 357;
MIN_TEMS constant integer := 1;
MAX_TEMS constant integer := 15;
NUM_FILAS constant integer := 1000;
BEGIN
CREATE TEMP TABLE NTematica(rownum, tematica_id) AS
SELECT S.n, (S.n * 841)::integer
FROM generate_series(minRowNum, maxRowNum) S(n);
-- generate_series() produces numbers from the first parameter to the last, inclusive
-- no need to query for those values
select
min(X.rownum), max(X.rownum) into minRowNum, maxRowNum
from
NTematica X;
FOR i IN 1..NUM_FILAS LOOP
SELECT array_agg(X.tematica_id) INTO tt
FROM NTematica X
WHERE X.rownum IN (
SELECT trunc(random() * (maxRowNum - minRowNum + 1) + minRowNum)::integer
FROM generate_series(MIN_TEMS, trunc(random() * (MAX_TEMS - MIN_TEMS + 1) + MIN_TEMS)::integer)
);
RAISE NOTICE 'First is % and % are the others', tt[1], tt[2:array_upper(tt)];
END LOOP;
DROP TABLE NTematica;
END; $$;
您还可以将匿名代码块编写为单个SQL语句,此处使用CTE以提高可读性:
WITH params(minRowNum integer, maxRowNum integer, MIN_TEMS integer, MAX_TEMS integer) AS
SELECT 1, 357, 1, 15
), rowNums(rwNum integer, tematica_id integer) AS (
SELECT S.n, (S.n * 841)::integer
FROM params, generate_series(params.minRowNum, params.maxRowNum) S(n)
)
SELECT tt[1] AS first, tt[2:array_upper(tt)] AS rest
FROM generate_series(1, 1000) ON true
JOIN (
SELECT array_agg(rw.tematica_id) AS tt
FROM params p, rowNums rw
WHERE rw.rwNum IN (
SELECT trunc(random() * (p.maxRowNum - p.minRowNum + 1) + p.minRowNum)::integer
FROM generate_series(p.MIN_TEMS, trunc(random() * (p.MAX_TEMS - p.MIN_TEMS + 1) + p.MIN_TEMS)::integer)
) agg ON true;
多次使用的参数都在顶行,因此易于修改,不存在不一致的风险。这应该比匿名代码块快得多,因为你会失去很多开销,尤其是TEMP TABLE
。显然,您将获得结果作为常规表格数据,而不是1,000条通知。
答案 1 :(得分:0)
它不应该工作,你不能在PL / pgSQL中为预准备语句使用SQL命令。这是无稽之谈 - 默认情况下,PLpgSQL中使用的每个嵌入式SQL语句都是预处理语句。