【问题标题】:Clone a row with a primary key in SQL?在 SQL 中用主键克隆一行?
【发布时间】:2011-01-01 10:35:37
【问题描述】:

我正在尝试制作一些跨 DB(SQL Server 和 PostgreSQL)兼容的 SQL。我需要做的是克隆一行。最好在 SQL 服务器上本地执行此操作,而不必将整行下拉到我们的客户端并重新插入。此外,我们宁愿不必从头开始创建新的 SQL 查询,动态输入列名,但我想这是一个选项。

例如: 我们有一个地址表,它是一个像这样的表: address_rid:整数,主键,自增,不为空 地址1:varchar(100) 地址2:varchar(100)

好吧,我们的目标是能够克隆其中一个行,以便 address1 和 address2 相同,但 address_rid 将像往常一样设置为下一个值。

你会怎么做呢?

另外,我见过Quickest way to clone row in SQL

其中有


INSERT INTO PrimKeys 
SELECT 'PrimKey2' AS PrimKey,* 
  FROM PrimKeys 
 WHERE PrimKey='PrimKey1'

我无法让这样的东西在 postgres 上工作,也无法在插入的主键上使用像 default 这样的东西。

编辑: 另外,我也希望在 SQL Server 和 Postgres 上执行 2 个单独的查询,尽管我更愿意只维护 1 个查询。

我使用 C# ado.net 作为我的客户端。

【问题讨论】:

  • 好的,这就是我正在研究的内容。但我很难找到 SQL Server 的等价物。

标签: c# sql-server postgresql ansi-sql


【解决方案1】:

聚会有点晚了,但这个功能可能会对人们有所帮助。

创建聚合函数来分隔结果中的文本值,逗号分隔符将用于获取列列表。

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

从那里传入要复制的记录的模式名称、表名和 id,它将返回创建记录的 id。 例如

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

您需要将字段名称 id 更改为您的主要/唯一字段名称。

【讨论】:

    【解决方案2】:

    Postgres 与 Oracle 一样,使用序列为人工/代理键生成顺序数值。为了得到下一个值,你需要使用:

    NEXTVAL(your_sequence_name) 
    

    ...在您的 INSERT 语句中设置主键的值。您将无法从 INSERT 语句中省略它,除非出于某种奇怪的原因该列可以为空。所以在 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'
    

    等效的 SQL Server 功能

    SQL Server(和 MySQL)允许您不指定列,或提供 NULL 作为主键列的占位符,并为您提供值。这是你得到的最接近的。

    参考:

    【讨论】:

      【解决方案3】:

      我不确定 PostgreSQL,但如果它像大多数系统一样支持自动增量,那么只需将主键字段留空。但是,这确实意味着您需要指定列名。

      此外,当您说克隆时,听起来您想要复制记录,但在您的描述中,address1 和 address2 列位于同一行数据中。

      如果您想将 address1 复制到 address2 列,您可以使用简单的更新语句。

      如果您尝试克隆整行以获得完全相同的两条记录,那么您可以使用类似的东西,它应该适用于两种环境。

      INSERT INTO Addresses
      ( address1, address2 )
      SELECT address1, address2
        FROM Addresses
       WHERE addressId = ID
      

      现在下一个问题是,您是否正在从一种数据库类型克隆到另一种数据库类型?如果是这样,那么无论如何您都需要将其拉入您的应用程序。如果你是那你为什么不使用像 nHibernate 这样的东西来抽象你的 db 层。

      如果目标只是制作副本,那么您可以使用上面的 sql 在数据库中直接进行。

      虽然这个 sql 可能是最简单的(如果需要,您可以使用系统/主表从数据库中反映它)您仍然需要指定列名。主要原因是,只要您添加 * 通配符,它​​仍然会尝试在选择列表中包含您的原始主键列,然后您所拥有的内容将多出 1 列。

      在插入语句中使用通配符时要注意的另一个问题是您的列顺序必须匹配,否则您会得到非常意外的结果。

      【讨论】:

      • 我认为你涵盖了所有可能的预期效果 ;-) +1
      • @earlz:在这种情况下,您可能需要考虑使用系统/主表来允许您编写与我上面的内容匹配的动态查询。然后你可以添加列,或者什么不需要也不需要担心。如果您可以为每种数据库类型构建一个存储过程,那么您可以为您支持的每个数据库引擎创建一个通用克隆过程。我知道这可以在不确定 PostgreSQL 的 MS SQL Server 中轻松完成。以 vyaskn.tripod.com/code/…> 为例。甚至可能有 PostgreSQL 版本。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-17
      • 2015-05-06
      • 1970-01-01
      相关资源
      最近更新 更多