创建一个函数来从PostgreSQL中的多个表中获取列

时间:2013-05-11 08:14:41

标签: postgresql plpgsql

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

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"

3 个答案:

答案 0 :(得分:2)

您的内部FOR循环必须使用manual中显示的FOR...EXECUTE语法:

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

在你的情况下,沿着这条线:

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

manual somewhere else

中解释了这个原因
  

通常,您需要在PL / pgSQL函数中生成动态命令,即每次执行时都会涉及不同表或不同数据类型的命令。 PL / pgSQL正常尝试缓存命令计划(如第39.10.2节所述)在这种情况下不起作用。为了处理这类问题,提供了EXECUTE语句[...]

答案 1 :(得分: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。我是对的吗?

再次感谢。

答案 2 :(得分: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()%I一起使用,将quote_ident()%1$I的串联替换为另一个参考相同的参数。

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