pri*_*nce 9 postgresql resultset execute dynamic-sql plpgsql
如何编写包含动态生成的SQL语句的存储过程,该语句返回结果集?这是我的示例代码:
CREATE OR REPLACE FUNCTION reporting.report_get_countries_new (
starts_with varchar,
ends_with varchar
)
RETURNS TABLE (
country_id integer,
country_name varchar
) AS
$body$
DECLARE
starts_with ALIAS FOR $1;
ends_with ALIAS FOR $2;
sql VARCHAR;
BEGIN
sql = 'SELECT * FROM lookups.countries WHERE lookups.countries.country_name >= ' || starts_with ;
IF ends_with IS NOT NULL THEN
sql = sql || ' AND lookups.countries.country_name <= ' || ends_with ;
END IF;
RETURN QUERY EXECUTE sql;
END;
$body$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 100 ROWS 1000;
Run Code Online (Sandbox Code Playgroud)
此代码返回错误:
ERROR: syntax error at or near "RETURN"
LINE 1: RETURN QUERY SELECT * FROM omnipay_lookups.countries WHERE o...
^
QUERY: RETURN QUERY SELECT * FROM omnipay_lookups.countries WHERE omnipay_lookups.countries.country_name >= r
CONTEXT: PL/pgSQL function "report_get_countries_new" line 14 at EXECUTE statement
Run Code Online (Sandbox Code Playgroud)
我尝试过其他方式而不是这个:
RETURN QUERY EXECUTE sql;
Run Code Online (Sandbox Code Playgroud)
方式1:
RETURN EXECUTE sql;
Run Code Online (Sandbox Code Playgroud)
方式2:
sql = 'RETURN QUERY SELECT * FROM....
/*later*/
EXECUTE sql;
Run Code Online (Sandbox Code Playgroud)
在所有情况下都没有成功.
最后,我想编写一个包含动态sql语句的存储过程,并从动态sql语句返回结果集.
Erw*_*ter 29
有改进的余地:
CREATE OR REPLACE FUNCTION report_get_countries_new (starts_with text
, ends_with text = NULL)
RETURNS SETOF lookups.countries AS
$func$
DECLARE
sql text := 'SELECT * FROM lookups.countries WHERE country_name >= $1';
BEGIN
IF ends_with IS NOT NULL THEN
sql := sql || ' AND country_name <= $2';
END IF;
RETURN QUERY EXECUTE sql
USING starts_with, ends_with;
END
$func$ LANGUAGE plpgsql;
-- the rest is default settings
Run Code Online (Sandbox Code Playgroud)
PostgreSQL 8.4引入了该USING
子句EXECUTE
,由于几个原因这很有用.回顾一下手册:
命令字符串可以使用参数值,这些参数值在命令中引用为
$1, $2
,等等.这些符号表示USING
子句中提供的值.这种方法通常比将数据值作为文本插入命令字符串更好:它避免了将值转换为文本和返回的运行时开销,并且它不太容易受到SQL注入攻击,因为不需要引用或逃跑.
IOW,它比使用参数的文本表示构建查询字符串更安全,更快,即使在使用时进行清理quote_literal()
.
请注意,$1, $2
在查询字符串中,请参阅USING
子句中提供的值,而不是函数参数.
当您返回时SELECT * FROM lookups.countries
,您可以简化RETURN
声明,如演示:
RETURNS SETOF lookups.countries
Run Code Online (Sandbox Code Playgroud)
在PostgreSQL中,有一个自动为每个表定义的复合类型.用它.结果是函数取决于类型,如果您尝试更改表,则会收到错误消息.在这种情况下删除并重新创建功能.
这可能是也可能不是 - 这通常是!如果改变表格,您希望了解副作用.你拥有它的方式,你的函数会默默地破坏并在下一次调用时引发异常.
如果为声明中的第二个参数提供显式默认值,则可以(但不必)简化调用,以防您不想设置上限ends_with
.
SELECT * FROM report_get_countries_new('Zaire');
Run Code Online (Sandbox Code Playgroud)
代替:
SELECT * FROM report_get_countries_new('Zaire', NULL);
Run Code Online (Sandbox Code Playgroud)
请注意此上下文中的函数重载.
即使容忍(目前),也不要引用语言名称.这是一个标识符.'plpgsql'
您可以在声明时分配变量.节省额外的一步.
参数在标题中命名.删除无意义的行:
starts_with ALIAS FOR $1;
ends_with ALIAS FOR $2;
Run Code Online (Sandbox Code Playgroud)使用quote_literal()来避免SQL注入(!!!)并修复引用问题:
CREATE OR REPLACE FUNCTION report_get_countries_new (
starts_with varchar,
ends_with varchar
)
RETURNS TABLE (
country_id integer,
country_name varchar
) AS
$body$
DECLARE
starts_with ALIAS FOR $1;
ends_with ALIAS FOR $2;
sql VARCHAR;
BEGIN
sql := 'SELECT * FROM lookups.countries WHERE lookups.countries.country_name ' || quote_literal(starts_with) ;
IF ends_with IS NOT NULL THEN
sql := sql || ' AND lookups.countries.country_name <= ' || quote_literal(ends_with) ;
END IF;
RETURN QUERY EXECUTE sql;
END;
$body$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 100 ROWS 1000;
Run Code Online (Sandbox Code Playgroud)
这是在9.1版中测试的,工作正常.
归档时间: |
|
查看次数: |
41318 次 |
最近记录: |