[英]Mysql sub select is very slow
I have a table like this :我有一张这样的桌子:
-------------------------------
| id | valid_until | username|
-------------------------------
| 1 | 2020-01-01 | user1 |
-------------------------------
| 1 | 2020-01-01 | user2 |
-------------------------------
| 1 | 2020-01-02 | user3 |
-------------------------------
| 1 | 2020-01-02 | user4 |
-------------------------------
| 1 | 2020-01-03 | user5 |
-------------------------------
| 1 | 2020-01-03 | user6 |
-------------------------------
| 1 | 2020-01-03 | user7 |
-------------------------------
This is the user subscription table, valid_until
says when the subscription will end up.这是用户订阅表,
valid_until
表示订阅何时结束。
I want to know active subscription in each day, So I have a range, for example, 2020-01-01
TO 2020-01-03
and here is my query :我想知道每天的活跃订阅,所以我有一个范围,例如,
2020-01-01
到2020-01-03
,这是我的查询:
SELECT
valid_until qva,
(SELECT
COUNT(iod.id) AS ct
FROM
`order_detail` iod
WHERE
valid_until > qva)
FROM
`order_detail`
WHERE
valid_until >= '2020-01-01'
AND valid_until <= '2020-01-03'
GROUP BY qva
But this query is too slow, What is the problem with my query?但是这个查询太慢了,我的查询有什么问题? Response time (230 sec)
响应时间(230 秒)
Your query is invalid according to the SQL standard.根据 SQL 标准,您的查询无效。 You have
GROUP BY qva
(and using the alias name here is usually not allowed, but in MySQL and MariaDB it is), but you don't apply an aggregate function on the subquery result.您有
GROUP BY qva
(通常不允许在此处使用别名,但在 MySQL 和 MariaDB 中是),但您没有GROUP BY qva
查询结果应用聚合函数。 MySQL is known for violating the standard here and they silently apply ANY_VALUE
on such unaggregated expressions. MySQL 以违反此处的标准而闻名,它们默默地将
ANY_VALUE
应用于此类未ANY_VALUE
表达式。
This makes the query slow.这会使查询变慢。 For each order in the date range the count is evaluated, only to pick one of those counts per date at the very end.
对于日期范围内的每个订单,都会评估计数,仅在最后每个日期选择这些计数之一。
So, let's build the query up from scratch.因此,让我们从头开始构建查询。 You want one result row for each day in the given date range.
您希望给定日期范围内的每一天都有一个结果行。 That is:
那是:
select distinct valid_until
where valid_until between date '2020-01-01' and date '2020-01-03'
from order_detail;
Then, for each of these dates you want to get the count.然后,对于这些日期中的每一个,您都希望获得计数。 You can do this in a subquery as in your original query.
您可以像在原始查询中一样在子查询中执行此操作。 I suppose it must be
>=
instead of >
though, as an order valid until a day is still valid that day (or at least this is what I'd expect).我想它必须是
>=
而不是>
,因为订单在当天仍然有效之前有效(或者至少这是我所期望的)。
SELECT
dates.day,
(
select count(*)
from order_detail od
where od.valid_until >= dates.day
) as ct
FROM
(
select distinct valid_until as day
where valid_until between date '2020-01-01' and date '2020-01-03'
from order_detail
) dates
order by dates.day;
(This assumes that valid_until
is a mere date. If it's a datetime, you'll have to adjust this query a little.) (这假设
valid_until
只是一个日期。如果它是一个日期时间,你将不得不稍微调整这个查询。)
UPDATE:更新:
As ysth told you in the request comments, your query will get you only days in the given range that exist as valid_until
in your table.正如 ysth 在请求评论中告诉您的那样,您的查询只会为您提供给定范围
valid_until
表中作为valid_until
存在的天数。 So does mine.我的也是。 If you wanted the three days regardless, you'd have to replace the subquery and make it independent from the table, eg
如果您无论如何都想要三天,则必须替换子查询并使其独立于表,例如
FROM
(
select date '2020-01-01' as day union all
select date '2020-01-02' as day union all
select date '2020-01-03' as day
) dates
I think you just want a cumulative count of valid_until
from the latest date to the earliest:我认为您只想要从最新日期到最早的
valid_until
累计计数:
select valid_until, count(*) day_count,
sum(count(*)) over (order by valid_until desc) as count_as_of_day
from order_details od
group by valid_until;
If you want this for a range of dates, then use a subquery:如果你想要这个范围的日期,那么使用子查询:
select od.*
from (select valid_until, count(*) day_count,
sum(count(*)) over (order by valid_until desc) as count_as_of_day
from order_details od
group by valid_until
) od
where valid_until >= :from and valid_until <= :to
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.