简体   繁体   English

Mysql子选择很慢

[英]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-012020-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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM