[英]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-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
但是這個查詢太慢了,我的查詢有什么問題? 響應時間(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.