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 :(得分: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 '+@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
根据许多事情(表和索引的大小等),它实际上可能比使用枢轴或案例或分组更快。这真的取决于你的数据。如果这是一次性的运行,我会选择最简单的方法。
答案 1 :(得分: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
相比,可以更有效地完成unpivot,特别是在扫描很多时。这是一个更高效的版本,虽然确切的语法取决于数据库:
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
版本那样每列扫描一次。
在任一版本中,都隐含假设所有列都具有相同的字符类型。这在您要转换的格式中是隐含的,其中所有值都在一列中。