0

我正在尝试创建一个函数来从我的数据库中的多个表中获取字段值。我制作了这样的脚本:

CREATE OR REPLACE FUNCTION get_all_changes() RETURNS SETOF RECORD AS
$$
DECLARE 
  tblname VARCHAR;
  tblrow RECORD;
  row RECORD;
BEGIN
    FOR tblrow IN SELECT tablename FROM pg_catalog.pg_tables WHERE schemaname='public' LOOP /*FOREACH tblname IN ARRAY $1 LOOP*/
      RAISE NOTICE 'r: %', tblrow.tablename;
      FOR row IN SELECT MAX("lastUpdate") FROM tblrow.tablename LOOP
          RETURN NEXT row;
      END LOOP;
    END LOOP;
END
$$
LANGUAGE 'plpgsql' ;

SELECT get_all_changes();

但它不起作用,每次都显示此错误

tblrow.tablename" not defined in line "FOR row IN SELECT MAX("lastUpdate") FROM tblrow.tablename LOOP"
4

3 回答 3

2

Your inner FOR loop must use the FOR...EXECUTE syntax as shown in the manual:

FOR target IN EXECUTE text_expression [ USING expression [, ... ] ] LOOP
    statements
END LOOP [ label ];

In your case something along this line:

FOR row IN EXECUTE 'SELECT MAX("lastUpdate") FROM ' || quote_ident(tblrow.tablename) LOOP
   RETURN NEXT row;
END LOOP

The reason for this is explained in the manual somewhere else:

Oftentimes you will want to generate dynamic commands inside your PL/pgSQL functions, that is, commands that will involve different tables or different data types each time they are executed. PL/pgSQL's normal attempts to cache plans for commands (as discussed in Section 39.10.2) will not work in such scenarios. To handle this sort of problem, the EXECUTE statement is provided[...]

于 2013-05-11T08:28:12.873 回答
0

回答您的新问题(错误标记为答案):

这可以简单得多。您不需要创建表,只需定义记录类型。如果有的话,您最好用 来创建一个类型CREATE TYPE,但这只有在您需要在多个地方使用该类型时才有效。对于单个功能,您可以RETURNS TABLE改用:

CREATE OR REPLACE FUNCTION get_all_changes(text[])
 RETURNS TABLE (tablename text
               ,"lastUpdate" timestamp with time zone
               ,nums integer) AS
$func$
DECLARE 
    tblname text;
BEGIN
    FOREACH tblname IN ARRAY $1 LOOP
        RETURN QUERY EXECUTE format(
        $f$SELECT '%I', MAX("lastUpdate"), COUNT(*)::int FROM %1$I
        $f$, tblname)
    END LOOP;
END
$func$ LANGUAGE plpgsql;

还有几点:

  • 使用RETURN QUERY EXECUTE而不是嵌套循环。更简单,更快。

  • 列别名仅用作文档,这些名称将被丢弃,取而代之的是RETURNS子句中声明的名称(直接或间接)。

  • 使用format()with%I将串联替换为quote_ident()%1$I再次引用相同的参数。

  • count()通常返回类型bigint。转换整数,因为您将返回类型中的列定义为:count(*)::int.

于 2013-05-12T19:12:46.323 回答
0

谢谢,我终于把我的脚本写成了:

CREATE TABLE IF NOT EXISTS __rsdb_changes (tablename text,"lastUpdate" timestamp with time zone, nums bigint);
    CREATE OR REPLACE FUNCTION get_all_changes(varchar[]) RETURNS SETOF __rsdb_changes AS  /*TABLE (tablename varchar(40),"lastUpdate" timestamp with time zone, nums integer)*/
    $$
    DECLARE 
      tblname VARCHAR;
      tblrow RECORD;
      row RECORD;
    BEGIN
        FOREACH tblname IN ARRAY $1 LOOP
          /*RAISE NOTICE 'r: %', tblrow.tablename;*/
          FOR row IN EXECUTE 'SELECT CONCAT('''|| quote_ident(tblname) ||''') AS tablename, MAX("lastUpdate") AS "lastUpdate",COUNT(*) AS nums FROM ' || quote_ident(tblname) LOOP
            /*RAISE NOTICE 'row.tablename: %',row.tablename;*/
            /*RAISE NOTICE 'row.lastUpdate: %',row."lastUpdate";*/
            /*RAISE NOTICE 'row.nums: %',row.nums;*/
            RETURN NEXT row;
          END LOOP;
        END LOOP;
        RETURN;
    END
    $$
    LANGUAGE 'plpgsql' ;

嗯,它有效。但似乎我只能创建一个表来定义返回结构,而不仅仅是 RETURNS SETOF RECORD。我对吗?

再次感谢。

于 2013-05-12T01:20:12.433 回答