【问题标题】:Matrix-like comparison of two query results两个查询结果的类矩阵比较
【发布时间】:2017-02-21 10:26:12
【问题描述】:

有没有办法将一个查询的整个结果集与另一个查询的结果集进行比较?

为了更清楚一点:我已经实现了一个流程来执行几个数据比较,例如“比较表 X 中的记录数与表 Y 中的记录数”,“比较一个表与预期的平均金额 X”、“将一年中每一天的表 Y 中的列 X 的值与年平均金额进行比较”等等。

现在,我需要实现以下内容:在给定的时间跨度内对表执行多列查询。然后对表执行相同的查询(相同的结构)第二个时间跨度。然后比较两个查询结果的每一行的每个单元格,并仅输出有差异的行(理想情况下带有一个标志 WHICH 单元格已更改)。

而且 - 好像还不够复杂 - 查询不是静态的!意味着我为各种检查定义了具有不同数量列的大量查询。然后将这些查询参数化(时间跨度等),然后比较定义的“查询对”的结果......

关于如何实现它的任何想法?作为第一步,我已经很高兴知道如何实现这个表/矩阵比较......

【问题讨论】:

  • 阅读except
  • @ZoharPeled 很好的提示,但EXCEPT 将返回所有行,只要有差异。有很多列,很难找到负责任的值...

标签: sql-server ssis sql-server-2012 sql-server-2014


【解决方案1】:

在一般分析结果集时,SQL-Server 不是很有帮助。有动态 SQL,还有 - tatatataaaaa! - XML。我真的很欣赏 XML 处理未知集合的能力!

以下代码会将结果集分解为 key-name-value 元组,您可以轻松地比较 value by value

两个虚拟表。数据非常相似,但存在差异:

DECLARE @tbl1 TABLE(ID INT,Value1 VARCHAR(10),Value2 VARCHAR,PointInTime DATETIME);
INSERT INTO @tbl1 VALUES
 (1,'a','b',{d'2017-01-01'})
,(2,'a','b',{d'2017-01-02'})
,(3,'a','x',{d'2017-01-03'})  
,(4,NULL,'b',{d'2017-01-04'})

DECLARE @tbl2 TABLE(ID INT,Value1 VARCHAR(10),Value2 VARCHAR,PointInTime DATETIME);
INSERT INTO @tbl2 VALUES
 (1,'a','b',{d'2017-01-01'})
,(2,NULL,'b',{d'2017-01-02'})
,(3,'a','x',{d'2017-01-03'})  
,(4,'y','b',{d'2017-01-05'});

--这将从第一个表创建一个 XML

DECLARE @xml1 XML=
(
    SELECT * 
    FROM @tbl1 AS tbl 
    FOR XML RAW,ELEMENTS XSINIL,TYPE
);

--检查输出

/*
SELECT @xml1;
<row xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <ID>1</ID>
  <Value1>a</Value1>
  <Value2>b</Value2>
  <PointInTime>2017-01-01T00:00:00</PointInTime>
</row>
[...more rows..]
*/

--与第二张表相同

DECLARE @xml2 XML=
(
    SELECT * 
    FROM @tbl2 AS tbl 
    FOR XML RAW,ELEMENTS XSINIL,TYPE
);

--我们必须告诉 SQL-Server 后面加入的行 ID 的名称

DECLARE @rowIDName NVARCHAR(100)=N'ID';

--两个 CTE 创建一个 ID-Name-Value 元组列表,可以轻松进行比较

WITH AllVals1 AS
(
    SELECT nd.value(N'(../*[local-name()=sql:variable("@rowIDName")])[1]',N'nvarchar(max)') AS RowID
          ,nd.value(N'local-name(.)',N'nvarchar(max)') AS ElementName
          ,nd.value(N'.',N'nvarchar(max)') AS ElementValue
    FROM @xml1.nodes(N'/row/*') AS A(nd)
)
,AllVals2 AS
(
    SELECT nd.value(N'(../*[local-name()=sql:variable("@rowIDName")])[1]',N'nvarchar(max)') AS RowID
          ,nd.value(N'local-name(.)',N'nvarchar(max)') AS ElementName
          ,nd.value(N'.',N'nvarchar(max)') AS ElementValue
    FROM @xml2.nodes(N'/row/*') AS A(nd)
)
SELECT v1.RowID,v1.ElementName,v1.ElementValue AS V1,v2.ElementValue AS V2
FROM AllVals1 AS v1
FULL OUTER JOIN AllVals2  AS v2 ON v1.RowID=v2.RowID AND v1.ElementName=v2.ElementName
WHERE v1.ElementValue<>v2.ElementValue

结果显示,在第 2 行中,“Value1”为“a”,现在为空;在第 4 行,“Value1”为空,现在为“y”,并且“PointInTime”不同:

RowID   ElementName V1                  V2
2       Value1      a   
4       Value1                          y
4       PointInTime 2017-01-04T00:00:00 2017-01-05T00:00:00

【讨论】:

  • 太好了,我试试看!看起来很有希望。 :-) 在此先感谢 - 一旦我测试过,我会带着结果回来。 :-)
  • 看起来不错。至少它有助于解决这项任务的很大一部分。不幸的是,Microsoft APS / PDW 不支持 FOR XML,但这是另一回事。谢谢你的支持!如果您也偶然发现了一些 PDW/APS/“无 XML”方法,请告诉我。 :-)
  • 嘿,你用更大的表测试过吗?刚刚遇到了非常糟糕的性能,每张表有 10k 条记录。但是我已经实现了一个 SSIS C# 组件来处理这个问题。 :-)
猜你喜欢
  • 2022-12-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多