Büyük miktarda veri için satırlar arasındaki farklılıkları ayrıntılı olarak sorgulayın


15

Her biri> 300 sütun içeren bir dizi büyük tablo var. Kullandığım uygulama, ikincil bir tabloda geçerli satırın bir kopyasını oluşturarak değiştirilen satırların "arşivlerini" oluşturur.

Önemsiz bir örnek düşünün:

CREATE TABLE dbo.bigtable
(
  UpdateDate datetime,
  PK varchar(12) PRIMARY KEY,
  col1 varchar(100),
  col2 int,
  col3 varchar(20),
  .
  .
  .
  colN datetime
);

Arşiv tablosu:

CREATE TABLE dbo.bigtable_archive
(
  UpdateDate datetime,
  PK varchar(12) NOT NULL,
  col1 varchar(100),
  col2 int,
  col3 varchar(20),
  .
  .
  .
  colN datetime
);

Herhangi bir güncelleme yürütülmeden önce dbo.bigtable, satırın bir kopyası oluşturulur ve dbo.bigtable_archiveardından dbo.bigtable.UpdateDategeçerli tarih ile güncellenir.

Bu nedenle UNIONiki tablonun bir araya getirilmesi ve gruplandırılması PK, sipariş edildiğinde bir zaman çizelgesi oluşturur UpdateDate.

Sıralanan UpdateDate, gruplandırılan, sıralar arasındaki farkları PKaşağıdaki biçimde detaylandıran bir rapor oluşturmak istiyorum :

PK,   UpdateDate,  ColumnName,  Old Value,   New Value

Old Valueve değerlerin kendileri için herhangi bir son işlem yapmam gerekmediğinden New Value, ilgili sütunlar VARCHAR(MAX)( dahil edilen sütunlar TEXTveya BYTEsütunlar yoktur ) olabilir.

Şu anda bunu, sorguları programlı olarak oluşturmaya başvurmadan, çok sayıda sütun için akılcı bir yol düşünemiyorum - bunu yapmak zorunda kalabilirim.

Birçok fikre açık, bu yüzden 2 gün sonra soruya bir ödül ekleyeceğim.

Yanıtlar:


15

Bu, özellikle 300'den fazla sütun ve kullanılamaması göz önüne alındığında güzel görünmeyecek LAG, ne de çok iyi performans göstermesi muhtemel değildir, ancak başlangıçta bir şey olarak, aşağıdaki yaklaşımı deneyeceğim:

  • UNION iki tablo.
  • Kombine setteki her PK için arşiv tablosundan önceki "enkarnasyonunu" alın (aşağıdaki uygulama OUTER APPLY+ TOP (1)yoksul bir adam olarak kullanır LAG).
  • Her veri sütununu varchar(max)çiftler halinde yayınlayın ve bunları çiftler halinde açın, yani geçerli ve önceki değer ( CROSS APPLY (VALUES ...)bu işlem için iyi çalışır).
  • Son olarak, sonuçları her çiftteki değerlerin birbirinden farklı olup olmadığına göre filtreleyin.

Transact-SQL yukarıda gördüğüm gibi:

WITH
  Combined AS
  (
    SELECT * FROM dbo.bigtable
    UNION ALL
    SELECT * FROM dbo.bigtable_archive
  ) AS derived,
  OldAndNew AS
  (
    SELECT
      this.*,
      OldCol1 = last.Col1,
      OldCol2 = last.Col2,
      ...
    FROM
      Combined AS this
      OUTER APPLY
      (
        SELECT TOP (1)
          *
        FROM
          dbo.bigtable_archive
        WHERE
          PK = this.PK
          AND UpdateDate < this.UpdateDate
        ORDER BY
          UpdateDate DESC
      ) AS last
  )
SELECT
  t.PK,
  t.UpdateDate,
  x.ColumnName,
  x.OldValue,
  x.NewValue
FROM
  OldAndNew AS t
  CROSS APPLY
  (
    VALUES
    ('Col1', CAST(t.OldCol1 AS varchar(max), CAST(t.Col1 AS varchar(max))),
    ('Col2', CAST(t.OldCol2 AS varchar(max), CAST(t.Col2 AS varchar(max))),
    ...
  ) AS x (ColumnName, OldValue, NewValue)
WHERE
  NOT EXISTS (SELECT x.OldValue INTERSECT x.NewValue)
ORDER BY
  t.PK,
  t.UpdateDate,
  x.ColumnName
;

13

Verileri geçici tabloya açarsanız

create table #T
(
  PK varchar(12) not null,
  UpdateDate datetime not null,
  ColumnName nvarchar(128) not null,
  Value varchar(max),
  Version int not null
);

Bir öz üzerinden birleştirmek ile yeni ve eski değerini bulmak için satır maç olabilir PK, ColumnNameve Version = Version + 1.

Çok hoş olmayan kısım, elbette, 300 sütununuzun iki temel tablodan geçici tabloya ayrılmasını sağlamaktır.

XML daha az garip yapmak için kurtarmaya.

Tabloda, açılmayacak gerçek sütunları bilmek zorunda kalmadan verileri XML ile özetlemek mümkündür. Sütun adları XML'deki öğe adları olarak geçerli olmalıdır, aksi takdirde başarısız olur.

Buradaki fikir, her satır için o satırın tüm değerlerine sahip bir XML oluşturmaktır.

select bt.PK,
       bt.UpdateDate,
       (select bt.* for xml path(''), elements xsinil, type) as X
from dbo.bigtable as bt;
<UpdateDate xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">2001-01-03T00:00:00</UpdateDate>
<PK xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">PK1</PK>
<col1 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">c1_1_3</col1>
<col2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">3</col2>
<col3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true" />
<colN xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">2001-01-03T00:00:00</colN>

elements xsinilile sütunlar için öğeler oluşturmak için orada NULL.

XML daha sonra nodes('*') her sütun için bir satır local-name(.)almak ve get öğesi adına kullanmak ve text()değeri almak için parçalanabilir .

  select C1.PK,
         C1.UpdateDate,
         T.X.value('local-name(.)', 'nvarchar(128)') as ColumnName,
         T.X.value('text()[1]', 'varchar(max)') as Value
  from C1
    cross apply C1.X.nodes('row/*') as T(X)

Aşağıda tam çözüm. VersionTers çevrildiğini unutmayın . 0 = Son sürüm.

create table #X
(
  PK varchar(12) not null,
  UpdateDate datetime not null,
  Version int not null,
  RowData xml not null
);

create table #T
(
  PK varchar(12) not null,
  UpdateDate datetime not null,
  ColumnName nvarchar(128) not null,
  Value varchar(max),
  Version int not null
);


insert into #X(PK, UpdateDate, Version, RowData)
select bt.PK,
       bt.UpdateDate,
       0,
       (select bt.* for xml path(''), elements xsinil, type)
from dbo.bigtable as bt
union all
select bt.PK,
       bt.UpdateDate,
       row_number() over(partition by bt.PK order by bt.UpdateDate desc),
       (select bt.* for xml path(''), elements xsinil, type)
from dbo.bigtable_archive as bt;

with C as 
(
  select X.PK,
         X.UpdateDate,
         X.Version,
         T.C.value('local-name(.)', 'nvarchar(128)') as ColumnName,
         T.C.value('text()[1]', 'varchar(max)') as Value
  from #X as X
    cross apply X.RowData.nodes('*') as T(C)
)
insert into #T (PK, UpdateDate, ColumnName, Value, Version)
select C.PK,
       C.UpdateDate,
       C.ColumnName,
       C.Value,
       C.Version
from C 
where C.ColumnName not in (N'PK', N'UpdateDate');

/*
option (querytraceon 8649);

The above query might need some trick to go parallel.
For the testdata I had on my machine exection time is 16 seconds vs 2 seconds
https://sqlkiwi.blogspot.com/2011/12/forcing-a-parallel-query-execution-plan.html
http://dataeducation.com/next-level-parallel-plan-forcing-an-alternative-to-8649/

*/

select New.PK,
       New.UpdateDate,
       New.ColumnName,
       Old.Value as OldValue,
       New.Value as NewValue
from #T as New
  left outer join #T as Old
    on Old.PK = New.PK and
       Old.ColumnName = New.ColumnName and
       Old.Version = New.Version + 1;

6

Sana başka bir yaklaşım öneririm.

Geçerli uygulamayı değiştiremeseniz de, veritabanı davranışını değiştirebilirsiniz.

Mümkünse, geçerli tablolara iki TRIGGER eklerdim.

Dbo.bigtable_archive üzerinde yeni bir kayıt INSTEAD OF INSERT yalnızca yeni kayıt mevcut değilse ekler.

CREATE TRIGGER dbo.IoI_BTA
ON dbo.bigtable_archive
INSTEAD OF INSERT
AS
BEGIN
    IF NOT EXISTs(SELECT 1 
                  FROM dbo.bigtable_archive bta
                  INNER JOIN inserted i
                  ON  bta.PK = i.PK
                  AND bta.UpdateDate = i.UpdateDate)
    BEGIN
        INSERT INTO dbo.bigtable_archive
        SELECT * FROM inserted;
    END
END

Ve bigtable üzerinde tam olarak aynı işi yapan ancak bigtable verilerini kullanan bir SONRAKİ EKLEME tetikleyicisi.

CREATE TRIGGER dbo.IoI_BT
ON dbo.bigtable
AFTER INSERT
AS
BEGIN
    IF NOT EXISTS(SELECT 1 
                  FROM dbo.bigtable_archive bta
                  INNER JOIN inserted i
                  ON  bta.PK = i.PK
                  AND bta.UpdateDate = i.UpdateDate)
    BEGIN
        INSERT INTO dbo.bigtable_archive
        SELECT * FROM inserted;
    END
END

Tamam, burada bu ilk değerlerle küçük bir örnek oluşturdum :

SELECT * FROM bigtable;
SELECT * FROM bigtable_archive;
Güncelleme Tarihi | PK | col1 | col2 | col3
: ------------------ | : - | : --- | ---: | : ---
02/01/2017 00:00:00 | ABC | C3 | 1 | C1-  

Güncelleme Tarihi | PK | col1 | col2 | col3
: ------------------ | : - | : --- | ---: | : ---
01/01/2017 00:00:00 | ABC | C1 | 1 | C1-  

Şimdi bigtable'dan beklemedeki tüm kayıtları bigtable_archive içine eklemelisiniz.

INSERT INTO bigtable_archive
SELECT *
FROM   bigtable
WHERE  UpdateDate >= '20170102';
SELECT * FROM bigtable_archive;
GO
Güncelleme Tarihi | PK | col1 | col2 | col3
: ------------------ | : - | : --- | ---: | : ---
01/01/2017 00:00:00 | ABC | C1 | 1 | C1-  
02/01/2017 00:00:00 | ABC | C3 | 1 | C1-  

Şimdi, uygulama bir dahaki sefere bigtable_archive tablosuna kayıt eklemeye çalıştığında, tetikleyiciler var olup olmadığını algılar ve eklemeden kaçınılır.

INSERT INTO dbo.bigtable_archive VALUES('20170102', 'ABC', 'C3', 1, 'C1');
GO
SELECT * FROM bigtable_archive;
GO
Güncelleme Tarihi | PK | col1 | col2 | col3
: ------------------ | : - | : --- | ---: | : ---
01/01/2017 00:00:00 | ABC | C1 | 1 | C1-  
02/01/2017 00:00:00 | ABC | C3 | 1 | C1-  

Açıkçası şimdi sadece arşiv tablosunu sorgulayarak değişikliklerin zaman çizelgesini alabilirsiniz. Ve uygulama asla bir tetikleyicinin sessizce yorganın altında işi yaptığını fark etmeyecektir.

dbfiddle burada


4

Çalışma önerisi, bazı örnek verilerle, @ rextester: bigtable unpivot


Operasyonun özü:

1 - Kullanım syscolumns ve xml için dinamik UNPIVOT operasyon için bizim sütun listeleri oluşturmak için; tüm değerler varchar (max) değerine dönüştürülür, w / NULL'lar 'NULL' dizesine dönüştürülür (bu, univivot atlama NULL değerleriyle ilgili sorunu giderir)

2 - Verileri # sütunlar temp tablosuna açmak için dinamik bir sorgu oluşturun

  • Neden (via CTE vs geçici tablo ile maddesinde)? büyük miktarda veri için potansiyel performans sorunu ve kullanılabilir bir indeks / karma şeması olmadan CTE'nin kendi kendine katılımı ile ilgili; geçici tablo, kendi kendine birleştirme üzerindeki performansı iyileştirmesi gereken bir dizin oluşturulmasına olanak tanır [bkz. yavaş CTE kendi kendine birleştirme ]
  • Veriler, PK / ColName + UpdateDate sırasında # sütunlara yazılır, böylece PK / Colname değerlerini bitişik satırlarda depolamamıza izin verir; bir kimlik sütunu ( rid ) birbirini takip eden bu satırlara rid = rid + 1

3 - İstenen çıktıyı oluşturmak için #temp tablosunda kendi kendine birleştirme gerçekleştirin

Yeniden test ediciden kesme-n-yapıştırma ...

Bazı örnek veriler ve # sütunlar tablosumuzu oluşturun:

CREATE TABLE dbo.bigtable
(UpdateDate datetime      not null
,PK         varchar(12)   not null
,col1       varchar(100)      null
,col2       int               null
,col3       varchar(20)       null
,col4       datetime          null
,col5       char(20)          null
,PRIMARY KEY (PK)
);

CREATE TABLE dbo.bigtable_archive
(UpdateDate datetime      not null
,PK         varchar(12)   not null
,col1       varchar(100)      null
,col2       int               null
,col3       varchar(20)       null
,col4       datetime          null
,col5       char(20)          null
,PRIMARY KEY (PK, UpdateDate)
);

insert into dbo.bigtable         values ('20170512', 'ABC', NULL, 6, 'C1', '20161223', 'closed')

insert into dbo.bigtable_archive values ('20170427', 'ABC', NULL, 6, 'C1', '20160820', 'open')
insert into dbo.bigtable_archive values ('20170315', 'ABC', NULL, 5, 'C1', '20160820', 'open')
insert into dbo.bigtable_archive values ('20170212', 'ABC', 'C1', 1, 'C1', '20160820', 'open')
insert into dbo.bigtable_archive values ('20170109', 'ABC', 'C1', 1, 'C1', '20160513', 'open')

insert into dbo.bigtable         values ('20170526', 'XYZ', 'sue', 23, 'C1', '20161223', 're-open')

insert into dbo.bigtable_archive values ('20170401', 'XYZ', 'max', 12, 'C1', '20160825', 'cancel')
insert into dbo.bigtable_archive values ('20170307', 'XYZ', 'bob', 12, 'C1', '20160825', 'cancel')
insert into dbo.bigtable_archive values ('20170223', 'XYZ', 'bob', 12, 'C1', '20160820', 'open')
insert into dbo.bigtable_archive values ('20170214', 'XYZ', 'bob', 12, 'C1', '20160513', 'open')
;

create table #columns
(rid        int           identity(1,1)
,PK         varchar(12)   not null
,UpdateDate datetime      not null
,ColName    varchar(128)  not null
,ColValue   varchar(max)      null
,PRIMARY KEY (rid, PK, UpdateDate, ColName)
);

Çözümün bağırsakları:

declare @columns_max varchar(max),
        @columns_raw varchar(max),
        @cmd         varchar(max)

select  @columns_max = stuff((select ',isnull(convert(varchar(max),'+name+'),''NULL'') as '+name
                from    syscolumns
                where   id   = object_id('dbo.bigtable')
                and     name not in ('PK','UpdateDate')
                order by name
                for xml path(''))
            ,1,1,''),
        @columns_raw = stuff((select ','+name
                from    syscolumns
                where   id   = object_id('dbo.bigtable')
                and     name not in ('PK','UpdateDate')
                order by name
                for xml path(''))
            ,1,1,'')


select @cmd = '
insert #columns (PK, UpdateDate, ColName, ColValue)
select PK,UpdateDate,ColName,ColValue
from
(select PK,UpdateDate,'+@columns_max+' from bigtable
 union all
 select PK,UpdateDate,'+@columns_max+' from bigtable_archive
) p
unpivot
  (ColValue for ColName in ('+@columns_raw+')
) as unpvt
order by PK, ColName, UpdateDate'

--select @cmd

execute(@cmd)

--select * from #columns order by rid
;

select  c2.PK, c2.UpdateDate, c2.ColName as ColumnName, c1.ColValue as 'Old Value', c2.ColValue as 'New Value'
from    #columns c1,
        #columns c2
where   c2.rid                       = c1.rid + 1
and     c2.PK                        = c1.PK
and     c2.ColName                   = c1.ColName
and     isnull(c2.ColValue,'xxx')   != isnull(c1.ColValue,'xxx')
order by c2.UpdateDate, c2.PK, c2.ColName
;

Ve sonuçlar:

resim açıklamasını buraya girin

Not: özür ... rextester çıktısını bir kod bloğuna kesmenin ve yapıştırmanın kolay bir yolunu bulamadı. Önerilere açığım.


Potansiyel konular / endişeler:

1 - Verilerin genel bir varchar'a (maks.) Dönüştürülmesi, veri hassasiyeti kaybına yol açabilir ve bu da bazı veri değişikliklerini kaçırdığımız anlamına gelebilir; 'varchar (max)' jenerasyonuna dönüştürüldüğünde / yayınlandığında hassasiyetlerini kaybeden (yani, dönüştürülen değerler aynı) aşağıdaki tarih ve şamandıra çiftlerini göz önünde bulundurun:

original value       varchar(max)
-------------------  -------------------
06/10/2017 10:27:15  Jun 10 2017 10:27AM
06/10/2017 10:27:18  Jun 10 2017 10:27AM

    234.23844444                 234.238
    234.23855555                 234.238

    29333488.888            2.93335e+007
    29333499.999            2.93335e+007

Veri hassasiyeti korunabilse de, biraz daha fazla kodlama gerektirir (örneğin, kaynak sütun veri tiplerine dayalı döküm); Şimdilik OP'nin tavsiyesi uyarınca genel varchar (max) ile çalışmayı seçtim (ve OP'nin veri hassasiyeti kaybı ile ilgili herhangi bir sorunla karşılaşmayacağımızı bilmek için verileri yeterince iyi bildiği varsayımı).

2 - gerçekten büyük veri setleri için, ister tempdb alanı, ister önbellek / bellek olsun, bazı sunucu kaynaklarını dışarı atma riskiyle karşı karşıyayız; birincil sorun, bir unpivot sırasında meydana gelen veri patlamasından kaynaklanır (örneğin, 1 satır ve 302 veriden 300 satıra ve PK ve UpdateDate sütunlarının 300 kopyası, 300 sütun adı da dahil olmak üzere 1200-1500 veriye gideriz)


1

Bu yaklaşım, değişiklikleri almak için bir sql oluşturmak için dinamik sorgu kullanır. SP bir tablo ve şema adı alır ve istediğiniz çıktıyı verir.

Varsayımlar PK ve UpdateDate sütunlarının tüm tablolarda mevcut olduğudur. Ve tüm arşiv tabloları originalTableName + "_archive" biçimindedir.

Not: Performans için kontrol etmedim.

Not: Bu dinamik sql kullandığından, güvenlik / sql enjeksiyonu hakkında uyarı eklemeliyim. SP'ye erişimi kısıtlayın ve sql enjeksiyonunu önlemek için başka doğrulamalar ekleyin.

    CREATE proc getTableChanges
    @schemaname  varchar(255),
    @tableName varchar(255)
    as

    declare @strg nvarchar(max), @colNameStrg nvarchar(max)='', @oldValueString nvarchar(max)='', @newValueString nvarchar(max)=''

    set @strg = '
    with cte as (

    SELECT  * , ROW_NUMBER() OVER(partition by PK ORDER BY UpdateDate) as RowNbr
    FROM    (

        SELECT  *
        FROM    [' + @schemaname + '].[' + @tableName + ']

        UNION

        SELECT  *
        FROM    [' + @schemaname + '].[' + @tableName + '_archive]

        ) a

    )
    '


    SET @strg = @strg + '

    SELECT  a.pk, a.updateDate, 
    CASE '

    DECLARE @colName varchar(255)
    DECLARE cur CURSOR FOR
        SELECT  COLUMN_NAME
        FROM    INFORMATION_SCHEMA.COLUMNS
        WHERE TABLE_SCHEMA = @schemaname
        AND TABLE_NAME = @tableName
        AND COLUMN_NAME NOT IN ('PK', 'Updatedate')

    OPEN cur
    FETCH NEXT FROM cur INTO @colName 

    WHILE @@FETCH_STATUS = 0
    BEGIN

        SET @colNameStrg  = @colNameStrg  + ' when a.' + @colName + ' <> b.' + @colName + ' then ''' + @colName + ''' '
        SET @oldValueString = @oldValueString + ' when a.' + @colName + ' <> b.' + @colName + ' then cast(a.' + @colName + ' as varchar(max))'
        SET @newValueString = @newValueString + ' when a.' + @colName + ' <> b.' + @colName + ' then cast(b.' + @colName + ' as varchar(max))'


    FETCH NEXT FROM cur INTO @colName 
    END

    CLOSE cur
    DEALLOCATE cur


    SET @colNameStrg = @colNameStrg  + '    END as ColumnChanges '
    SET @oldValueString = 'CASE ' + @oldValueString + ' END as OldValue'
    SET @newValueString = 'CASE ' + @newValueString + ' END as NewValue'

    SET @strg = @strg + @colNameStrg + ',' + @oldValueString + ',' + @newValueString

    SET @strg = @strg + '
        FROM    cte a join cte b on a.PK = b.PK and a.RowNbr + 1 = b.RowNbr 
        ORDER BY  a.pk, a.UpdateDate
    '

    print @strg

    execute sp_executesql @strg


    go

Örnek Çağrı:

exec getTableChanges 'dbo', 'bigTable'

Yanılmıyorsam, bu aynı satıra yapılan birden fazla değişikliği yakalamaz değil mi?
Mikael Eriksson

bu doğru .. aynı anda güncellenen birden fazla sütun yakalanmayacak. yalnızca değişiklik içeren ilk sütun yakalanır.
Dharmendar Kumar 'DK'

1

Örneğimde AdventureWorks2012`, Production.ProductCostHistory and Production.ProductListPriceHistory kullanıyorum. "Mükemmel bir geçmiş tablosu örneği olmayabilir" ama script arzu çıktısını ve doğru çıktıyı bir araya getirebilir ".

     DECLARE @sql NVARCHAR(MAX)
    ,@columns NVARCHAR(Max)
    ,@table VARCHAR(200) = 'ProductCostHistory'
    ,@Schema VARCHAR(200) = 'Production'
    ,@Archivecolumns NVARCHAR(Max)
    ,@ColForUnpivot NVARCHAR(Max)
    ,@ArchiveColForUnpivot NVARCHAR(Max)
    ,@PKCol VARCHAR(200) = 'ProductID'
    ,@UpdatedCol VARCHAR(200) = 'modifiedDate'
    ,@Histtable VARCHAR(200) = 'ProductListPriceHistory'
SELECT @columns = STUFF((
            SELECT ',CAST(p.' + QUOTENAME(column_name) + ' AS VARCHAR(MAX)) AS ' + QUOTENAME(column_name)
            FROM information_schema.columns
            WHERE table_name = @table
                AND column_name NOT IN (
                    @PKCol
                    ,@UpdatedCol
                    )
            ORDER BY ORDINAL_POSITION
            FOR XML PATH('')
            ), 1, 1, '')
    ,@Archivecolumns = STUFF((
            SELECT ',CAST(p1.' + QUOTENAME(column_name) + ' AS VARCHAR(MAX)) AS ' + QUOTENAME('A_' + column_name)
            FROM information_schema.columns
            WHERE table_name = @Histtable
                AND column_name NOT IN (
                    @PKCol
                    ,@UpdatedCol
                    )
            ORDER BY ORDINAL_POSITION
            FOR XML PATH('')
            ), 1, 1, '')
    ,@ColForUnpivot = STUFF((
            SELECT ',' + QUOTENAME(column_name)
            FROM information_schema.columns
            WHERE table_name = @table
                AND column_name NOT IN (
                    @PKCol
                    ,@UpdatedCol
                    )
            ORDER BY ORDINAL_POSITION
            FOR XML PATH('')
            ), 1, 1, '')
    ,@ArchiveColForUnpivot = STUFF((
            SELECT ',' + QUOTENAME('A_' + column_name)
            FROM information_schema.columns
            WHERE table_name = @Histtable
                AND column_name NOT IN (
                    @PKCol
                    ,@UpdatedCol
                    )
            ORDER BY ORDINAL_POSITION
            FOR XML PATH('')
            ), 1, 1, '')

--SELECT @columns   ,@Archivecolumns    ,@ColForUnpivot
SET @sql = N' 
    SELECT ' + @PKCol + ', ColumnName,
            OldValue,NewValue,' + @UpdatedCol + '
    FROM    (  
    SELECT p.' + @PKCol + '
        ,p.' + @UpdatedCol + '
        ,' + @columns + '
        ,' + @Archivecolumns + '
    FROM ' + @Schema + '.' + @table + ' p
    left JOIN ' + @Schema + '.' + @Histtable + ' p1 ON p.' + @PKCol + ' = p1.' + @PKCol + '

  ) t
    UNPIVOT (
        OldValue
        FOR ColumnName in (' + @ColForUnpivot + ')
    ) up

     UNPIVOT (
        NewValue
        FOR ColumnName1 in (' + @ArchiveColForUnpivot + ')
    ) up1

--print @sql
EXEC (@sql)

Burada iç Seçim sorgusunda p'yi Ana Tablo ve p1'i Geçmiş tablosu olarak düşünün.

Senaryomu anlamak için daha az sütun adı ile başka bir tablo adı alabilirsiniz.Herhangi bir Açıklama sonra bana ping gerekir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.