AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 175000
Accepted
Philᵀᴹ
Philᵀᴹ
Asked: 2017-06-01 00:39:13 +0800 CST2017-06-01 00:39:13 +0800 CST 2017-06-01 00:39:13 +0800 CST

Consulta detalhando diferenças entre linhas para uma grande quantidade de dados

  • 772

Eu tenho várias tabelas grandes, cada uma com mais de 300 colunas. O aplicativo que estou usando cria "arquivos" de linhas alteradas fazendo uma cópia da linha atual em uma tabela secundária.

Considere um exemplo trivial:

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

Tabela de arquivo:

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

Antes de qualquer atualização ser executada em dbo.bigtable, uma cópia da linha é criada em dbo.bigtable_archive, e dbo.bigtable.UpdateDateé atualizada com a data atual.

Portanto UNION, juntar as duas tabelas e agrupar por PKcria uma linha do tempo de alterações, quando ordenadas por UpdateDate.

Desejo criar um relatório detalhando as diferenças entre as linhas, ordenadas por UpdateDate, agrupadas por PK, no seguinte formato:

PK,   UpdateDate,  ColumnName,  Old Value,   New Value

Old Valuee New Valuepodem ser as colunas relevantes convertidas para a VARCHAR(MAX)(não há TEXTou BYTEcolunas envolvidas), pois não preciso fazer nenhum pós-processamento dos próprios valores.

No momento, não consigo pensar em uma maneira sensata de fazer isso para uma grande quantidade de colunas, sem recorrer a gerar as consultas programaticamente - talvez tenha que fazer isso.

Aberto a muitas ideias, então adicionarei uma recompensa à pergunta após 2 dias.

sql-server sql-server-2008-r2
  • 6 6 respostas
  • 658 Views

6 respostas

  • Voted
  1. Best Answer
    Andriy M
    2017-06-01T04:25:16+08:002017-06-01T04:25:16+08:00

    Isso não vai ficar bonito, especialmente considerando as mais de 300 colunas e a indisponibilidade de LAG, nem é provável que tenha um desempenho muito bom, mas apenas como algo para começar, eu tentaria a seguinte abordagem:

    • UNIONas duas tabelas.
    • Para cada PK no conjunto combinado, obtenha sua "encarnação" anterior da tabela de arquivos (a implementação abaixo usa OUTER APPLY+ TOP (1)como um pobre homem LAG).
    • Converta cada coluna de dados varchar(max)e desvire-os em pares, ou seja, o valor atual e o anterior ( CROSS APPLY (VALUES ...)funciona bem para esta operação).
    • Por fim, filtre os resultados com base na diferença entre os valores em cada par.

    O Transact-SQL do acima como eu vejo:

    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
    ;
    
    • 15
  2. Mikael Eriksson
    2017-06-01T23:32:14+08:002017-06-01T23:32:14+08:00

    Se você não dinamizar os dados para uma tabela temporária

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

    Você pode combinar as linhas para encontrar o valor novo e antigo com uma autojunção em PK, ColumnNamee Version = Version + 1.

    A parte não tão bonita é, é claro, fazer o unpivot de suas 300 colunas na tabela temporária das duas tabelas base.

    XML para o resgate para tornar as coisas menos complicadas.

    É possível desdinamizar dados com XML sem precisar saber quais colunas reais existem na tabela que serão desdinamizadas. Os nomes das colunas devem ser válidos como nomes de elementos em XML ou falharão.

    A ideia é criar um XML para cada linha com todos os valores dessa linha.

    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 xsinilexiste para criar elementos para colunas com NULL.

    O XML pode então ser fragmentado usando nodes('*') para obter uma linha para cada coluna e usar local-name(.)para obter o nome do elemento e text()obter o valor.

      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)
    

    Solução completa abaixo. Observe que Versionestá invertido. 0 = última versão.

    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;
    
    • 13
  3. McNets
    2017-06-07T01:39:56+08:002017-06-07T01:39:56+08:00

    Sugiro outra abordagem.

    Embora você não possa alterar o aplicativo atual, pode ser que você possa alterar o comportamento do banco de dados.

    Se possível, adicionaria dois TRIGGERS nas tabelas atuais.

    Um INSTEAD OF INSERT em dbo.bigtable_archive que adiciona o novo registro somente se ele não existir no momento.

    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
    

    E um gatilho AFTER INSERT no bigtable que faz exatamente o mesmo trabalho, mas usando dados do bigtable.

    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
    

    Ok, eu configurei um pequeno exemplo aqui com esses valores iniciais:

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

    Agora você deve inserir em bigtable_archive todos os registros pendentes do bigtable.

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

    Agora, na próxima vez que o aplicativo tentar inserir um registro na tabela bigtable_archive, os gatilhos detectarão se ele existe e a inserção será evitada.

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

    Obviamente, agora você pode obter a linha do tempo das alterações consultando apenas a tabela de arquivos. E o aplicativo nunca perceberá que um gatilho está silenciosamente fazendo o trabalho nos bastidores.

    dbfiddle aqui

    • 6
  4. markp-fuso
    2017-06-11T07:39:56+08:002017-06-11T07:39:56+08:00

    Working proposal, w/ some sample data, can be found @ rextester: bigtable unpivot


    The gist of the operation:

    1 - Use syscolumns and for xml to dynamically generate our column lists for the unpivot operation; all values will be converted to varchar(max), w/ NULLs being converted to the string 'NULL' (this addresses issue with unpivot skipping NULL values)

    2 - Generate a dynamic query to unpivot data into the #columns temp table

    • Why a temp table vs CTE (via with clause)? concerned with potential performance issue for a large volume of data and a CTE self-join with no usable index/hashing scheme; a temp table allows for creation of an index which should improve performance on the self-join [ see slow CTE self join ]
    • Data is written to #columns in PK+ColName+UpdateDate order, allowing us to store PK/Colname values in adjacent rows; an identity column (rid) allows us to self-join these consecutive rows via rid = rid + 1

    3 - Perform a self join of the #temp table to generate the desired output

    Cutting-n-pasting from rextester ...

    Create some sample data and our #columns table:

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

    The guts of the solution:

    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
    ;
    

    And the results:

    enter image description here

    Note: apologies ... couldn't figure out an easy way to cut-n-paste the rextester output into a code block. I'm open to suggestions.


    Potential issues/concerns:

    1 - conversion of data to a generic varchar(max) can lead to loss of data precision which in turn can mean we miss some data changes; consider the following datetime and float pairs which, when converted/cast to the generic 'varchar(max)', lose their precision (ie, the converted values are the same):

    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
    

    While data precision could be maintained it would require a bit more coding (eg, casting based on source column datatypes); for now I've opted to stick with the generic varchar(max) per the OP's recommendation (and assumption that the OP knows the data well enough to know that we won't run into any issues of data precision loss).

    2 - for really large sets of data we run the risk of blowing out some server resources, whether it be tempdb space and/or cache/memory; primary issue comes from the data explosion that occurs during an unpivot (eg, we go from 1 row and 302 pieces of data to 300 rows and 1200-1500 pieces of data, including 300 copies of the PK and UpdateDate columns, 300 column names)

    • 4
  5. Dharmendar Kumar 'DK'
    2017-06-07T09:02:17+08:002017-06-07T09:02:17+08:00

    Essa abordagem usa consulta dinâmica para gerar um sql para obter as alterações. O SP recebe um nome de tabela e esquema e fornece a saída desejada.

    As suposições são de que as colunas PK e UpdateDate estejam presentes em todas as tabelas. E todas as tabelas de arquivo têm o formato originalTableName + "_archive"..

    NB: Eu não verifiquei o desempenho.

    NB: como isso usa sql dinâmico, devo adicionar uma advertência sobre injeção de segurança/sql. Restrinja o acesso ao SP e adicione outras validações para evitar injeção de sql.

        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
    

    Chamada de amostra:

    exec getTableChanges 'dbo', 'bigTable'
    
    • 1
  6. KumarHarsh
    2017-06-09T03:23:57+08:002017-06-09T03:23:57+08:00

    I am using AdventureWorks2012`,Production.ProductCostHistory and Production.ProductListPriceHistory in my example.It may not be perfect history table example, "but script is able to put together the desire output and correct output".

         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)
    

    Here in inner Select query consider p as Main Table and p1 as History table.In unpivot it is important to convert it into same type.

    You can take any other table name with fewer column name to understand my script.Any Explanation need then ping me.

    • 1

relate perguntas

  • SQL Server - Como as páginas de dados são armazenadas ao usar um índice clusterizado

  • Preciso de índices separados para cada tipo de consulta ou um índice de várias colunas funcionará?

  • Quando devo usar uma restrição exclusiva em vez de um índice exclusivo?

  • Quais são as principais causas de deadlocks e podem ser evitadas?

  • Como determinar se um Índice é necessário ou necessário

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve