查找MySQL表之间的差异,仅返回特定列已更改的行

查找MySQL表之间的差异,仅返回特定列已更改的行,mysql,union,diff,Mysql,Union,Diff,我需要区分两个MysQL表,并报告对结果子集的更改 假设我有两张桌子: 表A: id name supplier value ----------------------------------------- 1 Alice X 100 2 Bob Y 200 3 Clare Z 300 4

我需要区分两个MysQL表,并报告对结果子集的更改

假设我有两张桌子:

表A:

id      name        supplier        value
-----------------------------------------
1       Alice       X               100
2       Bob         Y               200
3       Clare       Z               300
4       Desmond     X               400
表B:

id      name        supplier        value
-----------------------------------------
1       Alice       X               150
2       Bob         X               200
3       Clare       Z               350
4       Desmond     X               400
5       Emily       X               500
我对涉及供应商X的任何行的更改感兴趣。鉴于上述情况,我想返回:

ID 1,因为供应商为X且值已更改; ID 2,因为供应商已从Y更改为X; ID 5,因为供应商是X,表A中没有对应的行。 我对ID 3不感兴趣,因为虽然值已更改,但更改不涉及供应商X。我也不感兴趣 在ID 4中,因为根本没有变化

我可以使用UNION ALL来计算差异:

SELECT *
FROM
 (
   SELECT a.id, a.name, a.supplier, a.value, 'a' as tbl
   FROM a
   UNION ALL
   SELECT b.id, b.name, b.supplier, b.value, 'b' as tbl
   FROM b
)  t
GROUP BY id, name, supplier, value
HAVING COUNT(*) = 1
ORDER BY id
这将返回数据已更改的所有行:

id      name        supplier        value       tbl
---------------------------------------------------
1       Alice       X               100         a
1       Alice       X               150         b
2       Bob         Y               200         a
2       Bob         X               200         b
3       Clare       Z               300         a
3       Clare       Z               350         b
5       Emily       X               500         b
但是,它还包括我不感兴趣的ID 3,因为表A或表B中的行都没有供应商X


最后,我的问题是-如果其中一个不同的行是supplier X,如何返回结果?当然,我可以在代码中过滤结果,但是在一个查询中过滤结果会很好。

我会使用两个左连接和一个并集来处理它:

首先,将表A连接到表B,然后进行反向连接

我不确定是否可以通过表的ID连接这些表,因此我在本例中使用了名称作为连接列

每个连接都包含一个WHERE子句,该子句使用cirteria:对涉及供应商X的任何行的更改来过滤行


这里有一个SQLFIDLE:

您可能会在原始查询中添加一些where子句来检查供应商X,但我认为我会采取稍微不同的方法并使用join:

SELECT a.id, a.name, a.supplier, a.value, b.name, b.supplier, b.value
FROM a
INNER JOIN b ON (a.id = b.id AND (a.name != b.name OR a.value != b.value OR a.supplier != b.supplier))
WHERE a.supplier = 'X' OR b.supplier = 'X'
GROUP BY a.id;

这将获取已更改但仅与X相关的行。请注意,这假定每个表中始终只有一个匹配id。

扩展lldar的答案,您还可以通过对列进行散列,然后查找更改来获得差异

md5(concat(A.`Name`,A.`Supplier`, A.`Value`)) <> md5(concat(b.`Name`,b.`Supplier`,b.`Value`))
如果您有许多列,这将非常有用。理想情况下,从长远来看,您可以编辑表并将哈希添加为计算列


那么它将是简单的A.hash b.hash

需求可以通过只使用左连接来实现

SELECT b.NAME AS NAME, 
       a.supplier AS a_supplier, 
       a.value AS a_value, 
       b.supplier AS b_supplier, 
       b.value AS b_value
FROM   b 
       LEFT JOIN a 
              ON ( a.id = b.id ) 
WHERE  ( b.supplier = 'X' 
          OR a.supplier = 'X' ) 
       AND ( a.supplier != b.supplier 
              OR a.value != b.value 
              OR a.id IS NULL ) 
ORDER  BY b.id;

在这种情况下,您的'OR'子句将增加查询成本。这不是优化的解决方案嗯,是的,当两个表中都有ID时,这是有效的,但不幸的是,它无法捕获表A包含B中不存在的行的实例,反之亦然。这在与ID连接时非常有效,谢谢。我的示例相当简单,而真正的数据集大约有20列和25万行,但它每天运行一次,所以运行一段时间并不重要。@Graham很高兴它有帮助!是的,我考虑过这一点,认为存储一个计算出的散列将大大加快查询速度。但实际上,计算散列值所用的时间大约是30秒,我必须使用updatetblsethash=md5。。。而不是在插入时进行计算,这大大超过了在查询~2s与~3s中使用它所带来的惊人的边际效益。@Graham您不更新计算/生成的列!
SELECT b.NAME AS NAME, 
       a.supplier AS a_supplier, 
       a.value AS a_value, 
       b.supplier AS b_supplier, 
       b.value AS b_value
FROM   b 
       LEFT JOIN a 
              ON ( a.id = b.id ) 
WHERE  ( b.supplier = 'X' 
          OR a.supplier = 'X' ) 
       AND ( a.supplier != b.supplier 
              OR a.value != b.value 
              OR a.id IS NULL ) 
ORDER  BY b.id;