Какие столбцы можно использовать в предложении OUTPUT INTO?

Я пытаюсь создать таблицу сопоставления, чтобы связать идентификаторы новых строк в таблице с теми, из которых они скопированы. Предложение OUTPUT INTO кажется идеальным для этого, но, похоже, оно не ведет себя в соответствии с документацией.

Мой код:

DECLARE @Missing TABLE (SrcContentID INT PRIMARY KEY )
INSERT INTO @Missing 
    ( SrcContentID ) 
SELECT cshadow.ContentID
    FROM Private.Content AS cshadow
    LEFT JOIN Private.Content AS cglobal ON cshadow.Tag = cglobal.Tag
    WHERE cglobal.ContentID IS NULL 

PRINT 'Adding new content headers'
DECLARE @Inserted TABLE (SrcContentID INT PRIMARY KEY, TgtContentID INT )
INSERT INTO Private.Content 
    ( Tag, Description, ContentDate, DateActivate, DateDeactivate, SortOrder, CreatedOn, IsDeleted, ContentClassCode, ContentGroupID, OrgUnitID ) 
    OUTPUT cglobal.ContentID, INSERTED.ContentID INTO @Inserted (SrcContentID, TgtContentID)
SELECT Tag, Description, ContentDate, DateActivate, DateDeactivate, SortOrder, CreatedOn, IsDeleted, ContentClassCode, ContentGroupID, NULL 
    FROM Private.Content AS cglobal
    INNER JOIN @Missing AS m ON cglobal.ContentID = m.SrcContentID

Результат в сообщении об ошибке:

Msg 207, Level 16, State 1, Line 34
Invalid column name 'SrcContentID'.

(строка 34 - это строка с ВЫВОДОМ В)

Эксперименты показывают, что в OUTPUT INTO можно выбрать только те строки, которые действительно присутствуют в цели INSERT. Но это противоречит документам в онлайн-книгах. В статье о Пункт OUTPUT есть пример E, который описывает подобное использование:

The OUTPUT INTO clause returns values from the table being updated (WorkOrder) and also from the Product table. The Product table is used in the FROM clause to specify the rows to update.

Кто-нибудь работал с этой функцией?

(Тем временем я переписал свой код, чтобы выполнять эту работу с помощью цикла курсора, но это некрасиво, и мне все еще любопытно)

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
16
0
10 305
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Ответ принят как подходящий

Я убедился, что проблема в том, что вы можете использовать только столбцы INSERTED. В документации, кажется, указано, что вы можете использовать from_table_name, но я не могу заставить его работать (многосоставный идентификатор «m.ContentID» не может быть привязан.):

TRUNCATE TABLE main

SELECT *
FROM incoming

SELECT *
FROM main

DECLARE @Missing TABLE (ContentID INT PRIMARY KEY)
INSERT INTO @Missing(ContentID) 
SELECT incoming.ContentID
FROM incoming
LEFT JOIN main
    ON main.ContentID = incoming.ContentID
WHERE main.ContentID IS NULL

SELECT *
FROM @Missing

DECLARE @Inserted TABLE (ContentID INT PRIMARY KEY, [Content] varchar(50))
INSERT INTO main(ContentID, [Content]) 
OUTPUT INSERTED.ContentID /* incoming doesn't work, m doesn't work */, INSERTED.[Content] INTO @Inserted (ContentID, [Content])
SELECT incoming.ContentID, incoming.[Content] 
FROM incoming
INNER JOIN @Missing AS m
    ON m.ContentID = incoming.ContentID

SELECT *
FROM @Inserted

SELECT *
FROM incoming

SELECT *
FROM main

Очевидно, префикс from_table_name разрешен только на DELETE или UPDATE (или MERGE в 2008 году) - я не уверен, почему:

  • from_table_name

Префикс столбца, определяющий таблицу, включенную в предложение FROM инструкции DELETE или UPDATE, которая используется для указания строк для обновления или удаления.

Если изменяемая таблица также указана в предложении FROM, любая ссылка на столбцы в этой таблице должна быть дополнена префиксом INSERTED или DELETED.

У меня ТОЧНО такая же проблема, как и у вас, я чувствую вашу боль ... Насколько мне удалось выяснить, нет возможности использовать префикс from_table_name с оператором INSERT. Я уверен, что для этого есть серьезная техническая причина, и мне бы хотелось знать, что именно.

Хорошо, нашел, вот сообщение на форуме о том, почему это не работает: Форумы MSDN

И это дает ключ к разгадке. Заявление MERGE! Затем @Simon удалось предоставить синтаксис. Хороший!

lambacck 03.06.2011 00:12

Я думаю, что нашел решение этой проблемы, это, к сожалению, включает временную таблицу, но, по крайней мере, это предотвратит создание страшного курсора :) Что вам нужно сделать, так это добавить дополнительный столбец в таблицу, из которой вы копируете записи, и присвоить ей тип «uniqueidentifer».

затем объявите временную таблицу:

DECLARE @tmptable TABLE (uniqueid uniqueidentifier, original_id int, new_id int)

вставьте данные в свою временную таблицу следующим образом:

insert into @tmptable
(uniqueid,original_id,new_id)
select NewId(),id,0 from OriginalTable

продолжайте и сделайте настоящую вставку в исходную таблицу:

insert into OriginalTable
(uniqueid)
select uniqueid from @tmptable

Теперь, чтобы добавить вновь созданные значения идентификаторов в вашу временную таблицу:

update @tmptable
set new_id = o.id
from OriginalTable o inner join @tmptable tmp on tmp.uniqueid = o.uniqueid

Теперь у вас есть таблица поиска, которая содержит новый идентификатор и исходный идентификатор в одной записи, для вашего удовольствия :)

Надеюсь, это кому-нибудь поможет ...

Спасибо, но это жизнеспособное решение, только если ключ, который мне нужно отслеживать, является уникальным идентификатором. Вся проблема, которую я пытаюсь решить, заключается в отслеживании значения IDENTITY, которое назначается новой строке.

Chris Wuestefeld 17.06.2009 00:59

(MS) Если изменяемая таблица также указана в предложении FROM, любая ссылка на столбцы в этой таблице должна быть дополнена префиксом INSERTED или DELETED.

В вашем примере вы не можете использовать таблицу cglobal в OUTPUT, если это не INSERTED.column_name или DELETED.column_name:

INSERT INTO Private.Content 
    (Tag) 
    OUTPUT cglobal.ContentID, INSERTED.ContentID 
    INTO @Inserted (SrcContentID, TgtContentID)
SELECT Tag
    FROM Private.Content AS cglobal
    INNER JOIN @Missing AS m ON cglobal.ContentID = m.SrcContentID

Для меня сработала простая таблица псевдонимов, например:

INSERT INTO con1 
    (Tag) 
    OUTPUT **con2**.ContentID, INSERTED.ContentID 
    INTO @Inserted (SrcContentID, TgtContentID)
SELECT Tag
    FROM Private.Content con1
    **INNER JOIN Private.Content con2 ON con1.id=con2.id**
    INNER JOIN @Missing AS m ON con1.ContentID = m.SrcContentID

Вы можете сделать это с помощью MERGE в Sql Server 2008. Пример кода ниже:

--drop table A
create table A (a int primary key identity(1, 1))
insert into A default values
insert into A default values

delete from A where a>=3

-- insert two values into A and get the new primary keys
MERGE a USING (SELECT a FROM A) AS B(a)
ON (1 = 0) -- ignore the values, NOT MATCHED will always be true
WHEN NOT MATCHED THEN INSERT DEFAULT VALUES -- always insert here for this example
OUTPUT $action, inserted.*, deleted.*, B.a; -- show the new primary key and source data

Результат

INSERT, 3, NULL, 1
INSERT, 4, NULL, 2

т.е. для каждой строки новый первичный ключ (3, 4) и старый (1, 2). Создание таблицы с именем, например, #OUTPUT и добавление "INTO #OUTPUT;" в конце предложения OUTPUT сохранит записи.

Заявление MERGE - мой новый лучший друг :)

lambacck 03.06.2011 00:13

Хорошее объяснение и пример здесь: sqlblog.com/blogs/jamie_thomson/archive/2010/01/06/…

Ronnie Overby 17.09.2012 20:45

Другие вопросы по теме