Hello what is the easiest way to duplicate a DB record over the same table?

My problem is that the table where I am doing this has many column, like 100+, and I don't like how the solution looks like. Here is what I do (this is inside plpqsql function): ...

1. duplicate record

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 some columns

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

Here are the problems I am attempting to solve

  1. Listing all these 100+ columns looks lame
  2. When new column is added eventually the function should be updated too
  3. On separate DB instances the column order might differ, which would cause the function fail

I am not sure if I can list them once more (solving issue 3) like insert into <table> (<columns_list>) values (<query>) but then the query looks even uglier.

I would like to achieve something like 'insert into ', but this seems impossible the unique primary key constraint will raise a duplication error.

Any suggestions?

Thanks in advance for you time.

有帮助吗?

解决方案

This isn't pretty or particularly optimized but there are a couple of ways to go about this. Ideally, you might want to do this all in an UPDATE trigger though you could implement a duplication function something like this:

-- 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)

As I noted in my original comment, you might also take a look at the colnames extension as an alternative to querying the information schema.

其他提示

You don't need the update anyway, you can supply the constant values directly in the SELECT statement:

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;
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top