Чи можна для пункту SQL Output повернути стовпець, що не вставляється?


123

Я вніс деякі зміни в свою базу даних, і мені потрібно перемістити старі дані до нових таблиць. Для цього мені потрібно заповнити таблицю (ReportOptions), беручи дані з вихідної таблиці (Practice), і заповнити другу проміжну таблицю (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...)

Я зробив запит, щоб отримати всі потрібні мені дані від практики до практики ReportOptions, але у мене виникли проблеми з заповненням проміжної таблиці

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

Якби я міг посилатись на поле, яке не знаходиться в таблиці призначення в пункті OUTPUT, це було б чудово (я думаю, що не можу, але не знаю точно). Будь-які ідеї, як здійснити мою потребу?


1
Ви можете повернути будь-який із стовпців таблиці, у яку ви вставили рядок, у своєму OUTPUTпункті. Тож навіть якщо ви не вказали значення для вказаного стовпця у своєму INSERTвиписці, ви все одно можете вказати цей стовпець у OUTPUTпункті. Однак ви не можете повертати змінні SQL або стовпці з інших таблиць.
marc_s

2
@marc_s дякую за вашу відповідь, але у мене немає потрібного поля в таблиці призначення (мені потрібен PracticeId, який не є у ReportOption)
Алехандро Б.

Відповіді:


191

Це можна зробити, використовуючи MERGEзамість вставки:

тому замініть це

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

з

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

Ключовим моментом є використання предиката, який ніколи не буде істинним (1 = 0) в умовах пошуку злиття, тому ви завжди будете виконувати вставку, але матимете доступ до полів як у вихідному, так і в цільовому таблицях.


Ось весь код, який я використовував для його тестування:

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 

Більше читання, і джерело всього, що я знаю з цього питання, тут


2
Спасибі, це робить трюк! Я збирався використовувати підроблене темп-поле, але це набагато елегантніше.
Алехандро Б.

1
Відмінно! Цей трюк - це зерно золота! Додано до першого ряду в колекції!
Вадим Лобода

1
Солодкий! Я б хотів, щоб не довелося використовувати періодично баггі команду MERGE, але в іншому випадку вона ідеально елегантна.
Tab Alleman

4
Будьте попереджені. Я використав заяву про злиття, яка за останній рік зросла із використанням. Ми почали мати тайм-аути під час збереження, і виявилося, що оскільки оператор злиття завжди блокує таблиці, у нас було 35-160 секунд блокування таблиці кожні 4 хвилини. Мені доводиться реконструювати кілька операторів злиття, щоб використовувати вставку / оновлення та обмежувати кількість рядків, які вони оновлюють, до 500 за вставку / оновлення, щоб уникнути блокування таблиці. Я вважаю, що ця дуже важлива таблиця була зафіксована майже 2 1/2 години на день, що спричиняло все - від повільного економії до таймаутів.
CubeRoot

3
Крім того, угода для багатьох угод полягає в тому, що в MERGE є ціла маса нефіксованих помилок, які повертаються в дивних умовах. Наприклад, дивіться цю статтю Аарона Бертран. Microsoft відмовляється виправляти деякі з них, і моє таємне підозру полягає в тому, що MS знецінила всю свою послугу MS Connect лише для того, щоб спробувати забути про всі ті помилки у заяві MERGE, які вони не хочуть виправляти.
інженер з

14

Можливо, хтось, хто використовує MS SQL Server 2005 або новішої версії, вважає цю відповідь корисною.


MERGE працюватиме лише для SQL Server 2008 або новішої версії. Для відпочинку я знайшов ще одне вирішення, яке дасть вам змогу створювати різновидні карти відображення.

Ось як буде виглядати роздільна здатність для 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

Основна хитрість полягає в тому, що ми двічі заповнюємо таблицю відображення впорядкованими даними з таблиці джерел та пунктів призначення. Детальніше тут: Об'єднання вставлених даних за допомогою OUTPUT в SQL Server 2005


1
Це не вирішило мою проблему. Мені потрібно до Outputмого Insertвиходу, Tableякий включає Identityзначення від цілі Tableз не- Insertзначенням (PK) від джерела Table(btw, тому я міг би потім (в іншій партії) використовувати цей вихід Tableдля заповнення Columnв джерело Tableзі Identityзначенням). Без a Merge, я думаю, що мені доведеться: a) почати Transaction, b) отримати далі Identity, в) вставити в темп Tableіз обчисленими Identity, d) встановити Identity_Insert, e) Insertв ціль Tableз temp Table, f) очистити Identity_Insert.
Том
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.