views:

76

answers:

7

SUMMARY:
I need to use an OUTPUT clause on an INSERT statement to return columns which don't exist on the table into which I'm inserting. If I can avoid it, I don't want to add columns to the table to which I'm inserting.

DETAILS:
My FinishedDocument table has only one column. This is the table into which I'm inserting.

FinishedDocument
-- DocumentID

My Document table has two columns. This is the table from which I need to return data.

Document
-- DocumentID
-- Description

The following inserts one row into FinishedDocument. Its OUTPUT clause returns the DocumentID which was inserted. This works, but it doesn't give me the Description of the inserted document.

INSERT INTO FinishedDocument
OUTPUT INSERTED.DocumentID
SELECT DocumentID
FROM Document
WHERE DocumentID = @DocumentID

I need to return from the Document table both the DocumentID and the Description of the matching document from the INSERT.

What syntax do I need to pull this off? I'm thinking it's possible only with the one INSERT statement, by tweaking the OUTPUT clause (in a way I clearly don't understand)?

Is there a smarter way that doesn't resemble the path I'm going down here?

EDIT: SQL Server 2005

+1  A: 

I wonder if you can backdoor it like this (should it concern me that I'd actually consider doing it this way?):

;WITH r (DocumentID)
     AS (INSERT INTO FinishedDocument
         OUTPUT INSERTED.DocumentID
         SELECT DocumentID
           FROM Document
          WHERE DocumentID = @DocumentID)
 SELECT d.DocumentID, d.DocumentName
   FROM Document d
   JOIN r
     ON d.DocumentID = r.DocumentID

Your insert still uses the OUTPUT clause, but as an inline table that then links up to the Document to get your required info. Though, for some reason, I can't escape the feeling that this bastardizes the WITH clause hopelessly...

rwmnau
+1 beat me to it and you had a complete example.
Chris Lively
INSERT isn't valid in a CTE (syntax error)
dportas
I am led to believe that the CTE can only have a SELECT statement inside it.... so as good as this tip looks, I doubt it'll work.
marc_s
@marc_s and David: I was afraid that would happen, and I didn't have SQL handy to test it. Plus the statement just makes me uncomfortable to being with, having that insert inside the CTE... I'll come up with something else and update.
rwmnau
A: 

The OUTPUT clause can contain any of the fields that it operates on, e.g. in your case: any of the fields you're inserting data into. That means: any of the columns from the FinishedDocument table.

The OUTPUT clause however cannot join or grab data from other tables.

marc_s
A: 

Wrap the insert into a CTE, then on the select join the output back to your document table.

Chris Lively
A: 

In 2008 you can do it with a MERGE statement. Maybe you could consider upgrading :)

MERGE INTO FinishedDocument
USING Document d ON 1=0
WHEN NOT MATCHED AND d.DocumentID = @DocumentID THEN
INSERT (DocumentID) VALUES (@DocumentID)
OUTPUT d.DocumentID, d.Description;

I suggest you check the execution plan before you try this.

Is it worth it? You are only inserting from a variable so having two statements, one to select and one to insert isn't going to cost you anything extra. Use two statements.

dportas
My real world is a bit more complex than my question. In my real world, this is in a stored procedure which multiple users will be calling at the same time, and @DocumentID is actually a condition which will pass until the INSERT occurs. So, I'm afraid that a SELECT-then-INSERT could result in two users getting back the same document (because UserB called the sproc and SELECTed RowX after UserA selected RowX (but before UserA INSERTed, which would prevent UserB from getting the same row).
lance
Do it the other way round. Output the ID from the INSERT statement and then SELECT the description afterwards.
dportas
When I output the ID -- into what do I store it (for later use in the SELECT query)? I tried storing it into a variable, but I couldn't figure out the syntax, and "the OUTPUT INTO clause is not supported in INSERT statements that contain a <dml_table_source> clause" (http://msdn.microsoft.com/en-us/library/ms177564.aspx), so I can't use a temp table?
lance
+1  A: 

Look at Example A:

DECLARE @temp TABLE (DocumentID int)

INSERT INTO FinishedDocument 
    OUTPUT INSERTED.DocumentID 
    INTO @temp
SELECT DocumentID 
FROM Document 
WHERE DocumentID = @DocumentID 

SELECT Document.DocumentId, Document.Description
FROM @temp AS t
INNER JOIN Document 
    ON t.DocumentID = Document.DocumentID
Cade Roux
The page you linked states that "the OUTPUT INTO clause is not supported in INSERT statements that contain a <dml_table_source> clause". Turns out that my real world query falls victim to that.
lance
A: 

How about something like this?

create table FinishedDocument (
    DocumentId int
)

create table Document (
    DocumentId int,
    Description nvarchar(100)
)

create table #tmpDoc (
    DocumentId int
)

insert into Document
    (DocumentId, Description)
    values
    (1, 'Test')

insert into FinishedDocument
    (DocumentId)
    output Inserted.DocumentId into #tmpDoc 
    select D.DocumentId
        from Document D
        where D.DocumentId = 1

select D.DocumentId, D.Description
    from #tmpDoc t
        inner join Document D
            on t.DocumentId = D.DocumentId

drop table #tmpDoc      
drop table FinishedDocument
drop table Document
Joe Stefanelli
http://msdn.microsoft.com/en-us/library/ms177564.aspx states that "the OUTPUT INTO clause is not supported in INSERT statements that contain a <dml_table_source> clause". Turns out that my real world query falls victim to that. :(
lance
Could you select your <dml_table_source> into a temp table and use that for the insert?
Joe Stefanelli
A: 

If I'm reading you right, can't you do this in your stored procedure?

INSERT INTO FinishedDocument (DocumentID) values (@DocumentID)
SELECT DocumentID, Description FROM Document
    WHERE DocumentID = @DocumentID
egrunin