【问题标题】:Duplicate single database record重复的单个数据库记录
【发布时间】:2013-04-07 07:44:31
【问题描述】:

您好,在同一张表上复制数据库记录的最简单方法是什么?

我的问题是我正在执行此操作的表有很多列,例如 100+,我不喜欢解决方案的外观。这是我所做的(这是在 plpqsql 函数中): ...

1.重复记录

INSERT INTO history 
  (SELECT NEXTVAL('history_id_seq'), col_1, col_2, ... , col_100)
   FROM history
   WHERE history_id = 1234
   ORDER BY datetime DESC
   LIMIT 1)
 RETURNING
   history_id INTO new_history_id;

2。更新一些列

UPDATE history
SET 
    col_5 = 'test_5', 
    col_23 = 'test_23', 
    datetime = CURRENT_TIMESTAMP
WHERE history_id = new_history_id;

这是我试图解决的问题

  1. 列出所有这 100 多列看起来很糟糕
  2. 当最终添加新列时,函数也应该更新
  3. 在单独的数据库实例上,列顺序可能不同,这会导致函数失败

我不确定我是否可以像insert into <table> (<columns_list>) values (<query>) 一样再次列出它们(解决问题 3),但查询看起来更难看。

我想实现“插入”之类的东西,但这似乎不可能,唯一主键约束会引发重复错误。

有什么建议吗?

提前感谢您的宝贵时间。

【问题讨论】:

    标签: postgresql plpgsql postgresql-9.0


    【解决方案1】:

    这不是很漂亮或特别优化,但有几种方法可以解决这个问题。理想情况下,您可能希望在 UPDATE 触发器中完成所有这些操作,尽管您可以实现类似这样的复制功能:

    -- create source table
    CREATE TABLE history (history_id serial not null primary key, col_2 int, col_3 int, col_4 int, datetime timestamptz default now());
    
    -- add some data
    INSERT INTO history (col_2, col_3, col_4)
      SELECT g, g * 10, g * 100 FROM generate_series(1, 100) AS g;
    
    -- function to duplicate record
    CREATE OR REPLACE FUNCTION fn_history_duplicate(p_history_id integer) RETURNS SETOF history AS
    $BODY$
    DECLARE
      cols text;
      insert_statement text;
    BEGIN
    
      -- build list of columns
      SELECT array_to_string(array_agg(column_name::name), ',') INTO cols
        FROM information_schema.columns 
        WHERE (table_schema, table_name) = ('public', 'history')
        AND column_name <> 'history_id';
    
      -- build insert statement
      insert_statement := 'INSERT INTO history (' || cols || ') SELECT ' || cols || ' FROM history WHERE history_id = $1 RETURNING *';
    
      -- execute statement
      RETURN QUERY EXECUTE insert_statement USING p_history_id;
    
      RETURN;
    
    END;
    $BODY$
      LANGUAGE 'plpgsql';
    
    -- test
    SELECT * FROM fn_history_duplicate(1);
     history_id | col_2 | col_3 | col_4 |           datetime            
    ------------+-------+-------+-------+-------------------------------
            101 |     1 |    10 |   100 | 2013-04-15 14:56:11.131507+00
    (1 row)
    

    正如我在原始评论中所指出的,您还可以查看 colnames extension 作为查询信息架构的替代方法。

    【讨论】:

    • 完美,稍作修改以满足我的需要(使用 p_history_id 执行 insert_statement INTO new_history_id,然后在某些字段上更新历史记录),但工作正常,代码看起来很流畅,非常感谢!
    【解决方案2】:

    无论如何您都不需要更新,您可以直接在SELECT 语句中提供常量值:

    INSERT INTO history 
    SELECT NEXTVAL('history_id_seq'), 
           col_1, 
           col_2, 
           col_3, 
           col_4, 
           'test_5', 
           ... 
           'test_23', 
           ...,
           col_100
    FROM history
    WHERE history_sid = 1234
    ORDER BY datetime DESC
    LIMIT 1
     RETURNING history_sid INTO new_history_sid;
    

    【讨论】:

    • 谢谢,这确实是个好点,但我不想列出 100 多列
    • @gogh:不列出列是不可能的。老实说,100 多列似乎值得怀疑
    • 这不是很漂亮,但您可以编写一个函数来动态构建和执行 INSERT。这可以通过使用信息模式(或系统目录)生成列列表来实现,或者,如果您可以安装它,可以使用位于此处的 colnames 扩展:link。如果我有时间,我会发送两者的示例。
    • @marcj:感谢您提供的信息。我知道没有简单的方法,我只是想知道是否可以调整诸如“插入 tbl (select * from tbl ..)”之类的方法,将主键替换为序列的下一个值。跨度>
    • @gogh:我也遇到过这种情况并感受到你的痛苦。我确实在下面发布了一个可行的解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-19
    • 2018-09-27
    • 1970-01-01
    • 2020-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多