优化 MYSQL 排名 mod 查询

Optimize MYSQL ranking mod query

我在及时获取将数据集精简为 return 结果的查询时遇到了一些重大问题。我已将此 table 的索引(我认为这足以提高速度)以及查询逻辑粘贴在索引下方。

我尝试的一件事是删除内部查询中的 "ORDER BY",但这实际上并没有改善它的时间,也没有清除我选择的一些额外的不需要的列,从而改善了时间, 但没能做到 "fast enough"。

SELECT unix_date, price FROM
(SELECT @row := @row +1 as row_num, unix_date, price 
FROM (SELECT @row:=0, unix_date, price FROM 
price_data WHERE created_date >= '2017-03-26 00:00:00' AND created_date 
<= '2017-06-26 23:59:59' AND currency= 'USD' ORDER BY unix_date DESC) 
AS p) AS d
WHERE MOD(row_num, 288) = 1;

此查询的重点只是试图 return 价格数据点(unix 时间戳、价格)的结果集,但每第 288 个(或 X)将其精简到 return数据点。这个 table 目前真的很小(总行数:198109)所以我很难理解为什么查询要花这么长时间才能到达 return。

目前 table 上的索引如下:

| Table              | Non_unique | Key_name     | Seq_in_index | 
Column_name  | Collation | Cardinality | Sub_part | Packed | Null | 
Index_type | Comment | Index_comment |
+--------------------+------------+--------------+--------------+------
--------+-----------+-------------+----------+--------+------+---------
---+---------+---------------+
| price_data |          0 | PRIMARY      |            1 | 
unix_date    | A         |      200002 |     NULL | NULL   |      | 
BTREE      |         |               |
| price_data |          0 | PRIMARY      |            2 | 
currency     | A         |      200002 |     NULL | NULL   |      | 
BTREE      |         |               |
| price_data |          1 | created_date |            1 | 
created_date | A         |      200002 |     NULL | NULL   |      | 
BTREE      |         |               |
| price_data |          1 | price        |            1 | price        
| A         |      200002 |     NULL | NULL   | YES  | BTREE      |         
|               |
+--------------------+------------+--------------+--------------+------
--------+-----------+-------------+----------+--------+------+---------
---+---------+--

根据建议,我添加了创建 table:

  CREATE TABLE `price_data` (
  `created_date` datetime NOT NULL,
  `unix_date` int(11) NOT NULL,
  `currency` varchar(255) NOT NULL DEFAULT '',
  `price` decimal(10,6) DEFAULT NULL,
  PRIMARY KEY (`unix_date`,`currency`),
  KEY `created_date` (`created_date`),
  KEY `price` (`price`)
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1 |

如有任何关于如何提高此查询速度的建议,我们将不胜感激。

编辑:问题是否与此查询的最终 WHERE 类似,实际上是根据 row_num 的 "fake column" 评估信息,这是从先前的内部查询派生的,因此它本身并没有真正的索引?那么,当 WHERE 正在评估时,它并没有以普通索引列的速度进行评估吗?

将主键更改为货币,unix_date。由于您有货币的相等条件和 unix_date 的范围条件,您应该将具有相等条件的列放在第一位。那么 unix_date 上的范围条件和 ORDER BY 都应该使用主键顺序。

将条件应用于 unix_date,而不是 create_date,以使其使用主键索引。

您必须使用派生的 table 子查询,但不必使用两个嵌套级别的子查询。

SELECT row_num, unix_date, price
FROM (
    SELECT @row := @row + 1 AS row_num, unix_date, price
    FROM (SELECT @row := 0) AS _init
    CROSS JOIN price_data
    WHERE currency = 'USD' 
     AND unix_date BETWEEN UNIX_TIMESTAMP('2017-03-26 00:00:00') 
                       AND UNIX_TIMESTAMP('2017-06-26 23:59:59')
    ORDER BY unix_timestamp DESC
) AS t
WHERE MOD(row_num, 288) = 1

你应该学会use EXPLAIN来帮助你分析索引的使用情况。

你可能也喜欢我的介绍How to Design Indexes, Really, and the video: https://www.youtube.com/watch?v=ELR7-RdU9XU

MySQL 8.0 应该有窗口功能,所以明年找个时间看看吧。