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

时间:2018-08-17 09:19:51

标签: mysql union diff

我需要比较两个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。

所以最后,我的问题是-如何返回差异行之一是供应商X的结果?我当然可以在代码中过滤结果,但是在单个查询中执行此操作就很棒。

4 个答案:

答案 0 :(得分:1)

我会使用两个input[type="number"]:-webkit-outer-spin-button, input[type="number"]:-webkit-inner-spin-button和一个LEFT JOINS来实现它:

UNION

首先,将表CREATE TABLE `a` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `name` VARCHAR(50) NOT NULL DEFAULT '0', `supplier` VARCHAR(50) NOT NULL DEFAULT '0', `value` INT(11) NOT NULL DEFAULT '0', PRIMARY KEY (`id`) ) COLLATE='latin1_swedish_ci' ENGINE=InnoDB AUTO_INCREMENT=5 ; CREATE TABLE `b` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `name` VARCHAR(50) NOT NULL DEFAULT '0', `supplier` VARCHAR(50) NOT NULL DEFAULT '0', `value` INT(11) NOT NULL DEFAULT '0', PRIMARY KEY (`id`) ) COLLATE='latin1_swedish_ci' ENGINE=InnoDB AUTO_INCREMENT=6 ; INSERT INTO `a` (`id`, `name`, `supplier`, `value`) VALUES (1, 'Alice', 'X', 100); INSERT INTO `a` (`id`, `name`, `supplier`, `value`) VALUES (2, 'Bob', 'Y', 200); INSERT INTO `a` (`id`, `name`, `supplier`, `value`) VALUES (3, 'Clare', 'Z', 300); INSERT INTO `a` (`id`, `name`, `supplier`, `value`) VALUES (4, 'Desmond', 'X', 400); INSERT INTO `b` (`id`, `name`, `supplier`, `value`) VALUES (1, 'Alice', 'X', 150); INSERT INTO `b` (`id`, `name`, `supplier`, `value`) VALUES (2, 'Bob', 'X', 200); INSERT INTO `b` (`id`, `name`, `supplier`, `value`) VALUES (3, 'Clare', 'Z', 350); INSERT INTO `b` (`id`, `name`, `supplier`, `value`) VALUES (4, 'Desmond', 'X', 400); INSERT INTO `b` (`id`, `name`, `supplier`, `value`) VALUES (5, 'Emily', 'X', 500); SELECT a.name AS name, a.supplier AS a_supplier, a.value AS a_value, b.supplier AS b_supplier, b.value AS b_value FROM a LEFT JOIN b ON a.name = b.name WHERE (a.supplier ='X' OR b.supplier = 'X') AND (a.value <> b.value OR a.supplier <> b.supplier OR b.name IS NULL) UNION 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 b.name = a.name WHERE (a.supplier ='X' OR b.supplier = 'X') AND (a.value <> b.value OR a.supplier <> b.supplier OR a.name IS NULL) 连接到表A,然后进行反向连接。

我不确定是否可以通过表的ID联接表,因此在此示例中,我将名称用作联接列。

每个联接都包含一个B子句,该子句使用您的条件过滤行:“更改为涉及供应商X的任何行”

这里是一个SQLFiddle:http://sqlfiddle.com/#!9/46f213/1

答案 1 :(得分:0)

您可以在原始查询中添加一些where子句以检查Supplier X,但我认为我会采用略有不同的方法并使用联接:

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。

答案 2 :(得分:0)

扩展lldar的答案,您还可以通过对列进行散列然后寻找更改来获得区别。

md5(concat(A.`Name`,A.`Supplier`, A.`Value`)) <> md5(concat(b.`Name`,b.`Supplier`,b.`Value`))

如果您有很多列,这将很有帮助。从长远来看,理想情况下,您可以编辑表并将散列添加为“计算/计算”列。

那么它就是A.hash <> b.hash

答案 3 :(得分:0)

仅通过使用LEFT JOIN即可实现要求

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;