这是我的简化历史表结构:
id | property_id | price | created_at | updated_at | deleted_at
1 | 1 | 100 | 2016-04-10 01:00:00 | 2016-04-10 01:00:00 | NULL
2 | 1 | 300 | 2016-04-10 01:00:00 | 2016-04-10 01:00:00 | NULL
3 | 1 | 300 | 2016-04-10 02:00:00 | 2016-04-10 02:00:00 | NULL
4 | 2 | 200 | 2016-04-10 03:00:00 | 2016-04-10 03:00:00 | NULL
1 | 2 | 150 | 2016-04-10 04:00:00 | 2016-04-10 04:00:00 | NULL
我需要:
到目前为止,这是我的查询,但有点慢:
SELECT *
FROM `history` `t1`
WHERE `t1`.`created_at` >= '2016-04-13 00:00:00'
AND `t1`.`created_reason` = 'Scraped'
AND `t1`.`price` > -1
AND (SELECT `t2`.`price`
FROM `history` `t2`
WHERE `t2`.`property_id` = `t1`.`property_id`
AND `t2`.`created_at` < `t1`.`created_at`
AND `t2`.`price` > -1
ORDER BY DATE(`t2`.`created_at`) DESC
LIMIT 1
) <> `t1`.`price`
GROUP BY `t1`.`property_ad_id`
有关如何提高绩效的任何建议吗?
答案 0 :(得分:0)
这里有一些建议。
使用EXPLAIN获取查询执行计划。
正在为从t1返回的每个行执行相关子查询。这可能会大大降低性能。
相关子查询正在对函数的结果执行ORDER BY。这意味着MySQL必须为满足谓词的每一行计算该表达式,然后执行排序操作。 (使用LIMIT 1,MySQL可能不必对整个集合进行完全排序,但它确实需要至少执行一次传递才能获得第一行。)
因为DATE()函数正在消耗时间组件,如果t2中有多行满足其他谓词(早期created_at),相同的最新日期和不同时间,那么它们中的哪一个是不确定的行将首先排序。您可能会从04:00开始排队,而不是从17:00开始排队
建议:引用一个裸列,并确保有适当的索引。
建议:获得&#34;最大值&#34;创建于(有效使用索引)
建议:在前一行的48小时,72小时,一周内,你要在t2后面看一个下限?
建议:确保t1上的外部查询和t2上的子查询都有合适的索引。 (可能那些需要两个不同的索引)
作为第一次改进,一些小调整......
SELECT t1.*
FROM history t1
WHERE t1.created_at >= '2016-04-13 00:00:00'
AND t1.created_reason = 'Scraped'
AND t1.price > -1
AND t1.price <>
(
SELECT t2.price
FROM history t2
WHERE t2.property_id = t1.property_id
AND t2.created_at < t1.created_at
AND t2.created_at > t1.created_at + INTERVAL -30 DAY
AND t2.price > -1
ORDER BY t2.created_at DESC
LIMIT 1
)
GROUP BY t1.property_ad_id
最合适的指数可能是
... ON history(created_reason,created_at,property_id,price)
... ON history(property_id,created_at,price)
为了优化GROUP BY(避免排序操作),我们也可以考虑尝试使用前导列property_ad_id
的索引。这是一个很长的镜头,但可能值得一试,看看EXPLAIN是否显示它正在使用......
... ON history(property_ad_id,created_reason,created_at,property_id,price)