我们可以在不使用EXECUTE
的情况下选择PostgreSQL函数吗?
我正在尝试使用quote_ident()
来创建动态SQL,但它不起作用。
CREATE OR REPLACE FUNCTION select_server(p_id text)
RETURNS integer AS $$
DECLARE
serialnum_value INTEGER;
STATEMENT TEXT;
BEGIN
STATEMENT := 'tbl' || substr($1, 1, 4);
SELECT serialnum INTO serialnum_value FROM quote_ident(STATEMENT ) WHERE id = $1;
RETURN serialnum;
END;
有没有人知道如何在不使用EXECUTE
的情况下从PostgreSQL函数中选择动态表?
答案 0 :(得分:1)
根据igor的评论,你不能避免在这里执行。最好的选择是创建一个创建函数的函数,然后可以根据需要调用该函数。例如:
create function gen_select_server(p_id text)
returns boolean
as $gen$
begin
execute $exec$
create function $exec$ || quote_ident('select_server_' || p_id) || $exec$
returns integer
as $body$
begin
return serialnum
from $exec$ || quote_ident('tbl_' || p_id) || $exec$
where id = $exec$ || quote_literal('id_' || p_id) || $exec$;
end;
$body$ language plpgsql;
$exec$;
return true;
end;
$gen$ language plpgsql;
-- usage:
select gen_select_server('1234'); -- call this only once; uses execute
select * from select_server_1234(); -- no execute here
如上所述,字符串分隔符可能会变得混乱。另请注意,上述内容不是您的原始功能 - 它主要用于说明如何在大$exec$
“字符串”块中正确引用内容。
我建议坚持使用执行,这就是说。或者使用ORM,就此而言。保持这些微观优化可能比表现上的表面上的好处更糟糕。
答案 1 :(得分:1)
EXECUTE
中,如果没有PL/pgSQL,则无法执行动态SQL。这就是首先让它成为动态
但是你可以简化你的功能:
CREATE OR REPLACE FUNCTION select_server(p_id text, OUT serial_value integer) AS
$func$
BEGIN
EXECUTE 'SELECT serialnum FROM tbl' || left($1::text, 4)) || ' WHERE id = $1'
USING $1
INTO serial_value;
END
$func$ LANGUAGE plpgsql STRICT;
Normally you would have to sanitize any identifier.
但是,在这种特殊情况下,您不需要 quote_ident()
,因为唯一的动态组件是来自integer
的4位数字。这种方式既不可能是SQL注入也不是非法标识符。
使用p_id
子句将USING
的值作为值传递。
减少作业数量。那些在plpgsql中相对昂贵。您只需要一个SQL语句即可完成所有操作。
OUT
参数有助于缩短语法。
我还创建了函数STRICT
(RETURNS NULL ON NULL INPUT
),因为NULL
作为输入是没有意义的。
left()
略快于substr()
。