What columns can be used in OUTPUT INTO clause?

前端 未结 5 956
孤街浪徒
孤街浪徒 2021-02-05 11:45

I\'m trying to build a mapping table to associate the IDs of new rows in a table with those that they\'re copied from. The OUTPUT INTO clause seems perfect for that, but it does

相关标签:
5条回答
  • 2021-02-05 12:30

    I'm running into EXACTLY the same problem as you are, I feel your pain... As far as I've been able to find out there's no way to use the from_table_name prefix with an INSERT statement. I'm sure there's a viable technical reason for this, and I'd love to know exactly what it is.

    Ok, found it, here's a forum post on why it doesn't work: MSDN forums

    0 讨论(0)
  • 2021-02-05 12:35

    I think I found a solution to this problem, it sadly involves a temporary table, but at least it'll prevent the creation of a dreaded cursor :) What you need to do is add an extra column to the table you're duplicating records from and give it a 'uniqueidentifer' type.

    then declare a temporary table:

    DECLARE @tmptable TABLE (uniqueid uniqueidentifier, original_id int, new_id int)
    

    insert the the data into your temp table like this:

    insert into @tmptable
    (uniqueid,original_id,new_id)
    select NewId(),id,0 from OriginalTable
    

    the go ahead and do the real insert into the original table:

    insert into OriginalTable
    (uniqueid)
    select uniqueid from @tmptable
    

    Now to add the newly created identity values to your temp table:

    update @tmptable
    set new_id = o.id
    from OriginalTable o inner join @tmptable tmp on tmp.uniqueid = o.uniqueid
    

    Now you have a lookup table that holds the new id and original id in one record, for your using pleasure :)

    I hope this helps somebody...

    0 讨论(0)
  • 2021-02-05 12:37

    I've verified that the problem is that you can only use INSERTED columns. The documentation seems to indicate that you can use from_table_name, but I can't seem to get it to work (The multi-part identifier "m.ContentID" could not be bound.):

    TRUNCATE TABLE main
    
    SELECT *
    FROM incoming
    
    SELECT *
    FROM main
    
    DECLARE @Missing TABLE (ContentID INT PRIMARY KEY)
    INSERT INTO @Missing(ContentID) 
    SELECT incoming.ContentID
    FROM incoming
    LEFT JOIN main
        ON main.ContentID = incoming.ContentID
    WHERE main.ContentID IS NULL
    
    SELECT *
    FROM @Missing
    
    DECLARE @Inserted TABLE (ContentID INT PRIMARY KEY, [Content] varchar(50))
    INSERT INTO main(ContentID, [Content]) 
    OUTPUT INSERTED.ContentID /* incoming doesn't work, m doesn't work */, INSERTED.[Content] INTO @Inserted (ContentID, [Content])
    SELECT incoming.ContentID, incoming.[Content] 
    FROM incoming
    INNER JOIN @Missing AS m
        ON m.ContentID = incoming.ContentID
    
    SELECT *
    FROM @Inserted
    
    SELECT *
    FROM incoming
    
    SELECT *
    FROM main
    

    Apparently the from_table_name prefix is only allowed on DELETE or UPDATE (or MERGE in 2008) - I'm not sure why:

    • from_table_name

    Is a column prefix that specifies a table included in the FROM clause of a DELETE or UPDATE statement that is used to specify the rows to update or delete.

    If the table being modified is also specified in the FROM clause, any reference to columns in that table must be qualified with the INSERTED or DELETED prefix.

    0 讨论(0)
  • 2021-02-05 12:37

    You can do this with a MERGE in Sql Server 2008. Example code below:

    --drop table A
    create table A (a int primary key identity(1, 1))
    insert into A default values
    insert into A default values
    
    delete from A where a>=3
    
    -- insert two values into A and get the new primary keys
    MERGE a USING (SELECT a FROM A) AS B(a)
    ON (1 = 0) -- ignore the values, NOT MATCHED will always be true
    WHEN NOT MATCHED THEN INSERT DEFAULT VALUES -- always insert here for this example
    OUTPUT $action, inserted.*, deleted.*, B.a; -- show the new primary key and source data
    

    Result is

    INSERT, 3, NULL, 1
    INSERT, 4, NULL, 2
    

    i.e. for each row the new primary key (3, 4) and the old one (1, 2). Creating a table called e.g. #OUTPUT and adding " INTO #OUTPUT;" at the end of the OUTPUT clause would save the records.

    0 讨论(0)
  • 2021-02-05 12:38

    (MS) If the table being modified is also specified in the FROM clause, any reference to columns in that table must be qualified with the INSERTED or DELETED prefix.

    In your example, you can't use cglobal table in the OUTPUT unless it's INSERTED.column_name or DELETED.column_name:

    INSERT INTO Private.Content 
        (Tag) 
        OUTPUT cglobal.ContentID, INSERTED.ContentID 
        INTO @Inserted (SrcContentID, TgtContentID)
    SELECT Tag
        FROM Private.Content AS cglobal
        INNER JOIN @Missing AS m ON cglobal.ContentID = m.SrcContentID
    

    What worked for me was a simple alias table, like this:

    INSERT INTO con1 
        (Tag) 
        OUTPUT **con2**.ContentID, INSERTED.ContentID 
        INTO @Inserted (SrcContentID, TgtContentID)
    SELECT Tag
        FROM Private.Content con1
        **INNER JOIN Private.Content con2 ON con1.id=con2.id**
        INNER JOIN @Missing AS m ON con1.ContentID = m.SrcContentID
    
    0 讨论(0)
提交回复
热议问题