如何比较两个结构相似的表,只返回SQL Server中不同值的列名和值?

时间:2014-02-10 19:16:28

标签: sql-server sql-server-2008

我正在使用SQL Server 2008(v10.0 SP3)作为我的数据库。

我试图找到如何比较两个几乎相同的表结构,只返回列名和不匹配的列的值。

我有两张桌子。

表A 有大约260列和每条记录的唯一标识符。它来自另一台服务器上的视图。

表B 是表A的结构副本,添加了插入日期列和操作列。

IF OBJECT_ID('tempdb..#TableA') IS NOT NULL
    DROP TABLE #TableA
IF OBJECT_ID('tempdb..#TableB') IS NOT NULL
    DROP TABLE #TableB

CREATE TABLE #TableA (
UniqueID INT,[Name] CHAR(3),[Address] CHAR(15),
HairColor CHAR(6),ImportDate DATETIME
)

CREATE TABLE #TableB (
UniqueID INT,[Name] CHAR(3),[Address] CHAR(15),
HairColor CHAR(6),ImportDate DATETIME,
AuditDate DATETIME,[Action] CHAR(10)
)

INSERT INTO #TableA
VALUES (1,'Joe','1 Main St.','Brown','12/1/2013')

INSERT INTO #TableA
VALUES (2,'Jen','1 Main St.','Red','12/1/2013')

INSERT INTO #TableB
VALUES (2,'Jen','1 Main St.','Blonde','10/1/2013','12/1/2013','CHANGE')

INSERT INTO #TableB
VALUES (2,'Jen','1 Baker St.','Blonde','4/1/2010','10/1/2013','CHANGE')

INSERT INTO #TableB
VALUES (2,'Jen','4 Deer Ave.','Black','6/1/2004','4/1/2010','CHANGE')

SELECT * FROM #TableA AS ta

SELECT * FROM #TableB AS tb

表A

╔══════════╦══════╦════════════╦═══════════╦════════════╗
║ UniqueID ║ Name ║  Address   ║ HairColor ║ ImportDate ║
╠══════════╬══════╬════════════╬═══════════╬════════════╣
║        1 ║ Joe  ║ 1 Main St. ║ Brown     ║ 12/1/2013  ║
║        2 ║ Jen  ║ 1 Main St. ║ Red       ║ 12/1/2013  ║
╚══════════╩══════╩════════════╩═══════════╩════════════╝

表B

╔══════════╦══════╦═════════════╦═══════════╦════════════╦═══════════╦════════╗
║ UniqueID ║ Name ║   Address   ║ HairColor ║ ImportDate ║ AuditDate ║ Action ║
╠══════════╬══════╬═════════════╬═══════════╬════════════╬═══════════╬════════╣
║        2 ║ Jen  ║ 1 Main St.  ║ Blonde    ║ 10/1/2013  ║ 12/1/2013 ║ CHANGE ║
║        2 ║ Jen  ║ 1 Baker St. ║ Blonde    ║ 4/1/2010   ║ 10/1/2013 ║ CHANGE ║
║        2 ║ Jen  ║ 4 Deer Ave. ║ Black     ║ 6/1/2004   ║ 4/1/2010  ║ CHANGE ║
╚══════════╩══════╩═════════════╩═══════════╩════════════╩═══════════╩════════╝

每月截断并重新加载表A,但在此之前,进行表A(新A)的新数据与表A(旧A)中已有的数据之间进行比较。

如果新A不包含旧AI中的行,请将旧A行插入表B ,并将insertdateDELETED作为操作。

如果新A包含不在旧AI中的行,则将新A行插入表B ,并将insertdateADDED作为操作。

如果新A包含任何列与旧AI不匹配的行,请将旧A行插入表B ,并将insertdateCHANGE添加为行动。

如您所见,表B可以有多个行,其唯一标识符与表A相同,因为表A中唯一记录的源数据可能会逐月更改。

现在添加和删除很容易报告。我遇到的困难是报告变化。如果有260个常见行,我希望能够选择表B中与表A中的相关列不匹配的列,左侧加入TableA.UniqueID = TableB.UniqueID

我正在考虑使用表A中的输入UniqueID创建一个表值函数,并输出如下内容:

╔══════════╦════════════╦═════════════╦════════════╦═══════════╦════════╗
║ UniqueID ║ ColumnName ║    Value    ║ ImportDate ║ AuditDate ║ Action ║
╠══════════╬════════════╬═════════════╬════════════╬═══════════╬════════╣
║        2 ║ HairColor  ║ Blonde      ║ 10/1/2013  ║ 12/1/2013 ║ CHANGE ║
║        2 ║ Address    ║ 1 Baker St. ║ 4/1/2010   ║ 10/1/2013 ║ CHANGE ║
║        2 ║ HairColor  ║ Blonde      ║ 4/1/2010   ║ 10/1/2013 ║ CHANGE ║
║        2 ║ Address    ║ 4 Deer Ave. ║ 6/1/2004   ║ 4/1/2010  ║ CHANGE ║
║        2 ║ HairColor  ║ Black       ║ 6/1/2004   ║ 4/1/2010  ║ CHANGE ║
╚══════════╩════════════╩═════════════╩════════════╩═══════════╩════════╝

我只是不确定该怎么做。

这是我的大脑停止工作的地方。可以在INFORMATION_SCHEMA.COLUMNS中使用ORDINAL_POSITION吗?

SELECT a.ORDINAL_POSITION,a.COLUMN_NAME 
FROM INFORMATION_SCHEMA.COLUMNS AS a 
JOIN INFORMATION_SCHEMA.COLUMNS AS b ON a.COLUMN_NAME = b.COLUMN_NAME
WHERE a.TABLE_NAME = 'TableA' AND a.TABLE_SCHEMA='dbo'
AND b.TABLE_NAME = 'TableB' AND b.TABLE_SCHEMA='dbo'

3 个答案:

答案 0 :(得分:0)

你可以为每一栏尝试这个......

SELECT a.UniqueID,'HairColour' [ColumnName],b.HairColour [Value],...,'CHANGE' [Action] FROM Tableb b
INNER JOIN Tablea a ON
   a.UniqueID = b.uniqueID
WHERE a.HairColor <> b.HairColor

UNION

    SELECT a.UniqueID,'Address' [ColumnName],b.Address [Value],...,'CHANGE' [Action] FROM Tableb b
INNER JOIN Tablea a ON
   a.UniqueID = b.uniqueID
WHERE a.Address <> b.Address

etc...

答案 1 :(得分:0)

如果您有Visual Studio Premium或Ultimate,则可以使用架构比较数据库项目来实现此目的。它非常复杂,甚至可以让您为源和/或目标的差异生成DDL。如果需要,您还可以比较数据。我过去在很多项目中都使用过它。

MSDN:Compare and Synchronize Database Schemas

答案 2 :(得分:0)

听起来像UNPIVOT就是你所需要的。来自MSDN:Using PIVOT and UNPIVOT

以下是您问题的可能解决方案:

SELECT UniqueID, ColumnName, Value, ImportDate, AuditDate, Action
FROM
(
    SELECT 
        TableA.UniqueID, 
        CAST((CASE WHEN ISNULL(TableB.Address, '') <> ISNULL(TableA.Address, '')
              THEN ISNULL(TableB.Address, '')
              ELSE NULL END) AS nvarchar(255))
        AS Address, 
        CAST((CASE WHEN ISNULL(TableB.HairColor, '') <> ISNULL(TableA.HairColor, '')
              THEN ISNULL(TableB.HairColor, '')
              ELSE NULL END) AS nvarchar(255))
        AS HairColor, 
        TableB.ImportDate,
        TableB.AuditDate,
        TableB.Action
    FROM TableA INNER JOIN TableB ON TableB.UniqueID = TableA.UniqueID
) AS p
UNPIVOT
(
    Value FOR ColumnName in (Address, HairColor)
) AS up
WHERE Value IS NOT NULL

请注意,我将值转换为nvarchar(255)以解决类型之间的冲突。您可能需要将其调整为更合适的东西。

另外,我一眼就认为生成的结果可能需要根据导入和审核日期进行一些改进。但这是另一个话题。另外,我最终使用的是INNER JOIN,因为LEFT JOIN在这种情况下对我没有意义。也许我错过了什么。

所以,所有这一切,这应该得到你正在寻找的结果。