2017-02-18 81 views
3

我多次被告知,相同的查询MariaDB的工作原理与MySQL上的相同,直到遇到此问题。MariaDB的“GROUP BY”与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  | 
    +----+----------+---------------------+-----------+ 

基本上我想从查询得到的是最新记录来自每个分组(即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的,这个工作,因为内部查询订货会,然后外部查询得到第一每组从RESU内部查询的内容。外部查询基本服从内部查询的排序。

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

现在的问题是...如果我要重写查询,我要改写数百人......他们是一些如何一点点有点彼此不同。

我想知道这里有没有人有任何想法可以让我做出最小的改变。

谢谢大家提前。

+0

MySQL手册涵盖了这一点。它提供了3种有效的解决方案。如果你还在挣扎,请参阅http://meta.stackoverflow.com/questions/333952/why-should-i-provide-an-mcve-for-what-seems-to-me-to-bea-a- very-simple-sql-query – Strawberry

+0

检查你的mysql和mariaDB版本,并检查select中的列的方式必须由group by子句使用..你能否遇到与only_full_group_by设置模式不同的行为在你的db版本中有问题..你可以看到一些信息https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html – scaisEdge

+0

MySQL版本是5.5.52 ... MariaDB版本是10.1.21 ... 我有试图在MariaDB上运行SET sql_mode =“ONLY_FULL_GROUP_BY”,但没有任何区别。 – user2526586

回答

1

你的第一个查询将在MySQL可能工作,但其行为未记录:你是groupid分组,但你与*选择非聚集列和任何这些非聚合列的值是不确定 - 如果你得到的价值是第一个遇到的价值,那只是一个“运气问题”。这是真的,即使它不能被认为是正确的,在MySQL上,我从来没有见过这个“伎俩”失败(在这里stackoverflow有很多upvoted的答案,建议你使用这个技巧),但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你必须得到最后creationDate:

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

然后每最大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。在这个特定的上下文中,与表本身的左连接应该更易于编写和更高性能。

+0

我不知道我是否完全理解你......所以如果我用你的建议重写我的示例SQL,这是否正确? (...)我的表“我的表”(WHERE'ID' IN)( \t SELECT MAX('id')FROM'MyTable' WHERE'parentId' IN(...)GROUP BY'creationDate' HAVING'creationDate' = MAX('creationDate ') )GROUP BY'parentId'; – user2526586

+0

谢谢。这很有帮助。现在我只需要深入研究SQL的一团糟并逐个修复...... – user2526586

2

是的,这是一个链接唯一的答案。但链接到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/