Sql 将更新的列值作为行转换为表

Sql 将更新的列值作为行转换为表,sql,Sql,我遇到了一种情况,我需要将上面的表值转换为以下格式。(考虑最上面的一行是列名) 例如:我需要从旧记录和新记录中获取表的更改列值,并将它们记录在不同的表中。但问题是,我们有许多这样的表,每个表的列名和列数都不同 如果有人提出解决方案,我们将不胜感激 提前感谢您。最干净的方法可能是取消数据绑定,然后使用聚合。这确实需要对每个表进行自定义编码,您可以通过使用某种形式的动态SQL对其进行泛化 对于您的特定示例,以下是如何操作的示例: ID Column OldValue New

我遇到了一种情况,我需要将上面的表值转换为以下格式。(考虑最上面的一行是列名)

例如:我需要从旧记录和新记录中获取表的更改列值,并将它们记录在不同的表中。但问题是,我们有许多这样的表,每个表的列名和列数都不同

如果有人提出解决方案,我们将不胜感激


提前感谢您。

最干净的方法可能是取消数据绑定,然后使用聚合。这确实需要对每个表进行自定义编码,您可以通过使用某种形式的动态SQL对其进行泛化

对于您的特定示例,以下是如何操作的示例:

ID  Column      OldValue       New Value
1   Department  Phoenix        Denver
2   Department  Order Process  Marketing
2   City        San diego      San jose
这更有效,因为它通常只扫描表一次,而不是像在
union-all
版本中那样为每列扫描一次

在这两个版本中,都有一个隐式假设,即所有列都具有相同的字符类型。这在您要转换的格式中是隐含的,其中所有值都在一列中。

这是您想要的吗

select id, col,
       max(case when OldNew = 'Old' then value end) as OldValue,
       max(case when OldNew = 'New' then value end) as NewValue
from (select ID, OldNew, cols.col,
             (case when cols.col = 'Name' then Name
                   when cols.col = 'Department' then Department
                   when cols.col = 'City' then City
              end) as value
      from t cross join
           (select 'Name' as col union all select 'Department' union all select 'City') cols
     ) unpvt
group by id, col
having max(value) <> min(value) and max(value) is not null;
以下是动态代码:

 ID  Column      OldValue      New Value
 1   City        Phoenix       Denver
 2   Department  Order Process Marketing
 2   City        San Diego     San jose
在我的测试输出中,有以下内容:

DECLARE @sqlStm varchar(max);
DECLARE @sqlSelect varchar(max);

DECLARE @tablename varchar(200);
SET @tablename = 'testtable';

-- Assume table has ID column and State column.
SET @sqlSelect = ''
SET @sqlStm = 'WITH old AS
(
   SELECT *
   FROM '+@tablename+'
   WHERE State=''O''
), new AS
(
   SELECT *
   FROM '+@tablename+'
   WHERE State=''N''
)';

  DECLARE @aCol varchar(128)
  DECLARE curCols CURSOR  FOR
    SELECT column_name
    FROM information_schema.columns
    WHERE table_name = @tablename
      AND UPPER(column_name) NOT IN ('ID','STATE')

  OPEN curCols
  FETCH curCols INTO @aCol

  WHILE (@@FETCH_STATUS = 0)
  BEGIN
    SET @sqlStm = @sqlStm +
      ', changed'+@aCol+' AS
(
   SELECT n.ID, '''+@aCol+''' AS [Column], o.['+@aCol+'] AS oldValue, n.['+@aCol+'] AS newValue
   FROM new n
   JOIN old o ON n.ID = o.ID AND n.['+@aCol+'] != o.['+@aCol+']
)'
    IF LEN(@sqlSelect) > 0 SET @sqlSelect = @sqlSelect + ' UNION ALL '

    SET @sqlSelect = @sqlSelect + '
SELECT * FROM changed'+@aCol

    FETCH curCols INTO @aCol

  END

  CLOSE curCols
  DEALLOCATE curCols

    SET @sqlSelect = @sqlSelect + '
ORDER BY id, [Column]'


  PRINT @sqlStm+@sqlSelect

  EXEC (@sqlStm+@sqlSelect)

原始答复如下:

我会这样做,因为我认为这比其他可能更快的方法更清晰:

WITH old AS
(
   SELECT *
   FROM testtable
   WHERE State='O'
), new AS
(
   SELECT *
   FROM testtable
   WHERE State='N'
), changedName AS
(
   SELECT n.ID, 'Name' AS [Column], o.[Name] AS oldValue, n.[Name] AS newValue
   FROM new n
   JOIN old o ON n.ID = o.ID AND n.[Name] != o.[Name]
), changedDepartment AS
(
   SELECT n.ID, 'Department' AS [Column], o.[Department] AS oldValue, n.[Department] AS newValue
   FROM new n
   JOIN old o ON n.ID = o.ID AND n.[Department] != o.[Department]
), changedCity AS
(
   SELECT n.ID, 'City' AS [Column], o.[City] AS oldValue, n.[City] AS newValue
   FROM new n
   JOIN old o ON n.ID = o.ID AND n.[City] != o.[City]
)
SELECT * FROM changedName UNION ALL 
SELECT * FROM changedDepartment UNION ALL 
SELECT * FROM changedCity
ORDER BY id, [Column]

根据许多情况(表和索引的大小等),它实际上可能比使用数据透视、案例或分组更快。这取决于你的数据。如果这是一次性运行,我会选择最简单的方法。

您使用的是哪种DBMS?博士后?甲骨文?第一行不是应该在列下写“城市”吗?加斯顿和一匹名叫“否”的马在Sql server中是的。
霍根:是的,应该是城市。感谢您的发现。:)您好,谢谢您的回复,如果我有需要转换的预定义表集,这可能会起作用。但我的问题是,我不能在任何语句中提到列名,表也可以是任何内容。如果我只是传递表名,我的查询或进程应该识别行并将更改的列值放入新表。您没有将此作为一项要求进行说明——鉴于此新要求,您必须使用动态sql——动态sql可能会很慢,但适用于一次性或临时查询。谢谢Hogan为了快速响应,我想只编写动态sql,但在此之前,我想知道是否还有其他更好的方法可以实现相同的功能..非常感谢您提供的解决方案..将尝试..@Muthu-这是您的动态查询。。。喜欢。非常感谢你,它真的帮助了我们。再次感谢你创建了动态声明。
DECLARE @sqlStm varchar(max);
DECLARE @sqlSelect varchar(max);

DECLARE @tablename varchar(200);
SET @tablename = 'testtable';

-- Assume table has ID column and State column.
SET @sqlSelect = ''
SET @sqlStm = 'WITH old AS
(
   SELECT *
   FROM '+@tablename+'
   WHERE State=''O''
), new AS
(
   SELECT *
   FROM '+@tablename+'
   WHERE State=''N''
)';

  DECLARE @aCol varchar(128)
  DECLARE curCols CURSOR  FOR
    SELECT column_name
    FROM information_schema.columns
    WHERE table_name = @tablename
      AND UPPER(column_name) NOT IN ('ID','STATE')

  OPEN curCols
  FETCH curCols INTO @aCol

  WHILE (@@FETCH_STATUS = 0)
  BEGIN
    SET @sqlStm = @sqlStm +
      ', changed'+@aCol+' AS
(
   SELECT n.ID, '''+@aCol+''' AS [Column], o.['+@aCol+'] AS oldValue, n.['+@aCol+'] AS newValue
   FROM new n
   JOIN old o ON n.ID = o.ID AND n.['+@aCol+'] != o.['+@aCol+']
)'
    IF LEN(@sqlSelect) > 0 SET @sqlSelect = @sqlSelect + ' UNION ALL '

    SET @sqlSelect = @sqlSelect + '
SELECT * FROM changed'+@aCol

    FETCH curCols INTO @aCol

  END

  CLOSE curCols
  DEALLOCATE curCols

    SET @sqlSelect = @sqlSelect + '
ORDER BY id, [Column]'


  PRINT @sqlStm+@sqlSelect

  EXEC (@sqlStm+@sqlSelect)
WITH old AS
(
   SELECT *
   FROM testtable
   WHERE State='O'
), new AS
(
   SELECT *
   FROM testtable
   WHERE State='N'
), changedName AS
(
   SELECT n.ID, 'Name' AS [Column], o.[Name] AS oldValue, n.[Name] AS newValue
   FROM new n
   JOIN old o ON n.ID = o.ID AND n.[Name] != o.[Name]
), changedDepartment AS
(
   SELECT n.ID, 'Department' AS [Column], o.[Department] AS oldValue, n.[Department] AS newValue
   FROM new n
   JOIN old o ON n.ID = o.ID AND n.[Department] != o.[Department]
), changedCity AS
(
   SELECT n.ID, 'City' AS [Column], o.[City] AS oldValue, n.[City] AS newValue
   FROM new n
   JOIN old o ON n.ID = o.ID AND n.[City] != o.[City]
)
SELECT * FROM changedName UNION ALL 
SELECT * FROM changedDepartment UNION ALL 
SELECT * FROM changedCity
ORDER BY id, [Column]
with old as
(
   Select ID, Name,Department,City
   From table1
   Where State='O'
), new as
(
   Select ID, Name,Department,City
   From table1
   Where State='N'
), oldDepartment as
(
   Select ID, 'Department' as Column, o.Department as oldValue, n.Department as newValue
   From new
   join old on new.ID = old.ID and new.Department != old.Department
), oldCity as
(
   Select ID, 'City' as Column, o.City as oldValue, n.City as newValue
   From new
   join old on new.ID = old.ID and new.City != old.City
)
select * from oldDepartment
union all
select * from oldCity