比较所有列的 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。

这就是为什么第一个查询使用空安全比较&lt;=&gt;(宇宙飞船)运算符。 NULL &lt;=&gt; 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查询问题:我想根据另一列的组值对一列进行部分总和

使用 mariaDb,mySQL 获取以 km 为单位的半径内的所有空间类型点

数据库对比:选择MariaDB还是MySQL?

MariaDB/Mysql的用法

Mariadb/Mysql TEMPORARY表

(MariaDB)MySQL内置函数大全