如何在 PostgreSQL 中克隆一条记录

Posted

技术标签:

【中文标题】如何在 PostgreSQL 中克隆一条记录【英文标题】:How to clone a RECORD in PostgreSQL 【发布时间】:2014-09-10 23:18:58 【问题描述】:

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

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'

我错过了什么?

免责声明:我知道代码没有太多意义,我只是为了演示问题而创建的。

【问题讨论】:

表示该数据类型中没有该列。真的吗?也许列名只是id 缺少函数头(包括返回类型和语言声明)。我已经看过几次了,我真的很想知道是什么让人们认为去掉这个基本部分的功能是明智的(甚至可以接受的)。请编辑您的问题。 存在问题的列名。 添加了函数头和表DDL,谢谢 【参考方案1】:

这并不能直接回答您的问题,并且可能根本没有用,因为您没有真正描述您的最终目标。

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

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,但如果您想动态生成查询,可以将其包装在一个函数中。

【讨论】:

我有一个基表,其中包含数据的结构。我有两个后代:活动数据和存档数据。每次行更改时,都会将原始副本插入存档(带有时间戳和用户名)。这样,基表就拥有其记录的每个版本(因为它聚合了来自后代的数据)。我的目标是编写一个函数,它采用关系名称和键值,然后将每个更改作为单独的行返回:时间戳、用户、受影响的列、旧值和新值。你的想法不错,我会努力实现的【参考方案2】:

我很确定,对于您的实际问题,有更好的解决方案。但要回答所提出的问题,这里有一个多态类型的解决方案:

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

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 替换为一个简单的表达式。

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

Declare variable of composite type in PostgreSQL using %TYPE Refactor a PL/pgSQL function to return the output of various SELECT queries

旁白:

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

【讨论】:

这也应该有效,但我接受了另一个答案,因为它更优雅。语言名称引用和WITH (oids = true) 语句是由工具自动生成的,不会投入生产。不过,感谢您指出! @belidzs:我添加了一个答案来为实际问题提供解决方案,该问题触及了 plpgsql 的弱点。如果您可以使用纯 SQL 中的窗口函数解决您的问题,无论如何,请执行 that

以上是关于如何在 PostgreSQL 中克隆一条记录的主要内容,如果未能解决你的问题,请参考以下文章

从 Postgresql 视图中获取下一条记录

如何使用 CakePhp 克隆/复制 sql 记录?

postgresql某数据表中有多天的记录存在,我想取某天的第一条和最后一条记录,数据库查询语句怎么写

PostgreSQL 查询把多条记录合并一条

如何从 sql 查询中获取第一条和最后一条记录?

Postgresql 日志收集