PostgreSQL返回表函数使用动态SQL提示列不存在问题
报错原因
PL/pgSQL中静态SQL和EXECUTE执行的动态SQL,变量解析逻辑完全不同:
- 直接写在函数体内的静态SQL,PL/pgSQL引擎会自动扫描当前函数的作用域,匹配DECLARE块声明的变量、
RETURNS TABLE定义的输出参数,因此静态写SELECT kursor_uczniowie, uczniowie时,引擎能直接识别这两个是函数内的本地变量,不会去数据库表中查找同名列。 - 通过
EXECUTE运行的动态SQL运行在独立的SQL执行上下文,不会自动读取PL/pgSQL函数内的本地变量,你写在动态SQL字符串里的kursor_uczniowie、uczniowie会被当成普通SQL标识符,解析器会去查找当前搜索路径下的表是否存在同名列,找不到自然抛出列不存在的错误。
正确实现方法
动态SQL要引用PL/pgSQL本地变量,最规范的方式是用USING子句传参,动态SQL内部用$1、$2...的位置占位符对应传入的参数即可,修正后的代码如下:
CREATE OR REPLACE FUNCTION get_uczniowie3(klasaId integer) RETURNS TABLE ( kursor_uczniowie refcursor, uczniowie text ) LANGUAGE plpgsql AS $$ DECLARE -- 动态SQL里用位置占位符,不要直接写PL/pgSQL变量名 dsql text := 'SELECT $1, $2'; uczniowie text DEFAULT ''; uczen record; kursor_uczniowie CURSOR(klasaId integer) FOR SELECT imie, nazwisko FROM testy.uczniowie WHERE klasa = klasaId; BEGIN OPEN kursor_uczniowie(klasaId); LOOP FETCH kursor_uczniowie INTO uczen; EXIT WHEN NOT FOUND; uczniowie := uczniowie || ' ' || uczen.imie || ' ' || uczen.nazwisko; END LOOP; -- 注意:如果需要返回可用的refcursor给调用方,不要在此处关闭游标 -- 关闭后的游标无法被调用方FETCH数据,原静态版本也存在这个逻辑问题 -- CLOSE kursor_uczniowie; -- USING后按占位符顺序传入本地变量即可 RETURN QUERY EXECUTE dsql USING kursor_uczniowie, uczniowie; END; $$
额外说明
如果你的动态SQL需要动态拼接标识符(比如动态表名、动态列名),不要用USING传标识符,USING只能传值,这时候要搭配format()函数的%I(标识符转义)占位符处理,避免SQL注入问题。
内容的提问来源于stack exchange,提问作者Szarlej
相关产品推荐
相关产品推荐

