查找MySQL表之间的差异,仅返回特定列已更改的行
我需要区分两个MysQL表,并报告对结果子集的更改 假设我有两张桌子: 表A:查找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
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;