查找 MySQL 个表之间的差异,仅返回特定列已更改的行
Find diff between MySQL tables, only returning rows where a specific column has changed
我需要区分两个 MysQL table,并报告对结果子集的更改。
假设我有这两个 table:
Table答:
id name supplier value
-----------------------------------------
1 Alice X 100
2 Bob Y 200
3 Clare Z 300
4 Desmond X 400
Table乙:
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 的任何行的更改感兴趣。鉴于上述情况,我想 return:
- ID 1,因为供应商是X,值变了;
- ID 2,因为供应商从Y变成了X;
- ID 5,因为供应商是X,在table 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
这 returns 数据已更改的所有行:
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,因为来自 table A 或 B 的行都没有供应商 X。
最后,我的问题是 - 如何 return 结果中差异行之一是供应商 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)
首先,您将 table A
连接到 table B
,其次您进行反向连接。
我不确定您是否可以通过他们的 ID 加入 table,所以我在这个例子中使用名称作为连接列。
每个连接都包含一个 WHERE
子句,该子句使用您的条件过滤行:"changes to any row involving supplier X".
这是一个 SQLFiddle:http://sqlfiddle.com/#!9/46f213/1
您可以在原始查询中添加一些 where 子句来检查供应商 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 相关的行。请注意,这假设每个 table 中始终只有一个匹配的 ID。
扩展 lldar 的答案,您还可以通过散列列然后查找更改来获得差异。
md5(concat(A.`Name`,A.`Supplier`, A.`Value`)) <> md5(concat(b.`Name`,b.`Supplier`,b.`Value`))
如果您有很多列,这会很有帮助。理想情况下,从长远来看,您可以编辑表格并将散列添加为 "computed/calculated" 列。
那么就是A.hash <> b.hash
仅使用 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;
我需要区分两个 MysQL table,并报告对结果子集的更改。
假设我有这两个 table:
Table答:
id name supplier value
-----------------------------------------
1 Alice X 100
2 Bob Y 200
3 Clare Z 300
4 Desmond X 400
Table乙:
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 的任何行的更改感兴趣。鉴于上述情况,我想 return:
- ID 1,因为供应商是X,值变了;
- ID 2,因为供应商从Y变成了X;
- ID 5,因为供应商是X,在table 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
这 returns 数据已更改的所有行:
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,因为来自 table A 或 B 的行都没有供应商 X。
最后,我的问题是 - 如何 return 结果中差异行之一是供应商 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)
首先,您将 table A
连接到 table B
,其次您进行反向连接。
我不确定您是否可以通过他们的 ID 加入 table,所以我在这个例子中使用名称作为连接列。
每个连接都包含一个 WHERE
子句,该子句使用您的条件过滤行:"changes to any row involving supplier X".
这是一个 SQLFiddle:http://sqlfiddle.com/#!9/46f213/1
您可以在原始查询中添加一些 where 子句来检查供应商 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 相关的行。请注意,这假设每个 table 中始终只有一个匹配的 ID。
扩展 lldar 的答案,您还可以通过散列列然后查找更改来获得差异。
md5(concat(A.`Name`,A.`Supplier`, A.`Value`)) <> md5(concat(b.`Name`,b.`Supplier`,b.`Value`))
如果您有很多列,这会很有帮助。理想情况下,从长远来看,您可以编辑表格并将散列添加为 "computed/calculated" 列。
那么就是A.hash <> b.hash
仅使用 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;