Có thể để mệnh đề Đầu ra SQL trả về một cột không được chèn không?


123

Tôi đã thực hiện một số sửa đổi đối với cơ sở dữ liệu của mình và tôi cần di chuyển dữ liệu cũ sang các bảng mới. Để làm được điều đó, tôi cần điền vào một bảng (ReportOptions) lấy dữ liệu từ bảng gốc (Practice) và điền vào bảng trung gian thứ hai (PracticeReportOption).

ReportOption (ReportOptionId int PK, field1, field2...)
Practice (PracticeId int PK, field1, field2...)
PracticeReportOption (PracticeReportOptionId int PK, PracticeId int FK, ReportOptionId int FK, field1, field2...)

Tôi đã thực hiện truy vấn để lấy tất cả dữ liệu tôi cần để chuyển từ Practice sang ReportOptions, nhưng tôi gặp sự cố khi điền vào bảng trung gian

--Auxiliary tables
DECLARE @ReportOption TABLE (PracticeId int /*This field is not on the actual ReportOption table*/, field1, field2...)
DECLARE @PracticeReportOption TABLE (PracticeId int, ReportOptionId int, field1, field2)

--First I get all the data I need to move
INSERT INTO @ReportOption
SELECT P.practiceId, field1, field2...
  FROM Practice P

--I insert it into the new table, but somehow I need to have the repation PracticeId / ReportOptionId
INSERT INTO ReportOption (field1, field2...)
OUTPUT @ReportOption.PracticeId, --> this is the field I don't know how to get
       inserted.ReportOptionId
  INTO @PracticeReportOption (PracticeId, ReportOptionId)
SELECT field1, field2
  FROM @ReportOption

--This would insert the relationship, If I knew how to get it!
INSERT INTO @PracticeReportOption (PracticeId, ReportOptionId)
SELECT PracticeId, ReportOptionId
  FROM @ReportOption

Nếu tôi có thể tham chiếu một trường không có trong bảng đích trong mệnh đề OUTPUT, thì điều đó thật tuyệt (tôi nghĩ là không thể, nhưng tôi không biết chắc). Bất kỳ ý tưởng về cách thực hiện nhu cầu của tôi?


1
Bạn có thể trả về bất kỳ cột nào của bảng mà bạn đã chèn một hàng vào, trong OUTPUTmệnh đề của bạn . Vì vậy, ngay cả khi bạn không cung cấp giá trị cho một cột nhất định trong INSERTcâu lệnh của mình , bạn vẫn có thể chỉ định cột đó trong OUTPUTmệnh đề. Tuy nhiên, bạn không thể trả về các biến hoặc cột SQL từ các bảng khác.
marc_s

2
@marc_s nhờ trả lời của bạn, nhưng tôi không có lĩnh vực tôi cần trong bảng đích (Tôi cần PracticeId, mà không phải là trên ReportOption)
Alejandro B.

Câu trả lời:


191

Bạn có thể làm điều này bằng cách sử dụng MERGEthay vì chèn:

vì vậy hãy thay thế cái này

INSERT INTO ReportOption (field1, field2...)
OUTPUT @ReportOption.PracticeId, --> this is the field I don't know how to get
       inserted.ReportOptionId
  INTO @PracticeReportOption (PracticeId, ReportOptionId)
SELECT field1, field2
  FROM @ReportOption

với

MERGE INTO ReportOption USING @ReportOption AS temp ON 1 = 0
WHEN NOT MATCHED THEN
    INSERT (field1, field2)
    VALUES (temp.Field1, temp.Field2)
    OUTPUT temp.PracticeId, inserted.ReportOptionId, inserted.Field1, inserted.Field2
    INTO @PracticeReportOption (PracticeId, ReportOptionId, Field1, Field2);

Điều quan trọng là sử dụng một vị từ sẽ không bao giờ đúng (1 = 0) trong điều kiện tìm kiếm hợp nhất, vì vậy bạn sẽ luôn thực hiện chèn, nhưng có quyền truy cập vào các trường trong cả bảng nguồn và bảng đích.


Đây là toàn bộ mã tôi đã sử dụng để kiểm tra nó:

CREATE TABLE ReportOption (ReportOptionID INT IDENTITY(1, 1), Field1 INT, Field2 INT)
CREATE TABLE Practice (PracticeID INT IDENTITY(1, 1), Field1 INT, Field2 INT)
CREATE TABLE PracticeReportOption (PracticeReportOptionID INT IDENTITY(1, 1), PracticeID INT, ReportOptionID INT, Field1 INT, Field2 INT)

INSERT INTO Practice VALUES (1, 1), (2, 2), (3, 3), (4, 4)


MERGE INTO ReportOption r USING Practice p ON 1 = 0
WHEN NOT MATCHED THEN
    INSERT (field1, field2)
    VALUES (p.Field1, p.Field2)
    OUTPUT p.PracticeId, inserted.ReportOptionId, inserted.Field1, inserted.Field2
    INTO PracticeReportOption (PracticeId, ReportOptionId, Field1, Field2);

SELECT  *
FROM    PracticeReportOption

DROP TABLE ReportOption
DROP TABLE Practice
DROP TABLE PracticeReportOption 

Đọc thêm và nguồn của tất cả những gì tôi biết về chủ đề này là Đây


2
Cảm ơn, điều này thực hiện thủ thuật! Tôi đã định sử dụng trường tạm thời giả nhưng điều này thanh lịch hơn nhiều.
Alejandro B.

1
Thông minh! Bí quyết này là một hạt vàng! Đã thêm vào hàng đầu tiên trong bộ sưu tập!
Vadim Loboda 24/09/13

1
Suweet! Tôi ước gì nó không phải sử dụng lệnh MERGE thỉnh thoảng bị lỗi, nhưng nếu không thì nó hoàn toàn thanh lịch.
Tab Alleman

4
Được cảnh báo. Tôi đã sử dụng một tuyên bố hợp nhất mà trong năm qua đã phát triển với việc sử dụng. Chúng tôi bắt đầu có thời gian chờ trong khi lưu và hóa ra là do câu lệnh hợp nhất luôn khóa các bảng nên chúng tôi đang có 35-160 giây khóa bảng sau mỗi 4 phút. Tôi đang phải tạo lại một số câu lệnh hợp nhất để sử dụng chèn / cập nhật và giới hạn số hàng mà chúng cập nhật ở 500 mỗi lần chèn / cập nhật để tránh khóa bảng. Tôi ước tính rằng bảng rất quan trọng này đã bị khóa gần 2 tiếng rưỡi mỗi ngày, điều này khiến mọi thứ từ lưu chậm đến hết thời gian.
CubeRoot

3
Ngoài ra, một điểm phá vỡ thỏa thuận đối với nhiều người, đó là MERGE có rất nhiều lỗi chưa được khắc phục trong đó, chúng xuất hiện trong những điều kiện kỳ ​​lạ. Ví dụ: xem bài viết này của Aaron Bertrand. Microsoft đang từ chối sửa một số lỗi trong số đó, và nghi ngờ bí mật của tôi là MS đã không dùng toàn bộ dịch vụ MS Connect của họ chỉ để thử và khiến chúng tôi quên đi tất cả những lỗi đó trong tuyên bố MERGE mà họ không muốn sửa.
Reversed Engineer

14

Có thể ai đó sử dụng MS SQL Server 2005 hoặc thấp hơn sẽ thấy câu trả lời này hữu ích.


MERGE sẽ chỉ hoạt động cho SQL Server 2008 trở lên. Đối với phần còn lại, tôi đã tìm thấy một giải pháp khác sẽ cung cấp cho bạn khả năng tạo loại bảng ánh xạ.

Đây là cách giải quyết sẽ trông như thế nào cho SQL 2005:

DECLARE @ReportOption TABLE (ReportOptionID INT IDENTITY(1, 1), Field1 INT, Field2 INT)
DECLARE @Practice TABLE(PracticeID INT IDENTITY(1, 1), Field1 INT, Field2 INT)
DECLARE @PracticeReportOption TABLE(PracticeReportOptionID INT IDENTITY(1, 1), PracticeID INT, ReportOptionID INT, Field1 INT, Field2 INT)

INSERT INTO @Practice (Field1, Field2) VALUES (1, 1)
INSERT INTO @Practice (Field1, Field2) VALUES (2, 2)
INSERT INTO @Practice (Field1, Field2) VALUES (3, 3)
INSERT INTO @Practice (Field1, Field2) VALUES (4, 4)

INSERT INTO @ReportOption (field1, field2)
    OUTPUT INSERTED.ReportOptionID, INSERTED.Field1, INSERTED.Field2 INTO @PracticeReportOption (ReportOptionID, Field1, Field2)
    SELECT Field1, Field2 FROM @Practice ORDER BY PracticeID ASC;


WITH CTE AS ( SELECT PracticeID, ROW_NUMBER() OVER ( ORDER BY PracticeID ASC ) AS ROW FROM @Practice )
UPDATE M SET M.PracticeID = S.PracticeID 
    FROM @PracticeReportOption AS M
    JOIN CTE AS S ON S.ROW = M.PracticeReportOptionID

    SELECT * FROM @PracticeReportOption

Bí quyết chính là chúng ta điền vào bảng ánh xạ hai lần với dữ liệu có thứ tự từ bảng Nguồn và bảng đích. Để biết thêm chi tiết tại đây: Hợp nhất dữ liệu đã chèn bằng OUTPUT trong SQL Server 2005


1
Điều này sẽ không giải quyết được vấn đề của tôi. Tôi cần phải Outputtôi Insert's cho một đầu ra Tablebao gồm một Identitygiá trị từ mục tiêu Tablevới một phi Insertgiá trị -ed (PK) từ nguồn Table(btw, vì vậy tôi có thể sau đó (trong một loạt khác nhau) sử dụng rằng sản lượng Tableđể cư một Columntrong nguồn Tablevới Identitygiá trị). Nếu không có a Merge, tôi tưởng tượng tôi sẽ phải: a) bắt đầu Transaction, b) tiếp theo Identity, c) Chèn vào tạm thời Tablevới tính toán Identity, d) đặt Identity_Insert, e) Insertvào mục tiêu Tabletừ tạm thời Table, f) rõ ràng Identity_Insert.
Tom
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.