mysql - 使用单个 MySQL 表的各种条件优化 SQL 查询以获得最大值

标签 mysql query-optimization greatest-n-per-group

我有以下 SQL 查询

SELECT  *
    FROM  `sensor_data` AS `sd1`
    WHERE  (sd1.timestamp BETWEEN '2017-05-13 00:00:00'
                              AND '2017-05-14 00:00:00')
      AND  (`id` = 
            (
                SELECT  `id`
                    FROM  `sensor_data` AS `sd2`
                    WHERE  sd1.mid = sd2.mid
                      AND  sd1.sid = sd2.sid
                    ORDER BY  `value` DESC, `id` DESC
                    LIMIT  1)
           ) 

背景:

我已经 checked the validity of the query通过将 LIMIT 1 更改为 LIMIT 0,查询工作没有任何问题。但是,对于 LIMIT 1,查询没有完成,它只是说明 loading,直到我关闭并重新启动。

分解查询:

我已将具有日期边界的查询分解如下:

SELECT  *
    FROM  `sensor_data` AS `sd1`
    WHERE  (sd1.timestamp BETWEEN '2017-05-13 00:00:00'
                              AND '2017-05-14 00:00:00') 

这需要大约 0.24 秒来返回包含 8200 行的查询,每行有 5 列。

问题:

我怀疑我的查询的后半部分不正确或未优化。 表格如下:

当前表:

+------+-------+-------+-----+-----------------------+
|  id  |  mid  |  sid  |  v  |       timestamp       |
+------+-------+-------+-----+-----------------------+
|  51  |  10   |   1   |  40 |  2015-05-13 11:56:01  |
|  52  |  10   |   2   |  39 |  2015-05-13 11:56:25  |
|  53  |  10   |   2   |  40 |  2015-05-13 11:56:42  |
|  54  |  10   |   2   |  40 |  2015-05-13 11:56:45  |
|  55  |  10   |   2   |  40 |  2015-05-13 11:57:01  |
|  56  |  11   |   1   |  50 |  2015-05-13 11:57:52  |
|  57  |  11   |   2   |  18 |  2015-05-13 11:58:41  |
|  58  |  11   |   2   |  19 |  2015-05-13 11:58:59  |
|  59  |  11   |   3   |  58 |  2015-05-13 11:59:01  |
|  60  |  11   |   3   |  65 |  2015-05-13 11:59:29  |
+------+-------+-------+-----+-----------------------+

问:如何为每个 mid 获取每个 sidMAX(v)NB#1: 在上面的例子中 ROW 53, 54, 55 具有相同的值 (40),但我想检索具有最新时间戳的行,即 ROW 55

预期输出:

+------+-------+-------+-----+-----------------------+
|  id  |  mid  |  sid  |  v  |       timestamp       |
+------+-------+-------+-----+-----------------------+
|  51  |  10   |   1   |  40 |  2015-05-13 11:56:01  |
|  55  |  10   |   2   |  40 |  2015-05-13 11:57:01  |
|  56  |  11   |   1   |  50 |  2015-05-13 11:57:52  |
|  58  |  11   |   2   |  19 |  2015-05-13 11:58:59  |
|  60  |  11   |   3   |  65 |  2015-05-13 11:59:29  |
+------+-------+-------+-----+-----------------------+

表的结构:

enter image description here

注意#2: 由于此表有超过 1.1 亿个条目,因此设置日期界限非常重要,这限制了 24 小时内约 8000 个条目。

最佳答案

查询可以这样写:

SELECT t1.id, t1.mid, t1.sid, t1.v, t1.ts
FROM yourtable t1
INNER JOIN (
        SELECT mid, sid, MAX(v) as v 
            FROM yourtable
        WHERE ts BETWEEN '2015-05-13 00:00:00' AND '2015-05-14 00:00:00'
        GROUP BY mid, sid
    ) t2
    ON  t1.mid = t2.mid
    AND t1.sid = t2.sid
    AND t1.v   = t2.v
INNER JOIN (
        SELECT mid, sid, v, MAX(ts) as ts
            FROM yourtable
        WHERE ts BETWEEN '2015-05-13 00:00:00' AND '2015-05-14 00:00:00'
        GROUP BY mid, sid, v
    ) t3
    ON  t1.mid = t3.mid
    AND t1.sid = t3.sid
    AND t1.v   = t3.v
    AND t1.ts  = t3.ts; 

编辑和解释:

第一个子查询(第一个 INNER JOIN)根据 (mid, sid) 组合获取 MAX(v)。第二个子查询是为每个 (mid, sid, v) 识别 MAX(ts)。此时,两个查询不会影响彼此的结果。同样重要的是要注意 ts 日期范围选择是在两个子查询中独立完成的,因此最终查询要检查的行更少,并且没有额外的 WHERE 过滤器申请。

实际上,这转化为最初为每个 (mid, sid) 组合获取 MAX(v)(第一个子查询);如果对于给定的 (mid, sid) 组合,有多个具有相同值 MAX(v) 的记录,则通过选择MAX(ts) 对于第二个子查询获得的每个 (mid, sid, v) 组合。然后,我们通过两个 INNER JOIN 条件简单地将两个查询的输出相关联,以获取所需记录的 id

Demo

关于mysql - 使用单个 MySQL 表的各种条件优化 SQL 查询以获得最大值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43990650/

相关文章:

javascript - 如何从运行 PHP 和 MySQL 的 Web 服务器读取 JSON 数据?

mysql - 使用 LEFT JOIN 优化 JOIN

azure-cosmosdb - 根据 Azure Cosmos DB 中文档的属性获取最新文档

mysql - 更新列值为 True 且时间戳列为 MAX 的位置

mysql - com.mysql.jdbc.PacketTooBigException

xml - 优化 postgresql 9.6 中的批量 xml 数据插入

MySQL查询优化从大表中获取8-10条记录

MySQL每组获取一条记录

mysql - 从 SQL 中选择最佳结果

PHP MYSQL CONCAT,字符串开头包含逗号