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

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

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

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

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

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

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

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

Philippe Grondier 06.10.2008 21:56

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

tpower 23.10.2008 13:38
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
155
2
345 753
11
Перейти к ответу Данный вопрос помечен как решенный

Ответы 11

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

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

Dave Markle 29.11.2011 01:00

Insert может работать только с одним столом за раз. Множественные вставки должны иметь несколько операторов.

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

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

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

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

Rex Morgan 15.09.2010 00:52

можно ли использовать "OUTPUT INTO" дважды в одном sql

David 18.04.2020 11:46

@ V.Wu Я так не думаю, мне нужно настроить тест, чтобы увидеть.

Cade Roux 19.04.2020 19:06

В одном утверждение: Нет.

В одном сделка: Да

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

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

Это то, что я ищу давно. Спасибо :)

nandu.com 14.04.2011 21:24

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

Kirk Woll 10.09.2011 07:07

Лучший ответ! Я потратил так много времени, пытаясь понять, как объединить вставки в один запрос. Спасибо!!

MonkeyDoug 19.04.2013 00:33

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

hofnarwillie 25.04.2013 19:25

@hofnarwillie прав, с этим решением вам все равно нужно объявить цикл явно.

Fortunato 02.12.2013 13:52

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

Joel Coehoorn 17.11.2015 17:42

откуда @ObjectID? нужно где-то декларировать?

peractio 11.03.2020 02:04

@peractio Это здесь в качестве примера, чтобы показать, что вы можете смешивать результат DataID с чем-то, что было получено ранее. Он может быть объявлен ранее, в клиентском коде через параметр запроса или как аргумент хранимой процедуры.

Joel Coehoorn 11.03.2020 02:35

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

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

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

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

Rhyous 07.12.2014 00:57

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

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

Ответ принят как подходящий

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

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

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

the OUTPUT INTO clause cannot be on either side of a (primary key, foreign key) relationship

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

Я не думаю, что это правильный ответ, поскольку он использует таблицы базы данных, которые добавляют значимости в системах баз данных с высокой нагрузкой. Лучшее решение - это решение "Сергея Зиновьева", которое является дополнением к вышеприведенному высокопоставленному ответу. Обратите внимание, что ответ с высоким рейтингом неверен без предложения «SET XACT_ABORT ON;».

Ahmed 12.08.2020 12:04

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

$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 23.07.2012 23:49

-1. Этот ответ, похоже, использует методы MySQL в PHP. Вопрос помечен тегами sql и sql-сервер, без упоминания MySQL или PHP.

mskfisher 03.01.2014 01:56

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

Не могли бы вы добавить пояснения?

Kyll 07.05.2015 16:50

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

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

Только ваш ответ является наиболее актуальным и правильным. Утвержденный ответ не самый лучший. Ответ, получивший наибольшее количество голосов, содержит упомянутый вами недостаток.

Ahmed 11.08.2020 12:51

Другие вопросы по теме