【问题标题】:MySQL query with dependent subquery takes too long具有依赖子查询的 MySQL 查询耗时太长
【发布时间】:2010-07-22 10:09:12
【问题描述】:

我需要一位 SQL 专家来帮助我加快查询速度。

我有 2 张桌子,数量和价格。数量记录 2 个时间戳之间的数量值,相隔 15 分钟。 price 记录给定时间戳、给定价格类型的价格,每 5 分钟有一条价格 5 记录。

我需要 2 计算出每个时期的总价格,例如小时或天,在两个时间戳之间。这是通过每个时期的(数量乘以 15 分钟数量窗口中 3 个价格的平均值)之和计算得出的。

例如,假设我想查看 1 天每小时的总价格。结果集中每一行中的总价格值是该小时内四个 15 分钟时段中每个时段的总价格之和。每 15 分钟期间的总价格是通过将该期间的数量值乘以该数量期间的 3 个价格(每 5 分钟一个)的平均值来计算的。

这是我正在使用的查询和结果:

SELECT
MIN( `quantities`.`start_timestamp` ) AS `start`,
MAX( `quantities`.`end_timestamp` ) AS `end`,
SUM( `quantities`.`quantity` * (
  SELECT AVG( `prices`.`price` )
  FROM `prices`
  WHERE `prices`.`timestamp` >= `quantities`.`start_timestamp`
  AND `prices`.`timestamp` < `quantities`.`end_timestamp`
  AND `prices`.`type_id` = 1
) ) AS total
FROM `quantities`
WHERE `quantities`.`start_timestamp` >= '2010-07-01 00:00:00'
AND `quantities`.`start_timestamp` < '2010-07-02 00:00:00'
GROUP BY HOUR(  `quantities`.`start_timestamp` );

+---------------------+---------------------+----------+
| start               | end                 | total    |
+---------------------+---------------------+----------+
| 2010-07-01 00:00:00 | 2010-07-01 01:00:00 | 0.677733 |
| 2010-07-01 01:00:00 | 2010-07-01 02:00:00 | 0.749133 |
| 2010-07-01 02:00:00 | 2010-07-01 03:00:00 | 0.835467 |
| 2010-07-01 03:00:00 | 2010-07-01 04:00:00 | 0.692233 |
| 2010-07-01 04:00:00 | 2010-07-01 05:00:00 | 0.389533 |
| 2010-07-01 05:00:00 | 2010-07-01 06:00:00 | 0.335300 |
| 2010-07-01 06:00:00 | 2010-07-01 07:00:00 | 1.231467 |
| 2010-07-01 07:00:00 | 2010-07-01 08:00:00 | 0.352800 |
| 2010-07-01 08:00:00 | 2010-07-01 09:00:00 | 1.447200 |
| 2010-07-01 09:00:00 | 2010-07-01 10:00:00 | 0.756733 |
| 2010-07-01 10:00:00 | 2010-07-01 11:00:00 | 0.599467 |
| 2010-07-01 11:00:00 | 2010-07-01 12:00:00 | 1.056467 |
| 2010-07-01 12:00:00 | 2010-07-01 13:00:00 | 1.252600 |
| 2010-07-01 13:00:00 | 2010-07-01 14:00:00 | 1.285567 |
| 2010-07-01 14:00:00 | 2010-07-01 15:00:00 | 0.442933 |
| 2010-07-01 15:00:00 | 2010-07-01 16:00:00 | 0.692567 |
| 2010-07-01 16:00:00 | 2010-07-01 17:00:00 | 1.281067 |
| 2010-07-01 17:00:00 | 2010-07-01 18:00:00 | 0.652033 |
| 2010-07-01 18:00:00 | 2010-07-01 19:00:00 | 1.721900 |
| 2010-07-01 19:00:00 | 2010-07-01 20:00:00 | 1.362400 |
| 2010-07-01 20:00:00 | 2010-07-01 21:00:00 | 1.099300 |
| 2010-07-01 21:00:00 | 2010-07-01 22:00:00 | 0.646267 |
| 2010-07-01 22:00:00 | 2010-07-01 23:00:00 | 0.873100 |
| 2010-07-01 23:00:00 | 2010-07-02 00:00:00 | 0.546533 |
+---------------------+---------------------+----------+
24 rows in set (5.16 sec)

我需要查询运行得比这快得多,尽管它是可能的。这是 EXPLAIN EXTENDED 的结果...

+----+--------------------+------------+-------+-------------------+-----------------+---------+-------+-------+----------------------------------------------+
| id | select_type        | table      | type  | possible_keys     | key             | key_len | ref   | rows  | Extra                                        |
+----+--------------------+------------+-------+-------------------+-----------------+---------+-------+-------+----------------------------------------------+
|  1 | PRIMARY            | quantities | range | start_timestamp   | start_timestamp | 8       | NULL  |    89 | Using where; Using temporary; Using filesort |
|  2 | DEPENDENT SUBQUERY | prices     | ref   | timestamp,type_id | type_id         | 4       | const | 22930 | Using where                                  |
+----+--------------------+------------+-------+-------------------+-----------------+---------+-------+-------+----------------------------------------------+
2 rows in set, 3 warnings (0.00 sec)

我注意到依赖子查询没有使用键中的时间戳字段,并且查询正在扫描大量行。

谁能帮我让它运行得快很多吗?

这里是创建架构并用大量数据填充它所需的 SQL 语句(价值 2 个月)

# Create prices table

CREATE TABLE `prices` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `timestamp` datetime NOT NULL,
  `type_id` int(11) NOT NULL,
  `price` float(8,2) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `timestamp` (`timestamp`),
  KEY `type_id` (`type_id`)
) ENGINE=MyISAM;

# Create quantities table

CREATE TABLE `quantities` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `start_timestamp` datetime NOT NULL,
  `end_timestamp` datetime NOT NULL,
  `quantity` float(7,2) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `start_timestamp` (`start_timestamp`),
  KEY `end_timestamp` (`end_timestamp`)
) ENGINE=MyISAM;

# Insert first 2 rows into prices, one for each of 2 types, starting 64 days ago

INSERT INTO `prices` (`id`, `timestamp`, `type_id`, `price`) VALUES
(NULL, DATE_SUB(CURDATE(), INTERVAL 64 DAY), '1', RAND()),
(NULL, DATE_SUB(CURDATE(), INTERVAL 64 DAY), '2', RAND());

# Fill the prices table with a record for each type, for every 5 minutes, for the next 64 days

INSERT INTO prices (`timestamp`, `type_id`, `price`) SELECT DATE_ADD(`timestamp`, INTERVAL 32 DAY), `type_id`, RAND() FROM prices;
INSERT INTO prices (`timestamp`, `type_id`, `price`) SELECT DATE_ADD(`timestamp`, INTERVAL 16 DAY), `type_id`, RAND() FROM prices;
INSERT INTO prices (`timestamp`, `type_id`, `price`) SELECT DATE_ADD(`timestamp`, INTERVAL 8 DAY), `type_id`, RAND() FROM prices;
INSERT INTO prices (`timestamp`, `type_id`, `price`) SELECT DATE_ADD(`timestamp`, INTERVAL 4 DAY), `type_id`, RAND() FROM prices;
INSERT INTO prices (`timestamp`, `type_id`, `price`) SELECT DATE_ADD(`timestamp`, INTERVAL 2 DAY), `type_id`, RAND() FROM prices;
INSERT INTO prices (`timestamp`, `type_id`, `price`) SELECT DATE_ADD(`timestamp`, INTERVAL 1 DAY), `type_id`, RAND() FROM prices;
INSERT INTO prices (`timestamp`, `type_id`, `price`) SELECT DATE_ADD(`timestamp`, INTERVAL 12 HOUR), `type_id`, RAND() FROM prices;
INSERT INTO prices (`timestamp`, `type_id`, `price`) SELECT DATE_ADD(`timestamp`, INTERVAL 6 HOUR), `type_id`, RAND() FROM prices;
INSERT INTO prices (`timestamp`, `type_id`, `price`) SELECT DATE_ADD(`timestamp`, INTERVAL 3 HOUR), `type_id`, RAND() FROM prices;
INSERT INTO prices (`timestamp`, `type_id`, `price`) SELECT DATE_ADD(`timestamp`, INTERVAL 90 MINUTE), `type_id`, RAND() FROM prices;
INSERT INTO prices (`timestamp`, `type_id`, `price`) SELECT DATE_ADD(`timestamp`, INTERVAL 45 MINUTE), `type_id`, RAND() FROM prices;
INSERT INTO prices (`timestamp`, `type_id`, `price`) SELECT DATE_ADD(`timestamp`, INTERVAL 20 MINUTE), `type_id`, RAND() FROM prices;
INSERT INTO prices (`timestamp`, `type_id`, `price`) SELECT DATE_ADD(`timestamp`, INTERVAL 10 MINUTE), `type_id`, RAND() FROM prices;
INSERT INTO prices (`timestamp`, `type_id`, `price`) SELECT DATE_ADD(`timestamp`, INTERVAL 5 MINUTE), `type_id`, RAND() FROM prices;
INSERT INTO prices (`timestamp`, `type_id`, `price`) SELECT DATE_SUB(`timestamp`, INTERVAL 5 MINUTE), `type_id`, RAND() FROM prices WHERE MOD( (TIME_TO_SEC( `timestamp`) - TIME_TO_SEC(CONCAT(DATE_SUB(CURDATE(), INTERVAL 64 DAY), ' 00:00:00')) ), 45 *60 ) = 0 AND `timestamp` > CONCAT(DATE_SUB(CURDATE(), INTERVAL 64 DAY), ' 00:00:00');

# Insert first row into quantities, start timestamp is 64 days ago, end timestamp is start timestamp plus 15 minutes

INSERT INTO `quantities` (`id`, `start_timestamp`, `end_timestamp`, `quantity`) VALUES (NULL, DATE_SUB(CURDATE(), INTERVAL 64 DAY), DATE_SUB(CURDATE(), INTERVAL '63 23:45' DAY_MINUTE), RAND());

# Fill the quantities table with a record for each 15 minute period for the next 64 days

INSERT INTO `quantities` (`start_timestamp`, `end_timestamp`, `quantity`) SELECT DATE_ADD(`start_timestamp`, INTERVAL 32 DAY), DATE_ADD(`end_timestamp`, INTERVAL 32 DAY), RAND() FROM quantities;
INSERT INTO `quantities` (`start_timestamp`, `end_timestamp`, `quantity`) SELECT DATE_ADD(`start_timestamp`, INTERVAL 16 DAY), DATE_ADD(`end_timestamp`, INTERVAL 16 DAY), RAND() FROM quantities;
INSERT INTO `quantities` (`start_timestamp`, `end_timestamp`, `quantity`) SELECT DATE_ADD(`start_timestamp`, INTERVAL 8 DAY), DATE_ADD(`end_timestamp`, INTERVAL 8 DAY), RAND() FROM quantities;
INSERT INTO `quantities` (`start_timestamp`, `end_timestamp`, `quantity`) SELECT DATE_ADD(`start_timestamp`, INTERVAL 4 DAY), DATE_ADD(`end_timestamp`, INTERVAL 4 DAY), RAND() FROM quantities;
INSERT INTO `quantities` (`start_timestamp`, `end_timestamp`, `quantity`) SELECT DATE_ADD(`start_timestamp`, INTERVAL 2 DAY), DATE_ADD(`end_timestamp`, INTERVAL 2 DAY), RAND() FROM quantities;
INSERT INTO `quantities` (`start_timestamp`, `end_timestamp`, `quantity`) SELECT DATE_ADD(`start_timestamp`, INTERVAL 1 DAY), DATE_ADD(`end_timestamp`, INTERVAL 1 DAY), RAND() FROM quantities;
INSERT INTO `quantities` (`start_timestamp`, `end_timestamp`, `quantity`) SELECT DATE_ADD(`start_timestamp`, INTERVAL 12 HOUR), DATE_ADD(`end_timestamp`, INTERVAL 12 HOUR), RAND() FROM quantities;
INSERT INTO `quantities` (`start_timestamp`, `end_timestamp`, `quantity`) SELECT DATE_ADD(`start_timestamp`, INTERVAL 6 HOUR), DATE_ADD(`end_timestamp`, INTERVAL 6 HOUR), RAND() FROM quantities;
INSERT INTO `quantities` (`start_timestamp`, `end_timestamp`, `quantity`) SELECT DATE_ADD(`start_timestamp`, INTERVAL 3 HOUR), DATE_ADD(`end_timestamp`, INTERVAL 3 HOUR), RAND() FROM quantities;
INSERT INTO `quantities` (`start_timestamp`, `end_timestamp`, `quantity`) SELECT DATE_ADD(`start_timestamp`, INTERVAL 90 MINUTE), DATE_ADD(`end_timestamp`, INTERVAL 90 MINUTE), RAND() FROM quantities;
INSERT INTO `quantities` (`start_timestamp`, `end_timestamp`, `quantity`) SELECT DATE_ADD(`start_timestamp`, INTERVAL 45 MINUTE), DATE_ADD(`end_timestamp`, INTERVAL 45 MINUTE), RAND() FROM quantities;
INSERT INTO `quantities` (`start_timestamp`, `end_timestamp`, `quantity`) SELECT DATE_ADD(`start_timestamp`, INTERVAL 15 MINUTE), DATE_ADD(`end_timestamp`, INTERVAL 15 MINUTE), RAND() FROM quantities;
INSERT INTO quantities (`start_timestamp`, `end_timestamp`, `quantity`) SELECT DATE_SUB(`start_timestamp`, INTERVAL 15 MINUTE), DATE_SUB(`end_timestamp`, INTERVAL 15 MINUTE), RAND() FROM quantities WHERE MOD( (TIME_TO_SEC( `start_timestamp`) - TIME_TO_SEC(CONCAT(DATE_SUB(CURDATE(), INTERVAL 64 DAY), ' 00:00:00')) ), 45 * 60 ) = 0 AND `start_timestamp` > CONCAT(DATE_SUB(CURDATE(), INTERVAL 64 DAY), ' 00:00:00');

【问题讨论】:

  • 提高性能的唯一方法是将子查询重写为JOIN。
  • 我认为这也是 JochenJung 在下面推荐的内容,但我们无法让它生成正确的结果,而且仍然需要相同的时间?你有什么想法 Naktibalda 吗?
  • @Naktibalda,这不是真的;我们在这里谈论 mysql - 在某些情况下,例如上述情况,您必须通过故意进行子查询(不相关......我会给出答案)来帮助查询规划器

标签: mysql mysql-dependent-subquery


【解决方案1】:

这是我的第一次尝试。 这个很脏,并且在数据上使用以下属性:

  • 每个季度的数量有三个 5 分钟价格(如果在数据中违反了这一点,则查询将不起作用)
  • 注意每个和三个的基数,数据完整性检查不能保证这一点,因此我称之为脏
  • 对周期变化也不灵活

查询 1:

SELECT sql_no_cache
    min(q.start_timestamp) as start,  
    max(q.end_timestamp) as end, 
    sum((p1.price + p2.price + p3.price)/3*q.quantity) as total 
FROM 
    quantities q join 
    prices p1 on q.start_timestamp = p1.timestamp and p1.type_id = 1 join 
    prices p2 on p2.timestamp = adddate(q.start_timestamp, interval 5 minute) and p2.type_id = 1 join 
    prices p3 on p3.timestamp = adddate(q.start_timestamp, interval 10 minute) and p3.type_id = 1 
WHERE 
    q.start_timestamp between '2010-07-01 00:00:00' and '2010-07-01 23:59:59' 
GROUP BY hour(q.start_timestamp);

这个在我的慢速测试机器上在 0.01 秒内返回结果,原始查询在 ~6 秒内运行,而 gnarf 的查询在 ~0.85 秒内运行(所有查询始终使用 SQL_NO_CACHE 关键字进行测试,不会重用结果,但是在温暖的数据库上)。

编辑: 这是一个对价格方面的缺失行不敏感的版本 查询 1a

SELECT sql_no_cache
    min(q.start_timestamp) as start,  
    max(q.end_timestamp) as end, 
    sum( ( COALESCE(p1.price,0) + COALESCE(p2.price,0) + COALESCE(p3.price,0) ) / ( 
         3 -
         COALESCE(p1.price-p1.price,1) - 
         COALESCE(p2.price-p2.price,1) - 
         COALESCE(p3.price-p3.price,1)
        )
       *q.quantity) as total 
FROM 
    quantities q LEFT JOIN 
    prices p1 on q.start_timestamp = p1.timestamp and p1.type_id = 1 LEFT JOIN
    prices p2 on p2.timestamp = adddate(q.start_timestamp, interval 5 minute) and p2.type_id = 1 LEFT JOIN
    prices p3 on p3.timestamp = adddate(q.start_timestamp, interval 10 minute) and p3.type_id = 1 
WHERE 
    q.start_timestamp between '2010-07-01 00:00:00' and '2010-07-01 23:59:59' 
GROUP BY hour(q.start_timestamp);

编辑2: 查询 2: 这是对您的查询的直接改进和不同的方法,只需进行最小的更改,使我的机器上的执行时间约为 0.22 秒

SELECT sql_no_cache
MIN( `quantities`.`start_timestamp` ) AS `start`,
MAX( `quantities`.`end_timestamp` ) AS `end`,
SUM( `quantities`.`quantity` * (
  SELECT AVG( `prices`.`price` )
  FROM `prices`
  WHERE 
    `prices`.`timestamp` >= '2010-07-01 00:00:00' 
    AND `prices`.`timestamp` < '2010-07-02 00:00:00' 
    AND `prices`.`timestamp` >= `quantities`.`start_timestamp`
    AND `prices`.`timestamp` < `quantities`.`end_timestamp`
    AND `prices`.`type_id` = 1
) ) AS total
FROM `quantities`
WHERE `quantities`.`start_timestamp` >= '2010-07-01 00:00:00'
AND `quantities`.`start_timestamp` < '2010-07-02 00:00:00'
GROUP BY HOUR(  `quantities`.`start_timestamp` );

那是 mysql 5.1,我想我已经读过,在 5.5 中,查询计划器可以使用这种东西(合并索引)。此外,如果您可以通过外键使您的 start_timestamp 和时间戳相关联,这应该允许此类相关查询使用索引(但为此您需要修改设计并建立某种时间线表,然后可以引用按数量和价格计算)。

查询 3: 最后,最后一个版本在 ~0.03 秒内完成,但应该像查询 2 一样健壮和灵活

SELECT sql_no_cache
MIN(start),
MAX(end),
SUM(subtotal)
FROM 
(
SELECT sql_no_cache
q.`start_timestamp` AS `start`,
q.`end_timestamp` AS `end`,
AVG(p.`price` * q.`quantity`) AS `subtotal`
FROM `quantities` q
LEFT JOIN `prices` p ON p.timestamp >= q.start_timestamp AND 
                        p.timestamp < q.end_timestamp AND
                        p.timestamp >= '2010-07-01 00:00:00' AND 
                        p.`timestamp` < '2010-07-02 00:00:00' 
WHERE q.`start_timestamp` >= '2010-07-01 00:00:00' 
AND q.`start_timestamp` < '2010-07-02 00:00:00'
AND p.type_id = 1
GROUP BY q.`start_timestamp`
) forced_tmp
GROUP BY hour( start );

注意:不要忘记在生产环境中删除 sql_no_cache 关键字。

在上述查询中应用了许多反直觉的技巧(有时连接条件中重复的条件会加快查询速度,有时会减慢查询速度)。 Mysql 是一个很棒的小型 RDBMS,当涉及到相对简单的查询时非常快,但是当复杂性增加时,很容易遇到上述情况。

所以总的来说,我应用以下原则来设定我对查询性能的期望:

  • 如果基本结果集的行数

在这种特殊情况下,您从不到 1000 行开始(一天内的所有价格和数量,精度为 15 分钟),然后您应该能够计算最终结果。

【讨论】:

  • 你是个传奇,非常感谢。查询 2 在 0.0039 秒内返回完美结果,查询 3 也在 0.1655 秒内返回完美结果
  • @neilcrookes,不客气。您能否确认查询 2 在您的计算机上运行得比查询 3 快? (最初有未标记的 Query 1A,我现在正确标记了。另外,您应该允许 DB 蠕虫索引,我通常使用 sql_no_cache 运行查询几次以进行基准测试)。
  • (不能再编辑第一个评论,所以创建一个新的),你是一个传奇,非常感谢。查询 1a 在 0.0039 秒内返回完美结果,查询 2 也在 0.1655 秒内返回完美结果。查询 3 遇到与@gnarf 的查询相同的问题,因为它不返回该小时内没有价格的行,并且开始和结束时间对应于该小时内最早和最新的价格记录,但在 0.0144 秒内返回.查询 1a 是赢家。再次感谢。你是一个救生员。
  • @neilcrookes,只是为了完整起见 - 我在查询 3 中有一个错误,LEFT JOIN 服务器如果稍后要测试 p.type_id = 1 则没有任何目的,因为它会过滤掉带有 NULL 的行(即为什么它会删除这些行)。但是,通过输入 (p.type_id = 1 OR p.type_id IS NULL) 来纠正此错误会使查询速度减慢到 ~0.2 秒。不会编辑答案。
【解决方案2】:

这应该返回相同的结果并且执行速度稍快:

SELECT
  MIN( `quantities`.`start_timestamp` ) AS `start`,
  MAX( `quantities`.`end_timestamp` ) AS `end`,
  SUM( `quantities`.`quantity` * `prices`.`price` ) 
   * COUNT(DISTINCT `quantities`.`id`) 
   / COUNT(DISTINCT `prices`.`id`)
    AS total
FROM `quantities`
JOIN `prices` ON `prices`.`timestamp` >= `quantities`.`start_timestamp`
  AND `prices`.`timestamp` < `quantities`.`end_timestamp`
  AND `prices`.`type_id` = 1
WHERE `quantities`.`start_timestamp` >= '2010-07-01 00:00:00'
  AND `quantities`.`start_timestamp` < '2010-07-02 00:00:00'
GROUP BY HOUR(  `quantities`.`start_timestamp` );

由于您无法在SUM() 中计算AVG(),我不得不做一些有趣的COUNT(DISTINCT) 来计算每个quantities 返回的prices 的数量。我想知道这是否会给您与“真实”数据相同的结果...

使用JOIN

+----+-------------+------------+-------+------ -------------------------+-----------------+------ ---+--------+--------+----------+-------- --------------------------+
|编号 |选择类型 |表|类型 |可能的键 |关键 | key_len |参考 |行 |过滤 |额外 |
+----+-------------+------------+-------+--------- ----------------------+------+---------- +------+--------+----------+----------- ------------------------------------+
| 1 |简单 |数量 |范围 |开始时间戳,结束时间戳 |开始时间戳 | 8 |空 | 89 | 100.00 |使用哪里;使用临时的;使用文件排序 |
| 1 |简单 |价格 |全部 |时间戳,type_id |空 |空 |空 | 36862 | 62.20 |使用哪里;使用连接缓冲区 |
+----+-------------+------------+-------+--------- ----------------------+------+---------- +------+--------+----------+----------- ------------------------------------+

对比相同的查询仅将LEFT 添加到JOIN

+----+-------------+------------+-------+------ -------------+------+---------+--------+ --------+----------+-------------------------------- ---------------+
|编号 |选择类型 |表|类型 |可能的键 |关键 | key_len |参考 |行 |过滤 |额外 |
+----+-------------+------------+-------+--------- ----------+-----------------+---------+--------+--- ----+----------+---------------------------------- ------------+
| 1 |简单 |数量 |范围 |开始时间戳 |开始时间戳 | 8 |空 | 89 | 100.00 |使用哪里;使用临时的;使用文件排序 |
| 1 |简单 |价格 |参考 |时间戳,type_id | type_id | 4 |常量 | 22930 | 100.00 | |
+----+-------------+------------+-------+--------- ----------+-----------------+---------+--------+--- ----+----------+---------------------------------- ------------+

有趣的是LEFT 可以完全删除end_timestamp 作为可能的键,并且对选定的键进行了如此多的更改,使其花费了15倍的时间...

如果您想查看为您的 JOINS 指定索引提示,This reference page 可以为您提供更多帮助

【讨论】:

  • +1 这很好,在 (start_timestamp, end_timestamp) 和 (type_id, timestamp) 上添加复合索引也会有所帮助。但是,我想我可以将它降低到 ~0.01 秒
  • @Unreason --- scratches head 你说 +1,但还没有人投票 ;) --- &lt;/impatience&gt; --- 我有兴趣看看如何你得到它那么远!
  • 感谢 gnarf,这几乎是正确的。它在我的机器上运行了大约 0.4 秒,但结果与我原来的查询不同。我认为的原因是因为您要除以 COUNT(prices.price),它带有 GROUP 子句并且此数据将是 4 个数量行 * 3 个价格行 = 12,但是如果您除以 3,那么它会生成结果与我原来的查询相同。问题是我不想在查询中硬编码 3,但我无法弄清楚 SQL 是什么来从数据中导出该值。一旦那部分被分类,它就会很完美。有什么想法非常感谢?
  • @neilcrookes - 查询现在给我与测试数据相同的结果......但这并不意味着它的计算相同......事实上,如果你只删除一个价格时间范围内,计算会从您的查询中返回不同的结果。这个答案不太管用......
  • @gnarf,使用你的最新查询,我测试了它,删除了整个小时的连续价格,45 分钟,30 分钟,15 分钟和 5 分钟,我得到了几乎相同的结果。不同之处在于,在我原来的版本中,我确实得到了一小时的一行,没有任何价格,总共为 NULL,但我没有从您的查询中得到一行。同样在您的查询中,MIN 和 MAX 开始和结束时间戳是我仍然有价格的时间戳。然而,除了我只删除一个价格的那一小时之外,大多数总数都匹配 - 在这种情况下,差异非常小。
【解决方案3】:

请记住,仅仅因为您的列上有索引并不一定意味着它们会运行得更快。就目前而言,为每个单独的列创建索引,如果您只限制一列上的数据,则返回结果会非常快。

所以要尽量避免“使用文件排序”(您需要尽可能多地这样做),不妨试试以下索引:

CREATE INDEX start_timestamp_end_timestamp_id ON quantities (start_timestamp,end_timestamp,id);

价格表也有类似的东西(将您拥有的 3 个单独的索引组合成 1 个索引以加快查找速度)

非常详细地解释它以及如何优化您的索引(以及不同的解释的含义和目标)的优秀资源是:http://hackmysql.com/case1

【讨论】:

  • 感谢 AcidRaZor,但是在价格表中添加这个索引和一个索引并没有提高我的原始查询或 @gnarf 建议的查询的性能。
  • 值得一试 :) 但是,我仍然建议您阅读我引用的网站。他们更详细地介绍了如何通过查询提高性能
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-04-08
  • 1970-01-01
  • 1970-01-01
  • 2011-02-07
  • 2015-07-11
  • 1970-01-01
相关资源
最近更新 更多