查询每组代码的前N个适用于MySQL,但是对于MariaDB有不同的结果

时间:2016-07-17 12:31:19

标签: mysql sql mariadb

我有一个SQL查询,它提取每组的最新3条记录。 MySQL的查询结果与MariaDB不同。此查询在下面的sqlfiddle中实现

http://sqlfiddle.com/#!9/c09fe/2

表的内容

SELECT mac_addr, reader_name, value, time_change
FROM (
    SELECT t1.*,
           IF(@rn = reader_name, @rowno := @rowno + 1, @rowno := 1) AS rowno,
           @rn := reader_name
    FROM (
        SELECT *
          FROM tmp
        ORDER BY reader_name, time_change DESC
    ) t1
    CROSS JOIN (SELECT @rn := null, @rowno := 0) t2
) t
WHERE rowno <= 3

查询以提取每组的最新3条记录。

mac_addr    reader_name             value   time_change
'B99A88'    'name_8'                 1      July, 07 2016 19:21:48
'B99A88'    'own__detect_1'          1      June, 21 2016 13:30:00
'B99A88'    'own__detect_1'          0      April, 20 2016 15:22:23
'B99A88'    'own__detect_1'          1      April, 15 2016 17:39:52
'B99A88'    'own__temperature_1'    37      May, 04 2016 18:23:03
'B99A88'    'own__temperature_1'    29      May, 04 2016 18:19:33
'B99A88'    'own__temperature_1'    28      May, 04 2016 18:17:32

使用MySQL v5.6时的结果如下;

mac_addr    reader_name             value   time_change
'B99A88'    'name_8'                 1      2016-07-07 19:21:48
'B99A88'    'own__detect_1'          1      2016-06-21 13:30:00
'B99A88'    'own__temperature_1'    37      2016-05-04 18:23:03
'B99A88'    'own__temperature_1'    29      2016-05-04 18:19:33
'B99A88'    'own__temperature_1'    28      2016-05-04 18:17:32
'B99A88'    'own__detect_1'          0      2016-04-20 15:22:23
'B99A88'    'own__detect_1'          1      2016-04-15 17:39:52
'B99A88'    'own__detect_1'          0      2016-04-15 17:39:46
'B99A88'    'own__temperature_1'    28      2016-04-10 21:20:20
'B99A88'    'own__temperature_1'    33      2016-04-10 21:00:00
'B99A88'    'own__temperature_1'    34      2016-04-10 11:29:00

MySQL的结果就是我想要的。但是,我使用的是MariaDB,结果与MySQL结果不同。

MariaDB结果如下所示;

xgboost

如何修改查询代码,以便MariaDB的查询输出可以与MySQL相同?在MariaDB中使用窗口函数会是一个好主意吗?

4 个答案:

答案 0 :(得分:4)

允许查询执行忽略 ORDER BY中的FROM ( SELECT ... )。这可能是你所看到的差异的真正原因。 (我不认为戈登的答案是相关的。)

这里讨论的问题(4年前):https://mariadb.com/kb/en/mariadb/group-by-trick-has-been-optimized-away/;有一个解决方案,通过设置。

其他一些解决方案在这里:http://mysql.rjweb.org/doc.php/groupwise_max;它们旨在提高效率。

另一个可能的解决方案是在子查询上添加一个带有大数字的伪LIMIT

答案 1 :(得分:3)

您使用的ORDER BY有两个键:

    ORDER BY reader_name, time_change DESC

但是,这些键不能唯一标识每一行。因此,无法保证密钥相同的行的排序 - 即使在同一数据库上的两次查询运行之间也是如此。通常的解决方案是添加唯一的id列作为最后一个ORDER BY键,以便唯一标识每一行。

更一般地说,在SQL中,ORDER BY不使用稳定排序。当键相同时,稳定排序是保留键的原始排序的排序。原因很简单。 SQL表和结果集表示无序集。没有初始订单可以保留。

如果您有主键列,那么ORDER BY将是:

    ORDER BY reader_name, time_change DESC, pk

其余代码无需更改。您只希望排序稳定。

答案 2 :(得分:1)

使用标准SQL语言结构选择每组前N的经典方法是使用ROW_NUMBER

SELECT
    T.*
FROM
    (
        SELECT *
            ,ROW_NUMBER() OVER (PARTITION BY reader_name ORDER BY time_change DESC) AS rn
        FROM tmp
    ) AS T
WHERE T.rn <= 3
ORDER BY reader_name, time_change DESC;

此查询应适用于支持ROW_NUMBER的所有DBMS。 MySQL不支持它,所以人们必须使用脆弱的技巧和特定于MySQL的变量。

Window functions首先在MariaDB 10.2.0中引入。 MariaDB在优化查询方面有更多的自由,而且这个带变量的MySQL技巧不再可靠。

所以,回答你的问题,是的,使用MariaDB中的窗口函数是个好主意

另一种选择每组前N个的常用方法是使用LATERAL连接,当组数较小且表中的行数较大且您有适当的时,这比ROW_NUMBER更好索引和包含组列表的第二个表。我不知道MariaDB是否支持LATERAL连接。看起来没有。

答案 3 :(得分:1)

(我的)SQL不需要保持子查询结果的顺序。您必须在上层查询中对结果集进行排序,但在您的情况下,您实际上可以摆脱子查询:

SELECT mac_addr, reader_name, value, time_change
FROM (
    SELECT t1.*,
           IF(@rn = reader_name, @rowno := @rowno + 1, @rowno := 1) AS rowno,
           @rn := reader_name
    FROM tmp t1, (SELECT @rn := null, @rowno := 0) t2
    ORDER BY reader_name, time_change DESC
) t
WHERE rowno <= 3;

只是为了完整性:这种行为特定于变量的使用,并且由于结果实际上没有在sql标准中定义,它可能会在某一天发生变化(就像现在导致你麻烦的优化),但这将是很可能不会发生,如果有的话,直到完全支持窗口函数,所以你可以忽略这个细节。同样可能适用于强制下订单的其他方式,例如在Rick建议的内部查询中添加limit 999999999,尽管我可以想到一些(尚未实现的)优化路径可能再次导致未指定的顺序。 / p>