2010-09-14 5 views
49

Diciamo che la mia struttura della tabella simile a questa:Come posso INSERIRE i dati in due tabelle contemporaneamente in SQL Server?

CREATE TABLE [dbo].[table1] (
    [id] [int] IDENTITY(1,1) NOT NULL, 
    [data] [varchar](255) NOT NULL, 
    CONSTRAINT [PK_table1] PRIMARY KEY CLUSTERED ([id] ASC) 
) 

CREATE TABLE [dbo].[table2] (
    [id] [int] IDENTITY(1,1) NOT NULL, 
    [table1_id] [int] NOT NULL, 
    [data] [varchar](255) NOT NULL, 
    CONSTRAINT [PK_table2] PRIMARY KEY CLUSTERED ([id] ASC) 
) 

La [id] campo della prima tabella corrisponde alla [table1_id] campo della seconda. Quello che mi piacerebbe fare è inserire i dati in entrambe le tabelle in una singola transazione. Ora so già come fare questo facendo INSERT-SELECT-INSERT, in questo modo:

BEGIN TRANSACTION; 
DECLARE @id [int]; 
INSERT INTO [table1] ([data]) VALUES ('row 1'); 
SELECT @id = SCOPE_IDENTITY(); 
INSERT INTO [table2] ([table1_id], [data]) VALUES (@id, 'more of row 1'); 
COMMIT TRANSACTION; 

Questo è tutto bene e bene per i piccoli casi come quello in cui si sta solo inserendo forse una manciata di righe. Ma quello che devo fare è inserire un paio di centinaia di righe, o forse anche un milione di righe, tutto in una volta. I dati proviene da un altro tavolo, quindi se ero solo inserendolo in una singola tabella, che sarebbe stato facile, mi resta che fare questo:

INSERT INTO [table] ([data]) 
SELECT [data] FROM [external_table]; 

Ma come avrei fatto questo e dividere il dati in [table1] e [table2] e aggiornare ancora [table2] con l'appropriato [table1_id] mentre lo sto facendo? È possibile?

+0

Bella domanda che molti principianti di SQL chiedono spesso. –

risposta

29

Prova questo:

insert into [table] ([data]) 
output inserted.id, inserted.data into table2 
select [data] from [external_table] 

UPDATE: Re:

Denis - this seems very close to what I want to do, but perhaps you could fix the following SQL statement for me? Basically the [data] in [table1] and the [data] in [table2] represent two different/distinct columns from [external_table]. The statement you posted above only works when you want the [data] columns to be the same.

INSERT INTO [table1] ([data]) 
OUTPUT [inserted].[id], [external_table].[col2] 
INTO [table2] SELECT [col1] 
FROM [external_table] 

E 'impossibile uscita colonne esterne in un insert dichiarazione, quindi penso che si potrebbe fare qualcosa di simile

merge into [table1] as t 
using [external_table] as s 
on 1=0 --modify this predicate as necessary 
when not matched then insert (data) 
values (s.[col1]) 
output inserted.id, s.[col2] into [table2] 
; 
+0

Denis, ho usato solo OUTPUT per scrivere sulla variabile di tabella. Puoi usarlo per inserire direttamente in un tavolo live? – Bill

+0

@Puoi scommettere! –

+0

A proposito, non c'è bisogno di abbracciarlo nelle istruzioni 'begin tran ... commit tran', poiché verrà eseguito in una singola transazione. –

-1

È possibile scrivere una stored procedure che itera sulla transazione che si è proposto. L'iteratore sarebbe il cursore per la tabella che contiene i dati di origine.

+3

Non iterare mai quando esiste una soluzione basata su set. – HLGEM

+0

@HLGEM - dato il mio errore, quale delle soluzioni proposte consiglieresti? – mlschechter

+0

Definitivamente la clausola di output su una tabella temporanea. – HLGEM

0
BEGIN TRANSACTION; 

DECLARE @tblMapping table(sourceid int, destid int) 

INSERT INTO [table1] ([data]) 
OUTPUT source.id, new.id 
Select [data] from [external_table] source; 

INSERT INTO [table2] ([table1_id], [data]) 
Select map.destid, source.[more data] 
from [external_table] source 
    inner join @tblMapping map on source.id=map.sourceid; 

COMMIT TRANSACTION; 
+1

Data la risposta di Denis al mio commento, la sua soluzione è molto più pulita della mia. – Bill

+0

Non è possibile utilizzare 'source.id, new.id' nella clausola' output'. Ti è permesso usare solo 'inserito. *' Per 'inserire'. Per 'delete',' update' e 'merge' è possibile includere una colonna dalla tabella specificata. –

1

Tenere d'occhio SQL Server per supportare l'istruzione "INSERT ALL". Oracle ha già, sembra che questa (SQL Cookbook):

insert all 
    when loc in ('NEW YORK', 'BOSTON') THEN 
    into dept_east(deptno, dname, loc) values(deptno, dname, loc) 
    when loc in ('CHICAGO') THEN 
    into dept_mid(deptno, dname, loc) values(deptno, dname, loc) 
    else 
    into dept_west(deptno, dname, loc) values(deptno, dname, loc) 
select deptno, dname, loc 
    from dept 
0

Un'altra opzione è quella di eseguire i due inserti separatamente, lasciando la colonna nullo FK, quindi eseguire un aggiornamento di poulate correttamente.

Se non c'è nulla di naturale memorizzato all'interno delle due tabelle che corrispondono da un record all'altro (probabile), quindi creare una colonna GUID temporanea e popolare nei dati e inserire in entrambi i campi. Quindi è possibile aggiornare con l'FK corretto e disattivare i GUID.

Es .:

CREATE TABLE [dbo].[table1] ( 
    [id] [int] IDENTITY(1,1) NOT NULL, 
    [data] [varchar](255) NOT NULL, 
    CONSTRAINT [PK_table1] PRIMARY KEY CLUSTERED ([id] ASC), 
    JoinGuid UniqueIdentifier NULL 
) 

CREATE TABLE [dbo].[table2] ( 
    [id] [int] IDENTITY(1,1) NOT NULL, 
    [table1_id] [int] NULL, 
    [data] [varchar](255) NOT NULL, 
    CONSTRAINT [PK_table2] PRIMARY KEY CLUSTERED ([id] ASC), 
    JoinGuid UniqueIdentifier NULL 
) 


INSERT INTO Table1.... 

INSERT INTO Table2.... 

UPDATE b 
SET table1_id = a.id 
FROM Table1 a 
JOIN Table2 b on a.JoinGuid = b.JoinGuid 
WHERE b.table1_id IS NULL 

UPDATE Table1 SET JoinGuid = NULL 
UPDATE Table2 SET JoinGuid = NULL 
+0

Come impostare lo stesso 'JoinGuid' per entrambe le tabelle? Questo è semplice se si ha un record in 'Table1' e un record in' Table2', ma non riesco a immaginare come implementarlo in caso di molte righe. –

+0

Ad un certo punto del tuo codice dovresti essere in grado di mettere in relazione i dati, aggiungere il guid in quel punto. – cjk

3

ero anche alle prese con questo problema, e trovo che il modo migliore è quello di utilizzare un CURSOR.

Ho provato la soluzione Denis con OUTPUT, ma come ha menzionato, è impossibile generare colonne esterne in un'istruzione di inserimento e MERGE non può funzionare quando si inseriscono più righe per selezione.

Quindi, ho usato un CURSORE, per ogni riga nel tavolo esterno, ho fatto un INSERT, quindi uso il @@ IDENTITY per un altro INSERT.

DECLARE @OuterID int 

DECLARE MY_CURSOR CURSOR 
    LOCAL STATIC READ_ONLY FORWARD_ONLY 
FOR 
SELECT ID FROM [external_Table] 

OPEN MY_CURSOR 
FETCH NEXT FROM MY_CURSOR INTO @OuterID 

WHILE @@FETCH_STATUS = 0 
BEGIN 
INSERT INTO [Table] (data) 
    SELECT data 
    FROM  [external_Table] where ID = @OuterID 

    INSERT INTO [second_table] (FK,OuterID) 
    VALUES(@OuterID,@@identity) 

    FETCH NEXT FROM MY_CURSOR INTO @OuterID 
END 
CLOSE MY_CURSOR 
DEALLOCATE MY_CURSOR