在存储销售订单的MySQL数据库表格中,我有一个LastReviewed
列,其中包含修改销售订单的最后日期和时间(类型timestamp
,默认值CURRENT_TIMESTAMP
)。我想绘制特定用户每天(最近90天)修改的销售数量。
我正在尝试制作一个SELECT
,它返回自LastReviewed
日期以来的天数,以及有多少记录属于该范围。以下是我的查询,其工作正常:
SELECT DATEDIFF(CURDATE(), LastReviewed) AS days, COUNT(*) AS number FROM sales
WHERE UserID=123 AND DATEDIFF(CURDATE(),LastReviewed)<=90
GROUP BY days
ORDER BY days ASC
请注意,我为每条记录多次计算DATEDIFF()
以及CURDATE()
。这似乎真的无效,所以我想知道如何重用以前计算的结果。我尝试的第一件事是:
SELECT DATEDIFF(CURDATE(), LastReviewed) AS days, COUNT(*) AS number FROM sales
WHERE UserID=123 AND days<=90
GROUP BY days
ORDER BY days ASC
错误:Unknown column 'days' in 'where clause'
。所以我开始环顾网络。根据另一个讨论(Can I reuse a calculated field in a SELECT query?),我接下来尝试了以下内容:
SELECT DATEDIFF(CURDATE(), LastReviewed) AS days, COUNT(*) AS number FROM sales
WHERE UserID=123 AND (SELECT days)<=90
GROUP BY days
ORDER BY days ASC
错误:Unknown column 'days' in 'field list'
。我也尝试了以下内容:
SELECT @days := DATEDIFF(CURDATE(), LastReviewed) AS days,
COUNT(*) AS number FROM sales
WHERE UserID=123 AND @days <=90
GROUP BY days
ORDER BY days ASC
查询返回零结果,因此@days<=90
似乎返回false
,即使我将它放在SELECT
子句中并删除WHERE
子句,我也能看到@days
值低于90的某些结果。
我通过使用子查询得到了一些工作:
SELECT * FROM (
SELECT DATEDIFF(CURDATE(),LastReviewed) AS sales ,
COUNT(*) AS number FROM sales
WHERE UserID=123
GROUP BY days
) AS t
WHERE days<=90
ORDER BY days ASC
但我不知道这是否是最有效的方式。更不用说即使这个解决方案每个记录计算一次CURDATE()
,即使它的值从查询的开始到结尾都是相同的。这不是浪费吗?我是否想过这个?欢迎提供帮助。
注意:Mods,它应该在CodeReview上吗?我在这里发布是因为我试图使用的代码实际上不起作用
答案 0 :(得分:1)
您的问题实际上有两个问题。
首先,您忽略了WHERE
在SELECT
之前的事实。当服务器评估WHERE <expression>
时,它已经知道为评估<expression>
而完成的计算的值,并且可以将其用于SELECT
。
相反,你应该使用它:
WHERE LastReviewed < DATE_SUB(CURDATE(), INTERVAL 90 DAY)
优化器会看到这一点并让所有人兴奋,因为DATE_SUB(CURDATE(), INTERVAL 90 DAY)
可以解析为常量,可以在<
比较的一侧使用,这意味着如果存在一个索引LastReviewed
作为最左边的相关列,然后服务器可以使用索引立即消除具有常量值的LastReviewed >=
的所有行。
然后DATEDIFF(CURDATE(), LastReviewed) AS days
(SELECT
仍然需要)只会根据我们已经知道的行进行评估。
在(UserID,LastReviewed)上添加一个索引,服务器将能够非常快速地精确定位相关行。
答案 1 :(得分:1)
内置函数比获取行要便宜得多。
使用以下“复合”索引可以提高性能:
INDEX(UserID, LastReviewed)
和更改为
WHERE UserID=123
AND LastReviewed >= CURRENT_DATE() - INTERVAL 90 DAY
您的公式在函数调用中“隐藏”LastRevieded
,使其在索引中无法使用。
如果您仍然对此改进不满意,请考虑每晚查询计算昨天的统计数据并将其放入“汇总表”中。从那里开始,你提到的SELECT
可以更快地运行。