[英]How to optimize select query with case statements?
我有 3 個表,超過 1,000,000 條記錄。 我的選擇查詢運行了幾個小時。 如何優化它? 我是新手。
我嘗試為name
添加索引,但加載仍然需要數小時。
像這樣,
ALTER TABLE table2 ADD INDEX(name);
也像這樣,
CREATE INDEX INDEX1 table2(name);
SELECT MS.*, P.Counts FROM
(SELECT M.*,
TIMESTAMPDIFF(YEAR, M.date, CURDATE()) AS age,
CASE V.name
WHEN 'text' THEN M.name
WHEN V.name IS NULL THEN M.name
ELSE V.name
END col1
FROM table1 M
LEFT JOIN table2 V ON M.id=V.id) AS MS
LEFT JOIN
(select E.id, count(E.id) Counts
from table3 E
where E.field2 = 'value1'
group by E.id) AS P
ON MS.id=P.id;
Explain <above query>;
輸出:
+----+-------------+------------+------------+-------+---------------------------------------------+------------------+---------+------------------------+---------+----------+-----------------------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+------------+------------+-------+---------------------------------------------+------------------+---------+------------------------+---------+----------+-----------------------------------------------------------------+
| 1 | PRIMARY | M | NULL | ALL | NULL | NULL | NULL | NULL | 344763 | 100.00 | NULL |
| 1 | PRIMARY | <derived3> | NULL | ref | <auto_key0> | <auto_key0> | 8 | CP.M.id | 10 | 100.00 | NULL |
| 1 | PRIMARY | V | NULL | index | NULL | INDEX1 | 411 | NULL | 1411083 | 100.00 | Using where; Using index; Using join buffer (Block Nested Loop) |
| 3 | DERIVED | E | NULL | ref | PRIMARY,f2,f3 | f2| 43 | const | 966442 | 100.00 | Using index |
+----+-------------+------------+------------+-------+---------------------------------------------+------------------+---------+------------------------+---------+----------+-----------------------------------------------------------------+
我希望在不到 1 分鍾的時間內得到結果。
為清楚起見,查詢縮進。
SELECT MS.*, P.Counts
FROM (
SELECT M.*,
TIMESTAMPDIFF(YEAR, M.date, CURDATE()) AS age,
CASE V.name
WHEN 'text' THEN M.name
WHEN V.name IS NULL THEN M.name
ELSE V.name
END col1
FROM table1 M
LEFT JOIN table2 V ON M.id=V.id
) AS MS
LEFT JOIN (
select E.id, count(E.id) Counts
from table3 E
where E.field2 = 'value1'
group by E.id
) AS P ON MS.id=P.id;
您的查詢沒有過濾謂詞,因此它本質上是檢索所有行。 這是table1
的 1,000,000+ 行。 然后將它與table2
連接,然后與另一個表表達式/派生表連接。
為什么你希望這個查詢很快? 像這樣的大規模查詢通常會在晚上作為批處理運行。 我認為此查詢不適用於在線流程,對嗎?
也許你需要重新考慮這個過程。 您真的需要以交互方式一次處理數百萬行嗎? 用戶會閱讀網頁中的一百萬行嗎?
對於初學者,如果 v.name 為 null 或 v.name != 'text',您將返回相同的結果 'col1'。 也就是說,您可以在加入 table2 並使用 IFNULL 函數時包含該額外條件。
您是否按 field2 過濾 table3,您可能可以在包含 field2 的表 3 上創建索引。
您還應該檢查是否可以為這些表中的任何一個包含任何其他過濾器,如果可以,您可以考慮使用存儲過程來獲取結果。
另外,我不明白為什么您需要將第一個連接聚合到“MS”中,您可以像這樣輕松地一次性完成所有連接:
SELECT
M.*,
TIMESTAMPDIFF(YEAR, M.date, CURDATE()) AS age,
IFNULL(V.name, M.name) as col1,
P.Counts
FROM table1 M
LEFT JOIN table2 V ON M.id=V.id AND V.name <> 'text'
LEFT JOIN
(SELECT
E.id,
COUNT(E.id) Counts
FROM table3 E
WHERE E.field2 = 'value1'
GROUP BY E.id) AS P ON M.id=P.id;
我還假設您確實對所有這三個表中的所有 id 字段都有聚集索引,但沒有過濾器,如果您要處理數百萬條記錄,這將始終是一個很大的繁重查詢。 至少可以說您正在對 table1 進行表掃描。
在您發表評論后,我已包含此附加信息。
我已經提到了聚集索引,但是根據這里關於索引的官方文檔
當您在表上定義 PRIMARY KEY 時,InnoDB 將其用作聚集索引。 因此,如果您已經定義了主鍵,則無需執行任何其他操作。 有文檔還指出您應該為您創建的每個表定義一個主鍵。
如果沒有主鍵。 這是您請求的代碼片段。
ALTER TABLE table1 ADD CONSTRAINT pk_table1
PRIMARY KEY CLUSTERED (id);
注意:請記住,創建聚簇索引是一項大操作,對於像您這樣具有數據色調的表。 在生產服務器上,這不是您沒有計划就想做的事情。 此操作也將花費很長時間,並且在此過程中表將被鎖定。
子查詢並不總是得到很好的優化。
我想你可以把它弄平,比如:
SELECT M.*, V.*,
TIMESTAMPDIFF(YEAR, M.date, CURDATE()) AS age,
CASE V.name WHEN 'text' THEN M.name
WHEN V.name IS NULL THEN M.name
ELSE V.name END col1,
( SELECT COUNT(*) FROM table3 WHERE field2 = 'value1' AND id = x.id
) AS Counts
FROM table1 AS M
LEFT JOIN table2 AS V ON M.id = V.id
我可能有些地方不太對; 看看你能不能讓這個公式起作用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.