2008-09-30 12 views
15

Próbuję utworzyć tabelę odwzorowań, aby powiązać identyfikatory nowych wierszy w tabeli z tymi, z których są one kopiowane. Klauzula OUTPUT INTO wydaje się do tego idealna, ale wydaje się, że nie zachowuje się zgodnie z dokumentacją.Jakie kolumny można zastosować w klauzuli OUTPUT INTO?

Mój kod:

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 

Wyniki w komunikacie o błędzie:

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

(linia 34 będące jednym z wyjście do)

Eksperymentowanie sugeruje, że tylko wiersze, które są w rzeczywistości obecny w celu INSERT można wybrać w OUTPUT INTO. Ale to przeczy dokumentom w książkach online. Artykuł na OUTPUT klauzuli ma Przykład E opisujący podobną Zastosowanie:

wyjście do zwrotu klauzuli VALUES z tabeli aktualizowany (WorkOrder), a także z produktu tabeli. Tabela produktów jest używana w klauzuli FROM, aby określić wiersze aktualizacji do .

Czy ktoś pracował z tą funkcją?

(W międzyczasie mam przepisany mój kod, aby wykonać zadanie za pomocą pętli kursora, ale to brzydkie i wciąż jestem ciekaw)

Odpowiedz

13

I zostały zweryfikowane, że problemem jest to, że można używać tylko INSERTED kolumn. Dokumentacja wskazuje na to, że można użyć from_table_name, ale nie wydaje się uzyskać go do pracy (identyfikator wieloczęściowy „m.ContentID” nie mógł być związany.):

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 

Widocznie from_table_name przedrostek jest dozwolone wyłącznie na DELETE lub UPDATE (lub MERGE w 2008 roku) - nie jestem pewien, dlaczego:

  • from_table_name

Czy kol mn prefiks określający tabelę zawartą w klauzuli FROM instrukcji DELETE lub UPDATE, która jest używana do określenia wierszy do aktualizacji lub usunięcia.

Jeśli modyfikowana tabela jest również określona w klauzuli FROM, wszelkie odniesienia do kolumn w tej tabeli muszą być kwalifikowane przedrostkiem INSERTED lub DELETED.

6

Występuję DOKŁADNIE taki sam problem jak Ty, czuję twój ból ... O ile byłem w stanie się dowiedzieć, nie ma sposobu, aby użyć prefiksu from_table_name z instrukcją INSERT. Jestem pewien, że istnieje uzasadniony powód techniczny tego, i chciałbym wiedzieć dokładnie, co to jest.

Ok, znalazłem go, oto post na forum, dlaczego to nie działa: MSDN forums

+0

I zapewnia klucz do rozwiązania. Polecenie MERGE! Następnie @Simon udało się zapewnić składnię. Miły! – lambacck

0

Chyba znalazłem rozwiązanie tego problemu, to niestety wiąże się z tabeli tymczasowej, ale przynajmniej to będzie Zapobiegaj tworzeniu się przerażającego kursora :) Co musisz zrobić, to dodać dodatkową kolumnę do tabeli, z której duplikujesz rekordy i nadać jej typ "uniqueidentifer".

następnie zadeklarować tabeli tymczasowej:

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

wstawić te dane do tabeli temp tak:

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

zrób prawdziwy wkład w oryginalnej tabeli:

insert into OriginalTable 
(uniqueid) 
select uniqueid from @tmptable 

Teraz, aby dodać nowo utworzone wartości tożsamości do tabeli tymczasowej:

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

Teraz masz tablicę przeglądową, która posiada nowy identyfikator i oryginalny identyfikator w jednym rekordzie, dla przyjemności :) używając

mam nadzieję, że to pomoże ktoś ...

+1

Dzięki, ale jest to realnie możliwe rozwiązanie, jeśli klucz, który muszę śledzić, jest unikalnym identyfikatorem. Cały problem, który próbuję rozwiązać, to śledzenie wartości IDENTYFIKACJI, która jest przydzielana do nowego wiersza. –

0

(MS) Jeśli modyfikowana tabela jest również określona w klauzuli FROM, wszelkie odwołania do kolumn w tej tabeli muszą być kwalifikowane przedrostkiem INSERTED lub DELETED.

W przykładzie nie można użyć cglobal stolik na wyjściu, chyba że jest INSERTED.column_name lub 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 

Co pracował dla mnie była prosta tabela alias, tak:

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

Nie mogłem uzyskać takiego podejścia do pracy :-( –

21

można to zrobić za pomocą seryjnej w SQL Server 2008. Przykładowy kod poniżej:

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

R esult to

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

tj. dla każdego wiersza nowy klucz podstawowy (3, 4) i stary (1, 2). Tworzenie tabeli o nazwie np. #OUTPUT i dodanie "INTO # OUTUT"; na końcu klauzuli OUTPUT zapisywałby rekordy.

+1

Polecenie MERGE jest moim nowym najlepszym przyjacielem :) – lambacck

+5

Dobre wyjaśnienie i przykład tutaj: http://sqlblog.com/blogs/jamie_thomson/archive/2010/01/06/ scalaj-i-wysyłaj-swiss-army-knife-of-t-sql.aspx –

Powiązane problemy