爲行

2013-07-19 58 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版本。

在任一版本中,都有一個隱含假設,即所有列都具有相同的字符類型。這隱含在您要轉換爲的格式中,其中所有值都位於單個列中。