Reputation: 63845
I am attempting to make some cross-DB(SQL Server and PostgreSQL) compatible SQL. What I am needing to do is clone a row. It is preferable to do it locally on the SQL server without having to pull the entire row down to our client and reinsert it. Also, we prefer to not have to create a new SQL query from scratch putting in column names dynamically, but this is an option I suppose.
For example: We have an address table in it is a table like so: address_rid: integer, primary key, auto-incrementing, not null address1: varchar(100) address2: varchar(100)
Well, our goal is to just be able to clone one of these rows so that address1 and address2 are the same, yet the address_rid would be set to the next value as usual.
How would you go about doing this?
Also, I have seen Quickest way to clone row in SQL
which has
INSERT INTO PrimKeys
SELECT 'PrimKey2' AS PrimKey,*
FROM PrimKeys
WHERE PrimKey='PrimKey1'
I'm having trouble getting something like this to work on postgres and also to use something like default
on the primary key that gets inserted.
edit: also, I'd just as well take 2 separate queries that will do this on SQL Server and Postgres, though I'd prefer to just have to maintain 1 query.
and I am using the C# ado.net as my client.
Upvotes: 3
Views: 2182
Reputation:
A bit late to the party, but this function may help people.
Create aggregate function to delimit text values in result, comma delimiter will be use to get column list.
CREATE OR REPLACE FUNCTION concat_delimited( TEXT, TEXT, TEXT ) RETURNS TEXT AS $$
SELECT $1 || (CASE WHEN $1 = '' THEN '' ELSE $3 END) || $2;
$$
LANGUAGE SQL STRICT IMMUTABLE;
CREATE AGGREGATE delimited_list_of ( TEXT, TEXT ) (
SFUNC = concat_delimited,
STYPE = TEXT,
INITCOND = ''
);
Create function to duplicate records
CREATE OR REPLACE FUNCTION duplicate(_schema text,_tbl text, _id integer)
RETURNS integer AS
$BODY$
DECLARE cols text;
sql text;
result integer;
BEGIN
SELECT into cols delimited_list_of(column_name, ',')
FROM information_schema.columns
WHERE table_schema = _schema AND table_name = _tbl AND column_name != 'id';
raise notice 'Cols: %', cols;
sql := 'insert into ' || quote_ident(_schema) || '.' || quote_ident(_tbl) || '('
|| cols || ') SELECT ' || cols || ' FROM ' || quote_ident(_schema) || '.'
|| quote_ident(_tbl) || ' WHERE id = ' || _id || ' RETURNING id' ;
raise notice 'Query: %', sql;
EXECUTE sql into result;
RETURN result;
END;
$BODY$
LANGUAGE plpgsql VOLATILE COST 100;
From there just pass in the schema name, table name and id of the record to be duplicated cloned and it will return the id of the created record. E.g
select duplicate('_schema','test_tbl',500001);
you will need to change the field name id to whatever you primary/unique field name is.
Upvotes: 0
Reputation: 332601
Postgres, like Oracle, uses sequences to generate sequencial numeric values for artificial/surrogate keys. In order to get the next value, you need to use:
NEXTVAL(your_sequence_name)
...in your INSERT statement in order to set the value for the primary key. You won't be able to omit it from your INSERT statement, unless for some odd reason the column is nullable. So on Postgres you need to use:
INSERT INTO PrimKeys
SELECT NEXTVAL(your_sequence_name),
[rest of columns, because you can't include the pk col/value]
FROM PrimKeys
WHERE PrimKey='PrimKey1'
SQL Server (and MySQL) allow you to either not specify the column, or supply NULL
as a placeholder for the primary key column and will provide the value for you. That's as close as you get.
Reference:
Upvotes: 2
Reputation: 1349
I'm not sure about PostgreSQL but if it supports the auto-increment like most systems do then just leave the primary key field blank. However this does mean you need to specify the column names.
Also, when you say clone it sounds like you want a duplicate of your record but in your description the address1 and address2 columns are on the same row of data.
If you want to replicate your address1 to the address2 column you can use a simple update statement.
If you are trying to clone the entire row to have two records that are exactly the same then you could use something like this and it should work on both environments.
INSERT INTO Addresses
( address1, address2 )
SELECT address1, address2
FROM Addresses
WHERE addressId = ID
Now the next question is, Are you cloning from one DB type to the other? If so then you will need to pull it into your app anyway. If you are then why don't you use something like nHibernate to abstract the db layer away from you.
If the goal is to just make a copy then you can do it straight in the db with the sql above.
While this sql is probably the easiest (and you could reflect it from the db if needed using system / master tables) you would still need to specify the column names. The primary reason is that as soon as you add the * wildcard it would still try to include your original primary key column in the select list which would then have 1 extra column to what you have.
Another issue to be aware of when using wildcards in insert statements is that your column orders HAVE TO MATCH otherwise you will get very unexpected results.
Upvotes: 3