为行

2013-07-19 23 views
1
ID State Name  Department   City 
1 O  George  Sales    Phoenix 
1 N  George  Sales    Denver 
2 O  Michael  Order Process  San diego 
2 N  Michael  Marketing   San jose 

我的情况,我需要上表值转换为以下格式转换更新的列值的表。(考虑顶行是列名)为行

ID Column  OldValue  New Value 
1 Department Phoenix  Denver 
2 Department Order Process Marketing 
2 City  San diego  San jose 

即:我需要从旧记录和新记录中捕获表格的已更改列值,并将它们记录在不同的表格中。但问题是,我们有许多表格,列名称和列的数量对于每个表格都不同。

如果有人来解决方案,将不胜感激..!

预先感谢您。

+0

它是SqlServer吗? –

+0

您正在使用哪些DBMS? Postgres的?甲骨文? –

+0

第一行不应该在列下面说城市吗? – Hogan

回答

2

这是你想要的吗?

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 '[email protected]+' 
    WHERE State=''O'' 
), new AS 
(
    SELECT * 
    FROM '[email protected]+' 
    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'[email protected]+' AS 
(
    SELECT n.ID, '''[email protected]+''' AS [Column], o.['[email protected]+'] AS oldValue, n.['[email protected]+'] AS newValue 
    FROM new n 
    JOIN old o ON n.ID = o.ID AND n.['[email protected]+'] != o.['[email protected]+'] 
)' 
    IF LEN(@sqlSelect) > 0 SET @sqlSelect = @sqlSelect + ' UNION ALL ' 

    SET @sqlSelect = @sqlSelect + ' 
SELECT * FROM changed'[email protected] 

    FETCH curCols INTO @aCol 

    END 

    CLOSE curCols 
    DEALLOCATE curCols 

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


    PRINT @[email protected] 

    EXEC (@[email protected]) 

这在我的测试输出如下:

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 

取决于许多因素(表和索引等的大小),它实际上可能比使用枢轴或情况或分组更快。这真的取决于你的数据。如果这是一次性运行,我只是为了最简单的方式去寻找。

+0

嗨,感谢您的答复,这可能工作,如果我有预先定义的一组表需要转换。但在这里我的问题是我不能有任何声明提到的列名称,并且该表也可以是任何。如果我只传递表名,我的查询或进程应该标识行并将更改的列值放到新表中。 –

+0

你没有声明这是一个要求 - 鉴于这个新的要求,你必须使用动态SQL - 动态SQL往往是缓慢的,但将工作1关闭或临时查询。 – Hogan

+0

感谢Hogan的快速响应,我想只写动态sql,但在此之前,我想知道是否有其他更好的实现相同..谢谢你的解决方案..将尽力.. –

0

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

为了您的具体的例子,这里是做什么的说明:

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, 'Name' as col, Name as value 
     from t 
    ) union all 
     (select ID, OldNew, 'Department' as col, Department as value 
     from t 
    ) union all 
     (select ID, OldNew, 'City' as col, City as value 
     from t 
    ) 
    ) unpvt 
group by id, col 
having max(value) <> min(value) and max(value) is not null; 

这是用于说明目的。与使用union all相比,可以更有效地完成untivot,特别是在有很多扫描的情况下。这里是一个更有效的版本,虽然确切的语法取决于数据库:

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; 

这是更有效,因为它通常只扫描表一次,而不是每进行一次列在union all版本。

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