2010-09-14 72 views
49

Digamos que mi estructura de la tabla es como la siguiente:¿Cómo puedo INSERTAR datos en dos tablas simultáneamente en 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) 
) 

El campo de la primera tabla [id] corresponde al campo de la segunda [table1_id]. Lo que me gustaría hacer es insertar datos en ambas tablas en una sola transacción. Ahora ya sé cómo hacer esto haciendo INSERT-SELECT INSERT, así:

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; 

Eso es todo bien y bien para pequeños casos como el que sólo está insertando tal vez un puñado de filas. Pero lo que tengo que hacer es insertar un par de cientos de miles de filas, o posiblemente incluso un millón de filas, todas a la vez. Los datos proviene de otra mesa, así que si yo era sólo insertarlo en una sola tabla, que sería fácil, yo sólo tengo que hacer esto:

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

Pero ¿cómo voy a hacer esto y dividir el datos en [table1] y [table2], y todavía actualizar [table2] con el [table1_id] apropiado como lo estoy haciendo? ¿Es eso posible?

+0

Buena pregunta muchos de los principiantes a SQL a menudo preguntan. –

Respuesta

29

Prueba esto:

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

ACTUALIZACIÓN: 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] 

Es imposible salida columnas externas en una declaración insert, así que creo que podría hacer algo como esto

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, solo he usado OUTPUT para escribir en la variable de la tabla. ¿Puedes usarlo para insertar directamente en una mesa en vivo? – Bill

+0

@ Bill you betcha! –

+0

Por cierto, no hay necesidad de abrazarlo en las declaraciones 'begin tran ... commit tran', ya que se va a ejecutar en una sola transacción. –

-1

Puede escribir un procedimiento almacenado que itere sobre la transacción que ha propuesto. El iterador sería el cursor de la tabla que contiene los datos de origen.

+3

Nunca itere cuando hay una solución basada en conjuntos. – HLGEM

+0

@HLGEM - dado mi error, ¿cuál de las soluciones propuestas recomendaría? – mlschechter

+0

Definitivamente la cláusula de salida de una tabla temporal. – 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

Dada la respuesta de Denis a mi comentario, su solución es mucho más limpia que la mía. – Bill

+0

No puede usar 'source.id, new.id' en la cláusula' output'. Solo se le permite usar 'insertado. *' Para 'insertar'. Para 'delete',' update' y 'merge', es posible incluir una columna de la tabla especificada. –

1

Eche un vistazo a SQL Server para que admita la instrucción 'INSERT ALL'. Oracle tiene ya, parece que esto (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

Otra opción es ejecutar los dos insertos separado, dejando la columna nula FK, a continuación, ejecutar una actualización de poulate correctamente.

Si no hay nada natural almacenado dentro de las dos tablas que coinciden de un registro a otro (probable), entonces cree una columna GUID temporal y rellene esto en sus datos e insértelo en ambos campos. Luego puede actualizar con el FK adecuado y anular los GUID.

ej .:

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

¿Cómo establecer el mismo 'JoinGuid' para ambas tablas? Esto es simple si tiene un registro en 'Table1' y un registro en' Table2', pero no puedo imaginar cómo implementar esto en el caso de muchas filas. –

+0

En algún punto de tu código, deberías poder relacionar los datos, agregar el guid en ese punto. – cjk

3

también estaba luchando con este problema, y ​​parece que la mejor manera es usar un CURSOR.

He intentado la solución Denis con OUTPUT, pero como mencionó, es imposible generar columnas externas en una instrucción de inserción, y la MERGE no puede funcionar cuando se insertan varias filas por selección.

Así que, he usado un CURSOR, para cada fila en la tabla externa, he hecho un INSERTO, luego uso @@ IDENTIDAD para otro INSERTAR.

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 
Cuestiones relacionadas