Какие столбцы можно использовать в предложении OUTPUT INTO? - PullRequest
15 голосов
/ 01 октября 2008

Я пытаюсь создать таблицу сопоставления, чтобы связать идентификаторы новых строк в таблице с теми, из которых они скопированы. Предложение 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 с выходом INTO)

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

Предложение OUTPUT INTO возвращает значения из таблицы обновляется (WorkOrder), а также из продукта Таблица. Таблица Product используется в предложение FROM для указания строк обновить.

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

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

Ответы [ 5 ]

21 голосов
/ 24 марта 2011

Вы можете сделать это с помощью 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 сохранит записи.

13 голосов
/ 01 октября 2008

Я убедился, что проблема в том, что вы можете использовать только 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.

6 голосов
/ 18 марта 2009

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

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

0 голосов
/ 16 ноября 2010

(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
0 голосов
/ 19 марта 2009

Я думаю, что нашел решение этой проблемы, к сожалению, это временная таблица, но, по крайней мере, это предотвратит создание ужасного курсора :) Вам нужно добавить дополнительный столбец в таблицу, из которой вы дублируете записи, и придать ей тип «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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...