如何在PostgreSQL中克隆RECORD

时间:2014-09-10 23:18:59

标签: postgresql plpgsql dynamic-sql compositetype

我想循环查询,但也保留下一个循环的实际记录,所以我可以比较两个相邻的行。

CREATE OR REPLACE FUNCTION public.test ()
  RETURNS void AS
$body$
    DECLARE
      previous RECORD;
      actual RECORD;
      query TEXT;
      isdistinct BOOLEAN;
      tablename VARCHAR;
      columnname VARCHAR;
      firstrow BOOLEAN DEFAULT TRUE;
    BEGIN
      tablename = 'naplo.esemeny';
      columnname = 'esemeny_id';
      query = 'SELECT * FROM ' || tablename || ' LIMIT 2';
      FOR actual IN EXECUTE query LOOP
        --do stuff
        --save previous record
        IF NOT firstrow THEN
          EXECUTE 'SELECT ($1).' || columnname || ' IS DISTINCT FROM ($2).' || columnname 
            INTO isdistinct USING previous, actual;
          RAISE NOTICE 'previous: %', previous.esemeny_id;
          RAISE NOTICE 'actual: %', actual.esemeny_id;        
          RAISE NOTICE 'isdistinct: %', isdistinct; 
        ELSE
          firstrow = false;           
        END IF;
        previous = actual;
      END LOOP;
      RETURN;
    END;
$body$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 100;

表格:

CREATE TABLE naplo.esemeny (
  esemeny_id SERIAL,
  felhasznalo_id VARCHAR DEFAULT "current_user"() NOT NULL,
  kotesszam VARCHAR(10),
  idegen_azonosito INTEGER,
  esemenytipus_id VARCHAR(10),
  letrehozva TIMESTAMP WITHOUT TIME ZONE DEFAULT now() NOT NULL,
  szoveg VARCHAR,
  munkalap_id VARCHAR(13),
  ajanlat_id INTEGER,
  CONSTRAINT esemeny_pkey PRIMARY KEY(esemeny_id),
  CONSTRAINT esemeny_fk_esemenytipus FOREIGN KEY (esemenytipus_id)
    REFERENCES naplo.esemenytipus(esemenytipus_id)
    ON DELETE RESTRICT
    ON UPDATE RESTRICT
    NOT DEFERRABLE
) 
WITH (oids = true);

上面的代码不起作用,抛出以下错误消息:

ERROR:  could not identify column "esemeny_id" in record data type
LINE 1: SELECT ($1).esemeny_id IS DISTINCT FROM ($2).esemeny_id
                ^
QUERY:  SELECT ($1).esemeny_id IS DISTINCT FROM ($2).esemeny_id
CONTEXT:  PL/pgSQL function "test" line 18 at EXECUTE statement
LOG:  duration: 0.000 ms  statement: SET DateStyle TO 'ISO'

我错过了什么?

免责声明:我知道代码没有多大意义,我只是创建了所以我可以证明这个问题。

2 个答案:

答案 0 :(得分:2)

这并不能直接回答你的问题,也可能完全无用,因为你并没有真正描述你的最终目标。

如果最终目标是能够将当前行中列的值与前一行中相同列的值进行比较,那么使用窗口查询可能会好得多:

SELECT actual, previous
FROM (
    SELECT mycolumn AS actual,
        lag(mycolumn) OVER () AS previous
    FROM mytable
    ORDER BY somecriteria
) as q
WHERE previous IS NOT NULL 
    AND actual IS DISTINCT FROM previous

此示例打印当前行与上一行不同的行。

请注意,我添加了一个ORDER BY子句 - 谈论"前一行"是没有意义的。没有指定排序,否则你会得到随机结果。

这是普通的SQL,而不是PlPgSQL,但如果你想动态生成查询,可以将它包装在一个函数中。

答案 1 :(得分:1)

我很确定,您的实际问题有更好的解决方案。但回答问题,这是一个多态类型的解决方案:

主要问题是您需要众所周知的复合类型才能使用。在分配之前,匿名记录的结构是未定义的。

CREATE OR REPLACE FUNCTION public.test (actual anyelement, _col text
                                      , OUT previous anyelement) AS
$func$
DECLARE
   isdistinct bool;
BEGIN
   FOR actual IN
      EXECUTE format('SELECT * FROM %s LIMIT 3', pg_typeof(actual))
   LOOP
        EXECUTE format('SELECT ($1).%1$I IS DISTINCT FROM ($2).%1$I', _col)
        INTO   isdistinct
        USING  previous, actual;

      RAISE NOTICE 'previous: %; actual: %; isdistinct: %'
                  , previous, actual, isdistinct;

      previous := actual;
   END LOOP;

   previous := NULL;  -- reset dummy output (optional)
END
$func$ LANGUAGE plpgsql;

呼叫:

SELECT public.test(NULL::naplo.esemeny, 'esemeny_id')

我滥用OUT参数,因为无法使用多态复合类型声明其他变量(至少我反复失败)。

如果您的列名称稳定,则可以使用简单表达式替换第二个EXECUTE

我的时间不多了,在这些相关答案中有解释:

旁白:

  • 不要引用语言名称,它是标识符,而不是字符串。
  • 你真的需要WITH (oids = true)在你的桌子上吗?这仍然是允许的,但在现代Postgres中基本上已被弃用。