Моя база данных содержит три таблицы с именами Object_Table, Data_Table и Link_Table. Таблица ссылок содержит только два столбца: идентификатор записи объекта и идентификатор записи данных.
Я хочу скопировать данные из DATA_TABLE, где они связаны с одним заданным идентификатором объекта, и вставить соответствующие записи в Data_Table и Link_Table для другого заданного идентификатора объекта.
Я может делаю это, выбирая переменную таблицы и выполняя цикл, выполняя две вставки для каждой итерации.
Это лучший способ сделать это?
Редактировать: я хочу избежать цикла по двум причинам: во-первых, я ленив, а для таблицы цикла / времени требуется больше кода, больше кода означает больше мест для ошибки, а вторая причина - беспокойство о производительности.
Я могу скопировать все данные в одну вставку, но как заставить таблицу ссылок связываться с новыми записями данных, где каждая запись имеет новый идентификатор?
Я был бы доволен двумя вставками, просто идентификаторы, которые необходимо вставить в таблицу ссылок, - это идентификаторы, сгенерированные в первой вставке.


Если вы хотите, чтобы действия были более или менее атомарными, я бы обязательно заключил их в транзакцию. Таким образом, вы можете быть уверены, что оба произошли или оба произошли не так, как нужно.
Действия являются атомарными, если они заключены в транзакцию, а не «более или менее» атомарными. Что не обязательно гарантируется, так это уровень изоляции, если вы этого не укажете.
Insert может работать только с одним столом за раз. Множественные вставки должны иметь несколько операторов.
Я не знаю, нужно ли вам выполнять цикл через переменную таблицы - разве вы не можете просто использовать массовую вставку в одну таблицу, а затем массовую вставку в другую?
Кстати - я полагаю, вы имеете в виду скопировать данные из Object_Table; иначе вопрос не имеет смысла.
Вам по-прежнему нужны два оператора INSERT, но похоже, что вы хотите получить IDENTITY из первой вставки и использовать его во второй, и в этом случае вы можете посмотреть на OUTPUT или OUTPUT INTO: http://msdn.microsoft.com/en-us/library/ms177564.aspx
Спасибо! Я не знал о ключевом слове OUTPUT, именно то, что я искал. +1
можно ли использовать "OUTPUT INTO" дважды в одном sql
@ V.Wu Я так не думаю, мне нужно настроить тест, чтобы увидеть.
В одном утверждение: Нет.
В одном сделка: Да
BEGIN TRANSACTION
DECLARE @DataID int;
INSERT INTO DataTable (Column1 ...) VALUES (....);
SELECT @DataID = scope_identity();
INSERT INTO LinkTable VALUES (@ObjectID, @DataID);
COMMIT
Хорошей новостью является то, что приведенный выше код также гарантированно будет атомный и может быть отправлен на сервер из клиентского приложения с одной строкой sql в одном вызове функции, как если бы это был один оператор. Вы также можете применить триггер к одной таблице, чтобы получить эффект одной вставки. Однако в конечном итоге это все еще два оператора, и вы, вероятно, не захотите запускать триггер для вставки каждый.
Это то, что я ищу давно. Спасибо :)
@ Джоэл, отличный вопрос. Предположительно кто-то желал альтернативной реальности, а вы были носителем плохих новостей. ;)
Лучший ответ! Я потратил так много времени, пытаясь понять, как объединить вставки в один запрос. Спасибо!!
Это не решает проблемы. Он хочет вставить данные, прочитанные из Object_Table. Т.е. заявление insert into ... select .... Как приведенный выше код читает или просматривает данные Object_Table. Вам все равно нужно использовать табличную переменную, которую запрашивающий не хотел делать.
@hofnarwillie прав, с этим решением вам все равно нужно объявить цикл явно.
Конечно, это решает проблему. Возможно, я не писал для этого все кода, но тогда OP также не разделял все столбцы, которые он хотел скопировать. Функции, продемонстрированные в этом ответе, позволят OP делать то, что он просит ... запустить запрос для создания записи, получить идентификатор новой записи и использовать этот идентификатор для второй записи атомарным способом. OP уже знает, как делать вставку / выбор. Это то, что ему не хватало.
откуда @ObjectID? нужно где-то декларировать?
@peractio Это здесь в качестве примера, чтобы показать, что вы можете смешивать результат DataID с чем-то, что было получено ранее. Он может быть объявлен ранее, в клиентском коде через параметр запроса или как аргумент хранимой процедуры.
Похоже, что таблица ссылок фиксирует взаимосвязь «многие: многие» между таблицей объектов и таблицей данных.
Я предлагаю использовать хранимую процедуру для управления транзакциями. Если вы хотите вставить в таблицу объектов или данных, выполните свои вставки, получите новые идентификаторы и вставьте их в таблицу ссылок.
Это позволяет всей вашей логике оставаться инкапсулированной в одном легко вызываемом sproc.
Почему никто не поддержал вас? Хранимая процедура - очевидный и лучший способ. Объедините свой ответ с ответом Джоэла Кохорна, и вы получите лучший ответ!
Вы можете создать представление, выбрав имена столбцов, требуемые вашим оператором вставки, добавить триггер 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;».
// если вы хотите вставить то же, что и первая таблица
$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 таблиц, в которых это работало.
если что-то пойдет не так в середине вставок? Ваши вставки будут неполными. Правильно? Если так .. есть ли у вас функция отката для лечения? Если нет ... у вас проблема с целостностью данных.
-1. Этот ответ, похоже, использует методы MySQL в PHP. Вопрос помечен тегами sql и sql-сервер, без упоминания MySQL или PHP.
-- ================================================
-- 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
Не могли бы вы добавить пояснения?
Я хочу подчеркнуть использование
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
Только ваш ответ является наиболее актуальным и правильным. Утвержденный ответ не самый лучший. Ответ, получивший наибольшее количество голосов, содержит упомянутый вами недостаток.
Я не испытываю интереса к тому, чтобы делать это с ОДНОЙ вставкой, когда это делается с двумя вставками, работает отлично. Вы имеете в виду, что хотите убедиться, что обе вставки выполнены? Затем вам нужно будет проверить эту инструкцию фиксации / отката.