Can I keep old keys linked to new keys when making a copy in SQL?

后端 未结 2 1644
温柔的废话
温柔的废话 2021-01-16 04:55

I am trying to copy a record in a table and change a few values with a stored procedure in SQL Server 2005. This is simple, but I also need to copy relationships in other ta

相关标签:
2条回答
  • 2021-01-16 05:09

    INSERT statements loading data into tables with an IDENTITY column are guaranteed to generate the values in the same order as the ORDER BY clause in the SELECT.

    If you want the IDENTITY values to be assigned in a sequential fashion that follows the ordering in the ORDER BY clause, create a table that contains a column with the IDENTITY property and then run an INSERT .. SELECT … ORDER BY query to populate this table.

    From: The behavior of the IDENTITY function when used with SELECT INTO or INSERT .. SELECT queries that contain an ORDER BY clause

    You can use this fact to match your old with your new identity values. First collect the list of primary keys that you intend to copy into a temporary table. You can also include your modified column values as well if needed:

    select
        PrimaryKey,
        Col1
        --Col2... etc
    into #NewRecords
    from Table
    --where whatever...
    

    Then do your INSERT with the OUTPUT clause to capture your new ids into the table variable:

    declare @TableVariable table (
        New_ID int not null
    );
    
    INSERT INTO #table
        (Col1 /*,Col2... ect.*/)
    OUTPUT INSERTED.PrimaryKey INTO @NewIds
    SELECT Col1 /*,Col2... ect.*/
    from #NewRecords
    order by PrimaryKey
    

    Because of the ORDER BY PrimaryKey statement, you will be guaranteed that your New_ID numbers will be generated in the same order as the PrimaryKey field of the copied records. Now you can match them up by row numbers ordered by the ID values. The following query would give you the parings:

    select PrimaryKey, New_ID
    from
        (select PrimaryKey, 
            ROW_NUMBER() over (order by PrimaryKey) OldRow
        from #NewRecords
        ) PrimaryKeys
    join
        (
        select New_ID, 
            ROW_NUMBER() over (order by New_ID) NewRow
        from @NewIds
        ) New_IDs
    on OldRow = NewRow
    
    0 讨论(0)
  • 2021-01-16 05:13

    If you need to track both old and new keys in your temp table, you need to cheat and use MERGE:

    Data setup:

    create table T (
        ID int IDENTITY(5,7) not null,
        Col1 varchar(10) not null
    );
    go
    insert into T (Col1) values ('abc'),('def');
    

    And the replacement for your INSERT statement:

    declare @TV table (
        Old_ID int not null,
        New_ID int not null
    );
    merge into T t1
    using (select ID,Col1 from T) t2
    on 1 = 0
    when not matched then insert (Col1) values (t2.Col1)
    output t2.ID,inserted.ID into @TV;
    

    And (actually needs to be in the same batch so that you can access the table variable):

    select * from T;
    select * from @TV;
    

    Produces:

    ID  Col1
    5   abc
    12  def
    19  abc
    26  def
    
    Old_ID  New_ID
    5       19
    12      26
    

    The reason you have to do this is because of an irritating limitation on the OUTPUT clause when used with INSERT - you can only access the inserted table, not any of the tables that might be part of a SELECT.


    Related - More explanation of the MERGE abuse

    0 讨论(0)
提交回复
热议问题