【问题标题】:Quickest way to clone row in SQL在 SQL 中克隆行的最快方法
【发布时间】:2009-09-25 23:32:43
【问题描述】:

我想克隆具有单个主键 (PrimKey) 的多个表的行。在 SQL Server 2005 中克隆行的最快方法是什么?

这是一个例子,

克隆 PrimKey1 得到 PrimKey2。所以我尝试以下方法:

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

当然,这里的问题是,PrimKey 列在内部 SELECT 语句中重复出现。有没有其他与上述类似的简单方法?


感谢大家的回复。我继续编写了一个在我的应用程序中处理该问题的函数。我不使用存储过程或临时表,所以我不能使用你们中的一些人发布的几个有效答案。

【问题讨论】:

  • “最快输入”中的最快?永远不要在 SELECT 中使用 * 并且永远不要在 INSERT 中使用隐式列列表。
  • 谢谢 Remus,但这并没有回答我的问题。
  • 你也没有回答 Remus 的问题,这很重要。
  • 最快,因为字符数最少且最通用。如果我不使用 *,整个通用要求就会消失。

标签: sql sql-server sql-server-2005 tsql


【解决方案1】:

您可以运行类似下面的存储过程以避免输入所有列名。下面的示例假定为 int,但您可以将键类型换成任何数据类型。

create procedure [CloneRow]
    @tableName varchar(max),
    @keyName varchar(max),
    @oldKeyId int,
    @newTableId int output
as
    declare @sqlCommand nvarchar(max),
            @columnList varchar(max);

    select  @columnList = coalesce(@columnList + ',','') + sys.columns.name
    from    sys.columns
    where   object_name(sys.columns.object_id) = @tableName
        and sys.columns.name not in ( @keyName )
        and is_computed = 0;

    set @sqlCommand = 'insert into ' + @tableName + ' ( ' + @columnList + ') (' +
        'select ' + @columnList + ' from ' + @tableName + ' where ' + @keyName + ' = @oldKeyId )'
    exec sp_executesql @sqlCommand, N'@oldKeyId int', @oldKeyId = @oldKeyId
    select @newTableId = @@identity -- note scope_identity() won't work here!
GO

你这样称呼它:

declare @newOrderId int
exec [CloneRow] 'orderTable', 'orderId', 625911, @newOrderId output

【讨论】:

    【解决方案2】:

    这不是最漂亮的解决方案,但我认为它对你有用。首先,您将数据选择到具有“新”主键的临时表中,然后从临时表中删除旧主键列并使用临时表插入“克隆”行。

    SELECT 
    'PrimKey2' AS NewPrimKey,
    *
    INTO #TMP 
    FROM PrimKeys 
    WHERE PrimKey='PrimKey1';
    
    ALTER TABLE #TMP DROP COLUMN PrimKey;
    
    INSERT INTO PrimKeys
    SELECT * FROM #TMP;
    

    【讨论】:

      【解决方案3】:

      不确定“具有单个主键的多个表的行”是什么意思。

      PRIMARY KEY,根据定义,是UNIQUE

      要进行查询,您需要枚举所有列:

      INSERT
      INTO    PrimKeys (PrimKey, col1, col2, …)
      SELECT  'PrimKey2' AS PrimKey, col1, col2, …
      FROM    PrimKeys
      WHERE   PrimKey = 'PrimKey1'
      

      【讨论】:

      • 是的,我试图避免这种情况。原因是,我有 55 张桌子,它们都有一个共同点。例如,他们有一个名为“订单 ID”的主键。如果我使用你的方法,那么我需要知道 55 个表中每个表的每个列号。我正在寻找一种方法来遍历 55 个表并生成更通用的 SQL。
      【解决方案4】:

      首先,如果您需要对大量表执行通用操作,那么动态 SQL 和系统表是您的朋友。

      简而言之,Hakan 的解决方案适用于非身份 PK。我会收紧到:

      SELECT * INTO #TMP 
      FROM PrimKeys 
      WHERE PrimKey='PrimKey1';
      
      UPDATE #TMP SET PrimeKey = 'PrimeKey2';
      
      INSERT INTO PrimKeys
      SELECT * FROM #TMP;
      

      对于身份 PK,将上面的 UPDATE 更改为 DROP COLUMN:

      SELECT * INTO #TMP 
      FROM PrimKeys 
      WHERE PrimKey=101;
      
      ALTER TABLE #TMP DROP COLUMN PrimeKey;
      
      INSERT INTO PrimKeys
      SELECT * FROM #TMP;
      

      【讨论】:

      • 这个解决方案(见下面的 Hakan)是一个很好的解决方案。使用 SELECT INTO #temp 方法的好处在于,您可以复制表中的所有列,即使是在编写代码后添加的列。不幸的是,它不适用于不支持 SELECT INTO 的 SQL Azure。
      【解决方案5】:

      如果你从多个表中克隆,最快的方法是使用存储过程,这样所有的查询都留在数据库中,并且你不需要为客户端和服务器之间的通信付出代价。

      一旦你这样做了,然后开始单元测试,看看完成这个操作需要多长时间。然后根据你得到的建议开始尝试改变它,看看你是否有任何改进。

      【讨论】:

      • @James Black:我认为发帖人是在推断它将在“数据库”服务器上完成......
      • 听起来发帖人想从一个应用程序中进行多个查询来进行克隆,这将比存储过程慢。
      猜你喜欢
      • 2023-03-25
      • 1970-01-01
      • 2019-01-21
      • 1970-01-01
      • 1970-01-01
      • 2016-01-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多