在mysql表之间查找diff,只返回特定列已更改的行

du7egjpx  于 2021-06-20  发布在  Mysql
关注(0)|答案(4)|浏览(406)

我需要区分两个mysql表,并报告对结果子集的更改。
假设我有两张table:
表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;
ID5,因为供应商是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中的行都没有supplier x。
最后,我的问题是-如果其中一个不同的行是supplier x,如何返回结果?我当然可以在代码中过滤结果,但是在一个查询中这样做会更好。

uajslkp6

uajslkp61#

我会用两个 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 使用cirteria筛选行的子句:“对涉及供应商x的任何行的更改”。
这是一把小提琴:http://sqlfiddle.com/#!9/46f213/1号楼

ssm49v7z

ssm49v7z2#

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

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

如果您有许多列,这会很有帮助。理想情况下,从长远来看,您可以编辑这些表,并将哈希添加为“computed/calculated”列。
那就简单了 A.hash <> b.hash

gblwokeq

gblwokeq3#

您可以在原始查询中添加一些where子句来检查supplier 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。

4jb9z9bj

4jb9z9bj4#

只使用左连接就可以满足要求

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;

相关问题