SQL Server: возможно ли вставить в две таблицы одновременно?


143

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

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

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

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

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

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


Меня не интересует попытка сделать это с ОДНОЙ вставкой, когда работа с 2 вставками работает на отлично. Вы хотите убедиться, что оба вкладыша завершены? Затем вам нужно будет проверить эту инструкцию коммита / отката.
Филипп Грондиер

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

Ответы:


219

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

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

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

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


2
Это то, что я ищу долго. Спасибо :)
nandu.com

33
@ Джоэл, отличный вопрос. Предположительно кто-то хотел альтернативной реальности, а вы были носителями плохих новостей. ;)
Кирк Уолл

2
это спасло мой день сегодня :) спасибо
Shekhar_Pro

12
Это не решает проблему. Он хочет вставить данные, прочитанные из Object_Table. Т.е. insert into ... select ...заявление. Как приведенный выше код читает или перебирает данные Object_Table. Затем вам все еще нужно использовать табличную переменную, которую запрашивающий не хотел делать.
hofnarwillie

8
Конечно, это решает проблему. Возможно, я написал не весь код для этого, но затем ОП не разделял все столбцы, которые он хотел скопировать. Функции, продемонстрированные в этом ответе, позволят ОП делать то, что он просит ... выполнить запрос, чтобы создать запись, получить идентификатор новой записи и использовать этот идентификатор для второй записи атомарным способом. ОП уже знает, как сделать вставку / выбор. Это часть, которую он пропустил.
Джоэл Коухорн

35

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


1
Спасибо! Я не знал о ключевом слове OUTPUT, именно то, что я искал. +1
Рекс Морган

Можно ли использовать «OUTPUT INTO» дважды в одном sql
V.Wu

@ V. Не думаю, мне нужно будет провести тест, чтобы увидеть.
Cade Roux

18

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

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

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

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

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


Почему никто не проголосовал против тебя? Хранимая процедура является очевидным и лучшим способом. Объедините свой ответ с ответом Джоэла Кохорна, и вы получите лучший ответ!
Rhyous

4

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


2
Действия являются атомарными, если они заключены в транзакцию, а не «более или менее» атомарными. То, что не обязательно гарантировано, является уровнем изоляции, если вы не укажете это.
Дейв Маркл

4

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


4

Я хочу подчеркнуть использование

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

2

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

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

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


2

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


-1
-- ================================================
-- 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

Не могли бы вы добавить некоторые объяснения?
Кайлл

-2

// если вы хотите вставить то же самое, что и первая таблица

$qry = "INSERT INTO table (one, two, three) VALUES('$one','$two','$three')";

$result = @mysql_query($qry);

$qry2 = "INSERT INTO table2 (one,two, three) VVALUES('$one','$two','$three')";

$result = @mysql_query($qry2);

// или если вы хотите вставить определенные части таблицы один

 $qry = "INSERT INTO table (one, two, three) VALUES('$one','$two','$three')";


  $result = @mysql_query($qry);

 $qry2 = "INSERT INTO table2 (two) VALUES('$two')";

 $result = @mysql_query($qry2);

// я знаю, что это выглядит слишком хорошо, чтобы быть правым, но это работает, и вы можете продолжать добавлять запрос, просто изменив

    "$qry"-number and number in @mysql_query($qry"")

У меня есть 17 таблиц, в которых это работало.


если что-то пойдет не так в середине вставок? Ваши вкладыши будут неполными. право? Если это так .. у вас есть функция отката для его лечения? Если нет .. у вас есть проблемы с целостностью ваших данных.
Deepcell

7
-1. Этот ответ, кажется, использует методы MySQL в PHP. Вопрос помечен как sql и sql-server , без упоминания о MySQL или PHP.
mskfisher
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.