Než odpovím, dovolte mi nejprve říci, že si nemyslím, že je nejlepší protokolovat všechny tabulky do jedné tabulky. Pokud se vaše databáze rozroste, můžete skončit s vážným sporem v tabulce Log. Navíc všechna vaše data musí být změněna na varchar nebo sql_variant, aby mohla být vložena do stejného sloupce, což je nuceno zabírat více místa. Také si myslím, že protokolování každého aktualizovaného sloupce do samostatného řádku (přeskočení sloupců, které se neaktualizují) to udělá velmi těžko se ptáš. Víte, jak shromáždit všechna tato data, abyste skutečně získali složený a rozumný pohled na změny každého řádku, kdy a kým? Mít jednu tabulku protokolů na tabulku bude podle mého názoru mnohem jednodušší. Pak nebudete mít problémy, se kterými se pokoušíte, aby to fungovalo.
Věděli jste také o SQL Server 2008 Změnit sběr dat ? Použijte to místo toho, pokud používáte edici Enterprise nebo Developer SQL Server!
Kromě tohoto problému můžete dělat, co chcete, s logickým UNPIVOT (prováděním své vlastní verze). Nativní SQL 2005 UNPIVOT opravdu nemůžete použít, protože máte dva cílové sloupce, ne jeden. Zde je příklad pro SQL Server 2005 a vyšší pomocí CROSS APPLY k provedení UNPIVOT:
INSERT INTO dbo.LOG (Id_Table, Table_Key_Value, Id_Value, Old_Value, New_Value)
SELECT 12345, I.Id, X.Id_Value, X.Old_Value, X.New_Value
FROM
INSERTED I
INNER JOIN DELETED D ON I.ID = D.ID
CROSS APPLY (
SELECT 4556645, D.Name, I.Name
UNION ALL SELECT 544589, D.Surname, I.Surname
) X (Id_Value, Old_Value, New_Value)
WHERE
X.Old_Value <> X.New_Value
Zde je obecnější metoda pro SQL 2000 nebo jiné DBMS (měla by teoreticky fungovat v Oracle, MySQL atd. - pro Oracle přidejte FROM DUAL
na každý SELECT v odvozené tabulce):
INSERT INTO dbo.LOG (Id_Table, Table_Key_Value, Id_Value, Old_Value, New_Value)
SELECT *
FROM (
SELECT
12345,
I.Id,
X.Id_Value,
CASE X.Id_Value
WHEN 4556645 THEN D.Name
WHEN 544589 THEN D.Surname
END Old_Value,
CASE X.Id_Value
WHEN 4556645 THEN I.Name
WHEN 544589 THEN I.Surname
END New_Value
FROM
INSERTED I
INNER JOIN DELETED D ON I.ID = D.ID
CROSS JOIN (
SELECT 4556645
UNION ALL SELECT 544589
) X (Id_Value)
) Y
WHERE
Y.Old_Value <> Y.New_Value
SQL Server 2005 a vyšší mají nativní příkaz UNPIVOT, i když obecně, i když UNPIVOT bude fungovat, místo toho rád používám CROSS APPLY, protože je zde větší flexibilita při provádění toho, co chci. Konkrétně nativní příkaz UNPIVOT zde není funkční, protože UNPIVOT může cílit pouze na jeden cílový sloupec, ale potřebujete dva (Stará_hodnota, Nová_hodnota). Zřetězení dvou sloupců do jedné hodnoty (a pozdější oddělení) není dobré; vytvoření nesmyslné hodnoty korelátoru řádků do PIVOTu není dobré a nenapadá mě jiný způsob, jak to udělat, který by nebyl variací na tyto dva. Řešení CROSS APPLY pro vás bude skutečně nejlepší, aby přesně odpovídalo struktuře tabulky protokolů, kterou jste popsali.
Ve srovnání s mými dotazy zde nebude vaše metoda #1 fungovat tak dobře (v poměru asi {počet sloupců}:1 horší výkon). Vaše metoda č. 2 je dobrý nápad, ale stále není optimální, protože volání UDF má velkou režii a navíc pak musíte každý řádek procházet smyčkou (chvění).