簡體   English   中英

如何使用 case 語句優化選擇查詢?

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

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM