简体   繁体   English

在SQL中使用主键克隆一行?

[英]Clone a row with a primary key in SQL?

I am attempting to make some cross-DB(SQL Server and PostgreSQL) compatible SQL. 我正在尝试制作一些跨数据库(SQL Server和PostgreSQL)兼容的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. 最好在SQL服务器上本地执行此操作,而不必将整个行都拉到我们的客户端并重新插入。 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. 另外,我们希望不必从头开始创建新的SQL查询,而无需动态地输入列名,但这是我想的一种选择。

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) 例如:我们有一个地址表,它是一个像这样的表:address_rid:整数,主键,自动递增,不为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. 好吧,我们的目标是仅能够克隆这些行之一,以使address1和address2相同,但是address_rid将照常设置为下一个值。

How would you go about doing this? 您将如何去做?

Also, I have seen Quickest way to clone row in SQL 另外,我已经看到了在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. 我在让类似这样的东西在postgres上工作以及在插入的主键上使用像default这样的东西时遇到了麻烦。

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. 编辑:另外,我也希望在SQL Server和Postgres上执行2个独立的查询,尽管我希望只维护1个查询。

and I am using the C# ado.net as my client. 我正在使用C#ado.net作为客户端。

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. 我不确定PostgreSQL,但如果它像大多数系统一样支持自动增量,则只需将主键字段留空。 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. 同样,当您说克隆时,听起来好像您想要记录的副本,但是在您的描述中,address1和address2列在同一行数据上。

If you want to replicate your address1 to the address2 column you can use a simple update statement. 如果要将address1复制到address2列,则可以使用简单的更新语句。

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. 如果是的话,为什么不使用nHibernate之类的东西来使db层远离您。

If the goal is to just make a copy then you can do it straight in the db with the sql above. 如果目标只是制作副本,则可以使用上述sql直接在数据库中进行复制。

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. 尽管此sql可能是最简单的(并且可以使用system / master表从数据库中反映出来),但仍然需要指定列名。 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. 主要原因是,一旦添加了*通配符,它​​仍将尝试在选择列表中包括原始主键列,这样,该列将增加1个额外的列。

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. 在插入语句中使用通配符时要注意的另一个问题是,您的列必须“必须匹配”,否则您将得到非常意外的结果。

Postgres, like Oracle, uses sequences to generate sequencial numeric values for artificial/surrogate keys. 与Oracle一样,Postgres也使用序列来为人工/代理键生成序列的数值。 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. ...在您的INSERT语句中,以便设置主键的值。 You won't be able to omit it from your INSERT statement, unless for some odd reason the column is nullable. 您将无法从INSERT语句中忽略它,除非出于某种奇怪的原因该列可为空。 So on Postgres you need to use: 因此,在Postgres上,您需要使用:

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'

Equivalent SQL Server Functionality 等效的SQL Server功能

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. SQL Server(和MySQL)允许您不指定列,或者提供NULL作为主键列的占位符,并为您提供值。 That's as close as you get. 那离您越近。

Reference: 参考:

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. 从那里,只需输入要复制的复制记录的模式名称,表名称和ID,它将返回已创建记录的ID。 Eg 例如

select  duplicate('_schema','test_tbl',500001);

you will need to change the field name id to whatever you primary/unique field name is. 您将需要将字段名称ID更改为您的主/唯一字段名称。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM