SQL Server: Có thể chèn vào hai bảng cùng một lúc không?


143

Cơ sở dữ liệu của tôi chứa ba bảng gọi Object_Table, Data_TableLink_Table. Bảng liên kết chỉ chứa hai cột, danh tính của một bản ghi đối tượng và danh tính của một bản ghi dữ liệu.

Tôi muốn sao chép dữ liệu từ DATA_TABLEnơi nó được liên kết với một danh tính đối tượng cụ thể và chèn các bản ghi tương ứng vào Data_TableLink_Tablecho một danh tính đối tượng cụ thể khác.

Tôi có thể làm điều này bằng cách chọn vào một biến bảng và lặp qua hai lần chèn cho mỗi lần lặp.

Đây có phải là cách tốt nhất để làm điều đó?

Chỉnh sửa : Tôi muốn tránh một vòng lặp vì hai lý do, thứ nhất là tôi lười biếng và bảng vòng lặp / temp yêu cầu nhiều mã hơn, nhiều mã hơn có nghĩa là nhiều chỗ hơn để mắc lỗi và lý do thứ hai là mối quan tâm về hiệu suất.

Tôi có thể sao chép tất cả dữ liệu trong một lần chèn nhưng làm cách nào để có được bảng liên kết để liên kết đến các bản ghi dữ liệu mới trong đó mỗi bản ghi có một id mới?


Tôi không có hứng thú khi thử làm điều đó với MỘT lần chèn, khi thực hiện với 2 lần chèn hoạt động hoàn toàn tốt. Bạn có nghĩa là bạn muốn đảm bảo rằng 2 phần chèn được hoàn thành? Sau đó, bạn sẽ phải kiểm tra hướng dẫn cam kết / rollback này.
Philippe Grondier

2
Tôi sẽ rất vui với hai lần chèn, chỉ là các danh tính cần được chèn vào bảng liên kết là các danh tính được tạo trong lần chèn đầu tiên.
sức mạnh

Câu trả lời:


220

Trong một tuyên bố : Không.

Trong một giao dịch : Có

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

Tin tốt là mã trên cũng được đảm bảo là nguyên tử và có thể được gửi đến máy chủ từ một ứng dụng khách với một chuỗi sql trong một lệnh gọi hàm như thể đó là một câu lệnh. Bạn cũng có thể áp dụng một kích hoạt cho một bảng để có được hiệu ứng của một lần chèn. Tuy nhiên, cuối cùng vẫn là hai câu lệnh và có lẽ bạn không muốn chạy trình kích hoạt cho mỗi lần chèn.


2
Đây là những gì tôi đang tìm kiếm trong một thời gian dài. Cảm ơn :)
nandu.com

33
@Joel, câu hỏi tuyệt vời. Có lẽ ai đó muốn một thực tế thay thế và bạn là người mang tin xấu. ;)
Kirk Woll

2
điều này đã cứu ngày hôm nay của tôi :) thanx
Shekhar_Pro

12
Điều này không giải quyết được vấn đề. Anh ta muốn chèn dữ liệu đọc từ Object_Table. Tức là một insert into ... select ...tuyên bố. Làm thế nào để đoạn mã trên đọc hoặc lặp qua dữ liệu Object_Table. Sau đó, bạn vẫn cần sử dụng một biến bảng mà người hỏi không muốn làm.
hofnarwillie

8
Chắc chắn điều này giải quyết vấn đề. Có thể tôi đã không viết tất cả mã cho việc này, nhưng sau đó OP cũng không chia sẻ tất cả các cột mà anh ấy muốn sao chép. Các tính năng thể hiện trong câu trả lời này sẽ cho phép OP thực hiện những gì anh ta yêu cầu ... chạy truy vấn để tạo bản ghi, lấy ID của bản ghi mới và sử dụng ID đó cho bản ghi thứ hai theo cách nguyên tử. OP đã biết cách thực hiện thao tác chèn / chọn. Đây là mảnh anh đã mất tích.
Joel Coehoorn

35

Bạn vẫn cần hai INSERTcâu lệnh, nhưng có vẻ như bạn muốn lấy IDENTITYtừ lần chèn đầu tiên và sử dụng nó trong lần thứ hai, trong trường hợp đó, bạn có thể muốn xem xét OUTPUThoặc OUTPUT INTO: http://msdn.microsoft.com/en- chúng tôi / thư viện / ms177564.aspx


1
Cảm ơn! Tôi không biết về từ khóa OUTPUT, chính xác những gì tôi đang tìm kiếm. +1
Rex Morgan

có thể sử dụng "OUTPUT INTO" hai lần trong một sql
V.Wu

@ V.Wu Tôi không nghĩ vậy, tôi sẽ phải thiết lập một bài kiểm tra để xem.
Cade Roux

18

Sau đây thiết lập tình huống tôi có, sử dụng các biến bảng.

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

Nhờ một câu trả lời khác chỉ cho tôi về mệnh đề OUTPUT, tôi có thể chứng minh một giải pháp:

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

Tuy nhiên, hóa ra nó không đơn giản trong cuộc sống thực vì lỗi sau

mệnh đề OUTPUT INTO không thể ở hai bên của mối quan hệ (khóa chính, khóa ngoại)

Tôi vẫn có thể OUTPUT INTOmột bảng tạm thời và sau đó kết thúc với chèn bình thường. Vì vậy, tôi có thể tránh vòng lặp của mình nhưng tôi không thể tránh bảng tạm thời.



6

Nghe có vẻ như bảng Liên kết nắm bắt được nhiều: nhiều mối quan hệ giữa bảng Đối tượng và bảng Dữ liệu.

Đề nghị của tôi là sử dụng một thủ tục được lưu trữ để quản lý các giao dịch. Khi bạn muốn chèn vào bảng Object hoặc Data, hãy thực hiện các phần chèn của bạn, lấy ID mới và chèn chúng vào bảng Link.

Điều này cho phép tất cả logic của bạn vẫn được gói gọn trong một sproc dễ gọi.


Tại sao không có ai khác nâng đỡ bạn? Các thủ tục lưu trữ là cách rõ ràng và tốt nhất. Kết hợp câu trả lời của bạn với câu trả lời của Joel Coehoorn và bạn sẽ có câu trả lời tốt nhất!
Rhyous

4

Nếu bạn muốn các hành động trở thành nguyên tử nhiều hơn hoặc ít hơn, tôi sẽ đảm bảo bọc chúng trong một giao dịch. Bằng cách đó bạn có thể chắc chắn cả hai đã xảy ra hoặc cả hai đã không xảy ra khi cần thiết.


2
Các hành động là nguyên tử nếu chúng được bao bọc trong một giao dịch, không phải là "ít nhiều" nguyên tử. Điều không nhất thiết được đảm bảo là mức độ cô lập, trừ khi bạn chỉ định như vậy.
Dave Markle

4

Bạn có thể tạo Chế độ xem chọn tên cột theo yêu cầu chèn của mình, thêm Trình kích hoạt INSTEAD OF INSERT và chèn vào dạng xem này.


4

Tôi muốn nhấn mạnh vào việc sử dụng

SET XACT_ABORT ON;

cho giao dịch MSSQL với nhiều báo cáo sql.

Xem: https://msdn.microsoft.com/en-us/l Library / ms188792.aspx Họ cung cấp một ví dụ rất hay.

Vì vậy, mã cuối cùng sẽ trông như sau:

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

Chèn chỉ có thể hoạt động trên một bảng tại một thời điểm. Nhiều Chèn phải có nhiều câu lệnh.

Tôi không biết rằng bạn cần thực hiện việc lặp qua một biến bảng - bạn không thể chỉ sử dụng một khối chèn vào một bảng, sau đó khối này chèn vào bảng khác?

Nhân tiện - tôi đoán bạn có nghĩa là sao chép dữ liệu từ Object_Table; nếu không thì câu hỏi không có ý nghĩa


2

Trước khi có thể thực hiện một thao tác chèn đa năng trong Oracle, bạn có thể sử dụng một mẹo liên quan đến việc chèn vào một khung nhìn có trình kích hoạt INSTEAD OF được xác định trên đó để thực hiện các thao tác chèn. Điều này có thể được thực hiện trong SQL Server không?


-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

Bạn có thể thêm một số giải thích?
Kyll

-2

// nếu bạn muốn chèn giống như bảng đầu tiên

$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);

// hoặc nếu bạn muốn chèn một số phần nhất định của bảng một

 $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);

// tôi biết nó có vẻ quá tốt để đúng, nhưng nó hoạt động và bạn có thể tiếp tục thêm truy vấn chỉ cần thay đổi

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

Tôi có 17 bảng này đã làm việc trong.


nếu có gì sai ở giữa các phần chèn? Chèn của bạn sẽ không đầy đủ. đúng? Nếu làm như vậy .. bạn có chức năng rollback để điều trị không? Nếu không .. bạn có vấn đề với tính toàn vẹn dữ liệu của bạn.
deepcell

7
-1. Câu trả lời này dường như đang sử dụng các phương thức MySQL trong PHP. Câu hỏi được gắn thẻ sqlsql-server , không đề cập đến MySQL hoặc PHP.
mskfisher
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.