2010-09-14 1 views
49

Скажем, моя структура таблицы выглядит так:Как я могу вставить данные в две таблицы одновременно в 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) 
) 

[id] поле первой таблицы соответствует [table1_id] поле второго. То, что я хотел бы сделать, это вставить данные в обе таблицы в одну транзакцию. Теперь я уже знаю, как это сделать, выполнив INSERT-SELECT-INSERT, например:

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; 

Это все хорошо и прекрасно подходит для небольших подобных случаев, когда вы только вставки, может быть несколько строк. Но мне нужно вставить пару сотен тысяч строк или, возможно, даже миллион строк, все сразу. Данные поступают из другой таблицы, так что, если я только вставить его в одну таблицу, это было бы легко, я просто должен сделать это:

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

Но как бы я это сделать и расколоть данных в [table1] и [table2], и все еще обновлять [table2] с соответствующим [table1_id], как я это делаю? Возможно ли это?

+0

Хороший вопрос для многих новичков на SQL часто спрашивают. –

ответ

29

Попробуйте это:

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] 

Это невозможно для вывода внешних колонок в качестве insert заявления, так что я думаю, что вы могли бы сделать что-то подобное

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, я использовал OUTPUT только для записи в переменную таблицы. Можете ли вы использовать его для непосредственного вставки в живой стол? – Bill

+0

@ Поверьте, вы betcha! –

+0

Кстати, нет необходимости включать его в 'begin tran ... commit tran', поскольку он явно будет запущен в одной транзакции. –

-1

Вы можете написать хранимую процедуру, которая выполняет итерацию по транзакции, которую вы предложили. Итератором будет курсор для таблицы, содержащей исходные данные.

+3

Никогда не повторяйте, когда есть решение на основе набора. – HLGEM

+0

@HLGEM - учитывая мою ошибку, какое из предлагаемых решений вы бы порекомендовали? – mlschechter

+0

Определенно предложение вывода в таблице темпа. – 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

Учитывая ответ Дениса на мой комментарий, его решение намного чище, чем мое. – Bill

+0

Вы не можете использовать 'source.id, new.id' в предложении' output'. Вам разрешено использовать 'insert. *' Там для 'insert'. Для 'delete',' update' и 'merge' можно включить столбец из указанной таблицы. –

1

Обратите внимание, что SQL Server поддерживает инструкцию INSERT ALL. Oracle имеет уже, это выглядит следующим образом (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

Другим вариантом является запуск двух вставок отдельно, оставляя колонки нуль FK, затем запустить обновление для poulate правильно.

Если в двух таблицах нет ничего естественного, которые соответствуют одной записи другим (скорее всего), создайте временный столбец GUID и заполните это в своих данных и вставьте в оба поля. Затем вы можете обновить с помощью FK и исключить GUID.

т.д .:

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

Как установить одинаковый 'JoinGuid' для обеих таблиц? Это просто, если у вас есть одна запись в «Таблица1» и одна запись в «Таблице2», но я не могу представить, как реализовать это в случае многих строк. –

+0

В какой-то момент вашего кода вы должны иметь возможность связать данные, добавьте guid в эту точку. – cjk

3

Я также борется с этой проблемой, и считают, что лучший способ заключается в использовании CURSOR.

Я пробовал решение Denis с OUTPUT, но, как он упоминает, невозможно выводить внешние столбцы в инструкции insert, а MERGE не может работать, когда вы вставляете несколько строк по выбору.

Итак, я использовал КУРСОР, для каждой строки во внешней таблице я сделал INSERT, а затем использовал @@ IDENTITY для другого 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