SQL Server: возможно ли вставить в две таблицы одновременно? - PullRequest
127 голосов
/ 06 октября 2008

Моя база данных содержит три таблицы с именами Object_Table, Data_Table и Link_Table. Таблица ссылок содержит только два столбца: идентификатор записи объекта и идентификатор записи данных.

Я хочу скопировать данные из DATA_TABLE, где они связаны с одним указанным идентификатором объекта, и вставить соответствующие записи в Data_Table и Link_Table для другого заданного идентификатора объекта.

I может сделать это, выбрав в табличную переменную и циклически выполняя две вставки для каждой итерации.

Это лучший способ сделать это?

Редактировать : Я хочу избежать цикла по двум причинам: во-первых, я ленива и таблица цикла / температуры требует больше кода, чем больше кода, тем больше мест для ошибок и Вторая причина - это беспокойство по поводу производительности.

Я могу скопировать все данные в одну вставку, но как получить таблицу ссылок для ссылки на новые записи данных, где каждая запись имеет новый идентификатор?

Ответы [ 11 ]

202 голосов
/ 06 октября 2008

В одном заявлении : Нет.

В одной транзакции : Да

BEGIN TRANSACTION
   DECLARE @DataID int;
   INSERT INTO DataTable (Column1 ...) VALUES (....);
   SELECT @DataID = scope_identity();
   INSERT INTO LinkTable VALUES (@ObjectID, @DataID);
COMMIT

Хорошая новость заключается в том, что приведенный выше код также гарантированно равен atomic и может быть отправлен на сервер из клиентского приложения с одной строкой sql в одном вызове функции, как если бы это был один оператор , Вы также можете применить триггер к одной таблице, чтобы получить эффект одной вставки. Однако в конечном итоге это все еще два оператора, и вы, вероятно, не хотите запускать триггер для каждой вставки.

31 голосов
/ 06 октября 2008

Вам все еще нужны два INSERT оператора, но звучит так, как будто вы хотите получить IDENTITY из первой вставки и использовать его во второй, и в этом случае вы можете захотеть посмотреть OUTPUT или OUTPUT INTO: http://msdn.microsoft.com/en-us/library/ms177564.aspx

18 голосов
/ 06 октября 2008

Следующее устанавливает мою ситуацию, используя переменные таблицы.

DECLARE @Object_Table TABLE
(
    Id INT NOT NULL PRIMARY KEY
)

DECLARE @Link_Table TABLE
(
    ObjectId INT NOT NULL,
    DataId INT NOT NULL
)

DECLARE @Data_Table TABLE
(
    Id INT NOT NULL Identity(1,1),
    Data VARCHAR(50) NOT NULL
)

-- create two objects '1' and '2'
INSERT INTO @Object_Table (Id) VALUES (1)
INSERT INTO @Object_Table (Id) VALUES (2)

-- create some data
INSERT INTO @Data_Table (Data) VALUES ('Data One')
INSERT INTO @Data_Table (Data) VALUES ('Data Two')

-- link all data to first object
INSERT INTO @Link_Table (ObjectId, DataId)
SELECT Objects.Id, Data.Id
FROM @Object_Table AS Objects, @Data_Table AS Data
WHERE Objects.Id = 1

Благодаря другому ответу , который направил меня к предложению OUTPUT, я могу продемонстрировать решение:

-- now I want to copy the data from from object 1 to object 2 without looping
INSERT INTO @Data_Table (Data)
OUTPUT 2, INSERTED.Id INTO @Link_Table (ObjectId, DataId)
SELECT Data.Data
FROM @Data_Table AS Data INNER JOIN @Link_Table AS Link ON Data.Id = Link.DataId
                INNER JOIN @Object_Table AS Objects ON Link.ObjectId = Objects.Id 
WHERE Objects.Id = 1

Оказывается, однако, что это не так просто в реальной жизни из-за следующей ошибки

предложение OUTPUT INTO не может быть включено любая сторона (первичный ключ, внешний ключ) отношения

Я все еще могу OUTPUT INTO временную таблицу и затем закончить с обычной вставкой. Таким образом, я могу избежать цикла, но не могу избежать таблицы временных значений.

6 голосов
/ 06 октября 2008

Звучит так, будто таблица ссылок фиксирует взаимосвязь «многие: многие» между таблицей объектов и таблицей данных.

Я предлагаю использовать хранимую процедуру для управления транзакциями. Если вы хотите вставить данные в таблицу объектов или данных, выполните вставки, получите новые идентификаторы и вставьте их в таблицу ссылок.

Это позволяет сохранить всю логику в одном простом для вызова sproc.

4 голосов
/ 01 ноября 2016

Я хочу подчеркнуть, на

SET XACT_ABORT ON;

для транзакции MSSQL с несколькими операторами SQL.

См .: https://msdn.microsoft.com/en-us/library/ms188792.aspx Они дают очень хороший пример.

Итак, окончательный код должен выглядеть следующим образом:

SET XACT_ABORT ON;

BEGIN TRANSACTION
   DECLARE @DataID int;
   INSERT INTO DataTable (Column1 ...) VALUES (....);
   SELECT @DataID = scope_identity();
   INSERT INTO LinkTable VALUES (@ObjectID, @DataID);
COMMIT
4 голосов
/ 06 октября 2008

Вы можете создать представление, выбрав имена столбцов, требуемые оператором вставки, добавить триггер INSTEAD OF INSERT и вставить в это представление.

4 голосов
/ 06 октября 2008

Если вы хотите, чтобы действия были более или менее атомарными, я бы обязательно включил их в транзакцию. Таким образом, вы можете быть уверены, что оба произошли или оба не произошли, как нужно.

2 голосов
/ 06 октября 2008

Прежде чем вы сможете выполнить множественную вставку в Oracle, вы могли бы использовать прием, включающий вставку в представление, для которого был определен триггер INSTEAD OF для выполнения вставок. Можно ли это сделать в SQL Server?

2 голосов
/ 06 октября 2008

Вставка может работать только на одном столе одновременно. Несколько вкладок должны иметь несколько операторов.

Я не знаю, что вам нужно выполнить циклическое преобразование табличной переменной - вы не можете просто использовать массовую вставку в одну таблицу, а затем массовую вставку в другую?

Кстати - наверное, вы имеете в виду скопировать данные из Object_Table; в противном случае вопрос не имеет смысла.

0 голосов
/ 07 мая 2015
-- ================================================
-- Template generated from Template Explorer using:
-- Create Procedure (New Menu).SQL
--
-- Use the Specify Values for Template Parameters 
-- command (Ctrl-Shift-M) to fill in the parameter 
-- values below.
--
-- This block of comments will not be included in
-- the definition of the procedure.
-- ================================================
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE InsetIntoTwoTable

(
@name nvarchar(50),
@Email nvarchar(50)
)

AS
BEGIN

    SET NOCOUNT ON;


    insert into dbo.info(name) values (@name)
    insert into dbo.login(Email) values (@Email)
END
GO
...