优化 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 应该有窗口功能,所以明年找个时间看看吧。
我在及时获取将数据集精简为 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 应该有窗口功能,所以明年找个时间看看吧。