SQL Server: Чи можна вставити дві таблиці одночасно?


143

Моя база даних містить три таблиці називається Object_Table, Data_Tableі Link_Table. Таблиця посилань містить лише два стовпці, ідентифікацію об'єктного запису та особу запису даних.

Я хочу скопіювати дані, DATA_TABLEзвідки вони пов'язані з однією заданою ідентичністю об'єкта, і вставити відповідні записи в Data_Tableта Link_Tableдля іншої заданої ідентичності об'єкта.

Я можу це зробити, вибравши змінну таблиці та циклічно виконуючи два вставки для кожної ітерації.

Це найкращий спосіб зробити це?

Редагувати : Я хочу уникати циклу з двох причин. Перша полягає в тому, що я лінивий, а таблиця циклу / темпу вимагає більше коду, більше коду означає більше місць, щоб помилитися, а друга причина - занепокоєння щодо продуктивності.

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


Мені не цікаво намагатися зробити це з однією вставкою, коли це робиться з двома вставками, працює чудово. Ви маєте на увазі, що хочете переконатися, що обидві вставки виконані? Тоді вам доведеться перевірити цю інструкцію на фіксацію / відкат.
Філіп Гронд'є

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
@Joel, чудове запитання. Імовірно, хтось бажав альтернативної реальності, а ти був носієм поганих новин. ;)
Кірк Уолл

2
це врятувало мені сьогоднішній день :)
подяка

12
Це не вирішує проблему. Він хоче вставити дані, прочитані з Object_Table. Тобто insert into ... select ...заява. Як читається вищезгаданий код чи проходить цикл через дані Object_Table. Потім вам потрібно використовувати змінну таблиці, яку запитувач не хотів робити.
hofnarwillie

8
Звичайно, це вирішує проблему. Може бути , я не писав все коду для цього, але ОП не поділяють всі стовпці , які він хотів скопіювати, або. Особливості, продемонстровані у цій відповіді, дозволять ОП виконувати те, що він просить ... запустити запит, щоб створити запис, отримати ідентифікатор нового запису та використовувати цей ідентифікатор для другого запису атомним способом. ОП вже знає, як зробити вставку / вибір. Це той твір, якого він пропав.
Joel Coehoorn

35

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


1
Дякую! Я не знав про ключове слово OUTPUT, саме те, що шукав. +1
Рекс Морган

чи можна використовувати "OUTPUT INTO" двічі в одному sql
V.Wu

@ V.Wu Я не думаю, що мені доведеться створити тест, щоб побачити.
Кейд Ру

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

Схоже, таблиця посилань захоплює багато: багато стосунків між таблицею Об'єкт і таблицею даних.

Моя пропозиція - використовувати збережену процедуру для управління транзакціями. Коли ви хочете вставити в таблицю Об'єкт або Дані виконайте свої вставки, дістаньте нові ідентифікатори та вставте їх у таблицю Посилання.

Це дозволяє всій вашій логіці залишатися капсульованим в одному простому для виклику паростку.


Чому ще ніхто не звернувся до вас? Збережена процедура - це очевидний і найкращий спосіб. Поєднайте свою відповідь з відповіддю Джоела Куегорна і отримаєте найкращу відповідь!
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

Чи можете ви додати трохи пояснень?
Kyll

-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-сервера , без згадки про MySQL чи PHP.
mskfisher
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.