比较所有列的 MariaDB/MySQL 的替代方案
Posted
技术标签:
【中文标题】比较所有列的 MariaDB/MySQL 的替代方案【英文标题】:Alternative for EXCEPT for MariaDB/MySQL comparing all columns 【发布时间】:2017-07-18 18:20:51 【问题描述】:我知道 MariaDB 和 mysql 不支持 EXCEPT。 我想找到这样的替代方案:
SELECT * FROM table
EXCEPT
SELECT * FROM backup_table
其中 table 和 backup_table 具有相同的架构。
我看到的所有帖子都建议我使用“WHERE column IN (...)”比较单个列。 我的问题是我需要为每个表比较两个表之间的所有列。我希望将其编写为遍历所有表的过程或函数,以查找数据库中的任何更改。基本上,我想找出所有表中已更新或插入的所有记录。
【问题讨论】:
你可以做WHERE (column1, column2, ....) NOT IN (SELECT column1, column2, ....)
【参考方案1】:
如果我面临这个任务,我会使用反连接模式。这是一个外连接,用于返回当前表中的所有行,以及备份表中的“匹配”行。然后在 WHERE 子句中,我们排除所有完全匹配的行。返回不匹配的行。
SELECT t.*
FROM mytable t
LEFT
JOIN backup_mytable s
ON s.id <=> t.id
AND s.col_two <=> t.col_two
AND s.col_three <=> t.col_three
AND ...
WHERE s.id IS NULL
这假定列id
保证为非NULL。 PRIMARY KEY 列(或作为表的 PRIMARY KEY 一部分的任何列,或具有 NOT NULL 约束的任何列都可以使用。)
此查询仅返回与备份表中的行不匹配的行。它不指示它的行是否不存在,或者列的值是否已更改。
要获取原始表中与备份表中的行不匹配的行,只需交换表名即可。
对于所有列都定义为 NOT NULL 的表的特殊情况,我们可以在连接谓词上采用快捷方式。
FROM mytable t
NATURAL
LEFT
JOIN backup_mytable s
WHERE s.id IS NULL
这相当于一个 LEFT JOIN 与一个 USING 子句的所有列在两个表中命名相同。
FROM mytable t
LEFT
JOIN backup_mytable s
USING (id, col_two, col_three, ...)
WHERE s.id IS NULL
这相当于在每一列上指定一个相等比较(如果两个表具有相同的列)
FROM mytable t
LEFT
JOIN backup_mytable s
ON s.id = t.id
AND s.col_two = t.col_two
AND s.col_three = t.col_three
任何列中出现的任何 NULL 值都会与相等比较发生冲突,并返回 NULL。
这就是为什么第一个查询使用空安全比较<=>
(宇宙飞船)运算符。 NULL <=> NULL
将返回 TRUE,NULL = NULL
将返回 NULL。
对于第一个查询模式,我会使用 SQL 来帮助我生成所需的 SQL,而不是繁琐地输入每一列的所有比较。
SELECT CONCAT(' AND s.`',c.column_name,'` <=> t.`',c.column_name,'`') AS `-- stmt`
FROM information_schema.columns c
WHERE c.table_schema = 'mydatabase'
AND c.table_name = 'mytable'
ORDER BY c.ordinal_position
我会获取该查询返回的行,并将其粘贴到
SELECT t.*
FROM ... t
JOIN ... s
ON 1=1
-- paste here --
WHERE s.id IS NULL
ORDER BY t.id
如果我需要只匹配id
列的查询,并且需要识别哪些 列发生了变化,我会在SELECT 列表中使用表达式。例如:
SELECT s.`id` <=> t.`id` AS `match_id`
, s.`col_one` <=> t.`col_one` AS `match_col_one`
, s.`col_three` <=> t.`col_three` AS `match_col_three`
FROM mytable t
JOIN backup_mytable s
ON s.id = t.id
HAVING NOT match_col_one
此处在HAVING
子句中引用SELECT 列表中的列别名,以排除具有相同col_one
值的行;返回 col_one
不同的行。
再次,我将针对 information_schema.columns 使用 SQL 来帮助加快查询编写过程。
【讨论】:
【参考方案2】:我对 MySQL 或 MariaDB 没有太多经验,但我发现这是对另一个可能对您有用的问题的答案。
SELECT *
FROM match m
WHERE NOT EXISTS
(
SELECT 1
FROM email e
WHERE e.id = m.id
)
以下帖子归功于 Quassnoi: MySQL SELECT x FROM a WHERE NOT IN ( SELECT x FROM b ) - Unexpected result
【讨论】:
如果我正在查看两个表中的每个表中是否存在 id,您的建议将起作用。尽管该信息很重要,但我也希望比较表中的每一列以进行更改。不过谢谢你的建议。【参考方案3】:从version 10.3.0 开始,MariaDB 增加了对缺失集合操作的支持,包括但不限于EXCEPT。
CREATE TABLE `table` ( `item` VARCHAR(1) NOT NULL );
CREATE TABLE `backup_table` ( `item` VARCHAR(1) NOT NULL );
INSERT INTO `table` VALUES ( 'a' ), ( 'b' ), ( 'c' );
INSERT INTO `backup_table` VALUES ( 'a' ), ( 'b' ), ( 'd' );
SELECT * FROM `table`
EXCEPT
SELECT * FROM `backup_table`;
+------+
| item |
+------+
| c |
+------+
【讨论】:
以上是关于比较所有列的 MariaDB/MySQL 的替代方案的主要内容,如果未能解决你的问题,请参考以下文章
MariaDB/mysql SQL查询问题:我想根据另一列的组值对一列进行部分总和