【问题标题】:"GROUP BY" on MariaDB behaves differently from MySQLMariaDB 上的“GROUP BY”的行为与 MySQL 不同
【发布时间】:2017-02-18 07:42:17
【问题描述】:

我多次被告知,相同的查询 MariaDB 将像在 MySQL 上一样工作......直到我遇到这个问题。

最近,我正在尝试将应用程序从 MySQL(InnoDB) 克隆到 MariaDB(XtraDB)。 尽管 MariaDB 无需更改任何内容即可运行 MySQL 查询,但我惊讶地发现,相同的查询实际上在两个平台上的行为完全不同,尤其是在 ORDER BYGROUP BY 中。

举个例子:

    MyTable
    =======
    +----+----------+---------------------+-----------+
    | id | parentId | creationDate        | name      |
    +----+----------+---------------------+-----------+
    | 1  | 2357     | 2017-01-01 06:03:40 | Anna      |
    +----+----------+---------------------+-----------+
    | 2  | 5480     | 2017-01-02 07:13:20 | Becky     |
    +----+----------+---------------------+-----------+
    | 3  | 2357     | 2017-01-03 08:20:12 | Christina |
    +----+----------+---------------------+-----------+
    | 4  | 2357     | 2017-01-03 08:20:15 | Dorothy   |
    +----+----------+---------------------+-----------+
    | 5  | 5480     | 2017-01-04 09:25:45 | Emma      |
    +----+----------+---------------------+-----------+
    | 6  | 1168     | 2017-01-05 10:30:10 | Fiona     |
    +----+----------+---------------------+-----------+
    | 7  | 5480     | 2017-01-05 10:33:23 | Gigi      |
    +----+----------+---------------------+-----------+
    | 8  | 1168     | 2017-01-06 12:46:34 | Heidi     |
    +----+----------+---------------------+-----------+
    | 9  | 1168     | 2017-01-06 12:46:34 | Irene     |
    +----+----------+---------------------+-----------+
    | 10 | 2357     | 2017-01-07 14:58:37 | Jane      |
    +----+----------+---------------------+-----------+
    | 11 | 2357     | 2017-01-07 14:58:37 | Katy      |
    +----+----------+---------------------+-----------+

基本上,我想从查询中获得的是每个 GROUPing(即parentId)的最新记录。最近,我的意思是 MAX(creationDate) 和 MAX(id)

所以,对于上面的例子,由于只有三个不同的 parentId 值,我希望得到:

    +----+----------+---------------------+-----------+
    | id | parentId | creationDate        | name      |
    +----+----------+---------------------+-----------+
    | 11 | 2357     | 2017-01-07 14:58:37 | Katy      |
    +----+----------+---------------------+-----------+
    | 9  | 1168     | 2017-01-06 12:46:34 | Irene     |
    +----+----------+---------------------+-----------+
    | 7  | 5480     | 2017-01-05 10:33:23 | Gigi      |
    +----+----------+---------------------+-----------+

最初应用程序有类似这种方式的查询:

SELECT * FROM
  ( SELECT * FROM `MyTable` WHERE `parentId` IN (...)
    ORDER BY `creationDate` DESC, `id` DESC ) AS `t` 
  GROUP BY `parentId`;

MySQL 上,这是可行的,因为内部查询将排序,然后外部查询从内部查询的结果中获取每个 GROUP 的第一个。外部查询基本上遵循内部查询的顺序。

但在 MariaDB 上,外部查询会忽略内部查询结果的排序。我在 MariaDB 上得到了这个:

    +----+----------+---------------------+-----------+
    | id | parentId | creationDate        | name      |
    +----+----------+---------------------+-----------+
    | 1  | 2357     | 2017-01-01 06:03:40 | Anna      |
    +----+----------+---------------------+-----------+
    | 2  | 5480     | 2017-01-02 07:13:20 | Becky     |
    +----+----------+---------------------+-----------+
    | 6  | 1168     | 2017-01-05 10:30:10 | Fiona     |
    +----+----------+---------------------+-----------+

为了在 MariaDB 上实现相同的行为,我想出了类似的方法。 (但不确定这是否准确。)

SELECT `t1`.* FROM `MyTable` `t1` LEFT JOIN `MyTable` `t2` ON (
        `t1`.`parentId` = `t2`.`parentId`
    AND `t2`.`parentId` IN (...)
    AND `t1`.`creationDate` <= `t2`.`creationDate`
    AND `t1`.`id` < `t2`.`id`)
  ) WHERE `t2`.`id` IS NULL;

现在的问题是……如果我要重写查询,我必须重写数百个……而且它们彼此之间有些不同。

我想知道这里是否有人有任何想法可以让我做出尽可能少的更改。

提前谢谢大家。

【问题讨论】:

  • MySQL 手册涵盖了这一点。它提供了 3 个有效的解决方案。如果你还在苦苦挣扎,请参阅meta.stackoverflow.com/questions/333952/…
  • 检查你的mysql和mariaDB版本并检查select中的列必须由group by子句使用的方式..你的问题是否与你的db版本中的only_full_group_by设置模式不同的行为有关..你可以看到一些信息dev.mysql.com/doc/refman/5.7/en/sql-mode.html
  • MySQL 版本是 5.5.52... MariaDB 版本是 10.1.21... 我尝试在 MariaDB 上运行 SET sql_mode = "ONLY_FULL_GROUP_BY" 但没有区别。

标签: mysql database group-by sql-order-by mariadb


【解决方案1】:

是的,这是一个仅链接的答案。但是这些链接指向 MariaDB 网站。

这里是另一个关于“不兼容”的讨论:https://mariadb.com/kb/en/mariadb/group-by-trick-has-been-optimized-away/

从技术上讲,MySQL 实现了对 Ansi 标准的扩展。很久以后,它决定删除它,所以我想你会发现 MySQL 已经迁移到 MariaDB。

这里列出了“快速”进行分组最大值的方法,这可能是您想要做的:https://mariadb.com/kb/en/mariadb/groupwise-max-in-mariadb/

【讨论】:

    【解决方案2】:

    您的第一个查询可能会在 MySQL 中运行,但它的行为没有记录:您按 groupid 分组,但您选择带有 * 的非聚合列,并且任何这些非聚合列的值为 undefined - 如果你得到的值是第一个遇到的值,那只是“运气问题”。

    确实,即使它不能被认为是正确的,在 MySQL 上我从未见过这个“技巧”失败(在 stackoverflow 上有很多赞成的答案建议你使用这个技巧),但是 MariaDB 使用不同的优化引擎,您不能依赖 MySQL 未记录的行为。

    您的第二个查询需要稍作调整:

    and (
      `t1`.`creationDate` < `t2`.`creationDate`
      or (
        `t1`.`creationDate` = `t2`.`creationDate`
         and `t1`.`id` < `t2`.`id`
      )
    )
    

    因为首先您按创建日期排序,然后如果多个记录共享相同的创建日期,您将获得具有最高 id 的记录。

    还有其他方法可以编写相同的查询,例如

    select * from mytable
    where id in (
      select max(m.id)
      from mytable m inner join (
        select parentID, max(creationDate) as max_cd
        from mytable
        group by ParentID
      ) t on m.parentID = t.parentID and m.creationDate = t.max_cd
      group by m.parentID, m.creationDate
    )
    

    但每个查询都需要单独重写。

    编辑

    您的示例稍微复杂一些,因为您同时按creationDate 和id 进行排序。让我解释得更好。首先要做的是,对于每个 parentID,您必须获取最后一个创建日期:

    select parentID, max(creationDate) as max_cd
    from MyTable
    group by parentID
    

    那么对于每个 max creationDate 你必须获得最高的 id:

    select t.parentID, t.max_cd, max(t.id) as max_id
    from
      MyTable t inner join (  
        select parentID, max(creationDate) as max_cd
        from MyTable
        group by parentID
      ) t1 on t.parentID = t1.parentID and t.creationDate = t1.max_cd
    group t.parentID, t.max_cd
    

    那么您必须获取此查询返回 id 的所有记录。在这种特殊情况下,与表本身的 LEFT JOIN 应该更易于编写且性能更高。

    【讨论】:

    • 我不知道我是否完全理解你......所以如果我使用你的建议重写我的示例 SQL,这是否正确? SELECT * FROM MyTable WHERE id IN (SELECT MAX(id) FROM MyTable WHERE parentId IN (...) GROUP BY creationDate HAVING creationDate = MAX(creationDate)分组parentId;
    • 谢谢。这很有帮助。现在我只需要深入研究杂乱无章的 SQL 并一一修复...
    猜你喜欢
    • 2010-12-18
    • 2015-07-14
    • 2015-05-05
    • 2010-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多