我有一个包含多个相同模式的数据库。每个模式中都有许多名为'tran _...'的表。我想循环遍历所有模式中的所有'tran_'表,并提取属于特定日期范围的记录。这是我到目前为止的代码:
CREATE OR REPLACE FUNCTION public."configChanges"(starttime timestamp, endtime timestamp)
RETURNS SETOF character varying AS
$BODY$DECLARE
tbl_row RECORD;
tbl_name VARCHAR(50);
tran_row RECORD;
out_record VARCHAR(200);
BEGIN
FOR tbl_row IN
SELECT * FROM pg_tables WHERE schemaname LIKE 'ivr%' AND tablename LIKE 'tran_%'
LOOP
tbl_name := tbl_row.schemaname || '.' || tbl_row.tablename;
FOR tran_row IN
SELECT * FROM tbl_name
WHERE ch_edit_date >= starttime AND ch_edit_date <= endtime
LOOP
out_record := tbl_name || ' ' || tran_row.ch_field_name;
RETURN NEXT out_record;
END LOOP;
END LOOP;
RETURN;
END;
$BODY$
LANGUAGE plpgsql;
当我尝试运行时,我得到:
ERROR: relation "tbl_name" does not exist LINE 1: SELECT * FROM tbl_name WHERE ch_edit_date >= starttime AND c...
答案 0 :(得分:1)
您不能将plpgsql变量用作SQL表名或SQL列名。在这种情况下,您必须使用动态SQL:
FOR tran_row IN
EXECUTE format('SELECT * FROM %I
WHERE ch_edit_date >= starttime AND ch_edit_date <= endtime', tbl_name)
LOOP
out_record := tbl_name || ' ' || tran_row.ch_field_name;
RETURN NEXT out_record;
END LOOP;
答案 1 :(得分:0)
@Pavel已经为您的基本错误提供了修复程序。
但是,由于您的tbl_name
实际上是模式限定的(schema.table
中有两个单独的标识符),因此无法使用%I
整体转义在format()
。您必须单独转义每个标识符。
除此之外,我建议采用不同的方法。外循环是必要的,但内循环可以用更简单,更有效的基于集合的方法替换:
CREATE OR REPLACE FUNCTION public.config_changes(_start timestamp, _end timestamp)
RETURNS SETOF text AS
$func$
DECLARE
_tbl text;
BEGIN
FOR _tbl IN
SELECT quote_ident(schemaname) || '.' || quote_ident(tablename)
FROM pg_tables
WHERE schemaname LIKE 'ivr%'
AND tablename LIKE 'tran_%'
LOOP
RETURN QUERY EXECUTE format (
$$
SELECT %1$L || ' ' || ch_field_name
FROM %1$s
WHERE ch_edit_date BETWEEN $1 AND $2
$$, _tbl
)
USING _start, _end;
END LOOP;
RETURN;
END
$func$ LANGUAGE plpgsql;
您必须使用动态SQL来参数化标识符(或代码),就像@Pavel已经告诉过您的那样。使用RETURN QUERY EXECUTE
,您可以直接返回动态查询的结果。例子:
请记住,标识符必须在动态SQL中被视为不安全的用户输入,并且必须始终进行清理以避免语法错误和 SQL注入:
请注意我如何分别转义表和架构:
quote_ident(schemaname) || '.' || quote_ident(tablename)
因此,我只是使用%s
在后面的查询中插入已经转义的表名。并%L
将其转义为输出的字符串文字。
我喜欢在_
前加上参数和变量名称,以避免与列名称发生冲突。没有其他特殊含义。
与原始功能相比略有不同。这个返回一个转义的标识符(仅在必要时双引号)作为表名,例如:
"WeIRD name"
而不是
WeIRD name
如果可能,请使用inheritance完全避免对上述功能的需要。完整的例子: