簡體   English   中英

Mysql子選擇很慢

[英]Mysql sub select is very slow

我有一張這樣的桌子:

-------------------------------
| 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  |
-------------------------------  

這是用戶訂閱表, valid_until表示訂閱何時結束。

我想知道每天的活躍訂閱,所以我有一個范圍,例如, 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

但是這個查詢太慢了,我的查詢有什么問題? 響應時間(230 秒)

根據 SQL 標准,您的查詢無效。 您有GROUP BY qva (通常不允許在此處使用別名,但在 MySQL 和 MariaDB 中是),但您沒有GROUP BY qva查詢結果應用聚合函數。 MySQL 以違反此處的標准而聞名,它們默默地將ANY_VALUE應用於此類未ANY_VALUE表達式。

這會使查詢變慢。 對於日期范圍內的每個訂單,都會評估計數,僅在最后每個日期選擇這些計數之一。

因此,讓我們從頭開始構建查詢。 您希望給定日期范圍內的每一天都有一個結果行。 那是:

select distinct valid_until
where valid_until between date '2020-01-01' and date '2020-01-03'
from order_detail;

然后,對於這些日期中的每一個,您都希望獲得計數。 您可以像在原始查詢中一樣在子查詢中執行此操作。 我想它必須是>=而不是> ,因為訂單在當天仍然有效之前有效(或者至少這是我所期望的)。

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;

(這假設valid_until只是一個日期。如果它是一個日期時間,你將不得不稍微調整這個查詢。)

更新:

正如 ysth 在請求評論中告訴您的那樣,您的查詢只會為您提供給定范圍valid_until表中作為valid_until存在的天數。 我的也是。 如果您無論如何都想要三天,則必須替換子查詢並使其獨立於表,例如

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

我認為您只想要從最新日期到最早的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;

如果你想要這個范圍的日期,那么使用子查詢:

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