MariaDB上的"GROUP BY"行为与MySQL不同



我已经被告知很多次,同样的疑问,玛丽亚德(MariaDB)的工作方式就像在mysql上的方式一样……直到我遇到这个问题。

最近,我试图从 mySQL (Innodb)到 Mariadb (xtradb)的应用程序。尽管Mariadb不需要更改任何内容而运行MySQL查询,但我惊讶地发现,同一查询实际上在两个平台上的行为都大不相同,尤其是在> 和组中, 。

示例:

    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      |
    +----+----------+---------------------+-----------+

基本上我想从查询中获得的是每个分组的最新记录(即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`;

on mySQL ,这起作用,因为内部查询将排序,然后外部查询从内部查询的结果中获取每个组的第一个。外部查询基本上遵守内部查询的顺序。

但是,在 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;

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

我想知道这里有人是否有任何想法可以使我做出最小的变化。

预先感谢大家。

是的,这是一个仅链接的答案。但是链接是到Mariadb站点。

这是对"不兼容"的另一个讨论:https://mariadb.com/kb/en/mariadb/group-by-by-trick-has-has-has-ben-been-optimimized-away/

从技术上讲,MySQL实施了ANSI标准的扩展。很久以后,它决定删除它,所以我认为您会发现MySQL已迁移到Mariadb。

这是"快速"做群体最大方法的列表,这可能是您要尝试的事情:https://mariadb.com/kb/kb/en/mariadb/groupwise-max-max-max-in-mariadb/

您的第一个查询可能在mySQL中起作用,但没有记录在mysql中:您是按groupid进行分组,但是您选择了具有 * *的非聚集列,以及任何非 - 的值汇总列是未定义的 - 如果您获得的值是第一个遇到的值,那只是一个"运气问题"。

的确,即使不能正确地认为它是正确的,在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
)

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

编辑

您的示例有些复杂,因为您正在通过创建日期和ID订购。让我更好地解释。首先要做的是,对于每个parentid,您都必须获得最后一个创建日期:

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

然后,对于每个最大创建模式,您都必须获得最高的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的所有记录。在此特定上下文中

最新更新