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

标签 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 的结果?我当然可以在代码中过滤结果,但最好是在单个查询中执行此操作。

最佳答案

我会使用两个 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 连接到表 B,然后进行反向连接。

我不确定您是否可以通过它们的 ID 连接表,所以我在这个例子中使用名称作为连接列。

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

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

关于mysql - 查找 MySQL 表之间的差异,仅返回特定列已更改的行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51892202/

相关文章:

php - mysql中的函数(1064错误)

MySQL 重构冗长查询

SQL 查询 - 在 UNION 中使用 Order By

php - Laravel 5.5 急切加载抛出错误 - 试图获取非对象的属性

MySQL脏读与SQL标准中定义的脏读

php - 使用 COALESCE 但列仍然更新

r - 当 R 中少一行时,使用 diff() 函数添加新列

mysql - 使用来自其他 MySQL 表的数据填充 MySQL 表

python - 在两个二进制文件中查找匹配序列

diff - 如何在 linux 中使用在 windows (with CRLF) 中创建的补丁?