mysql
  • indexing
  • explain
  • 2016-09-19 87 views 1 likes 
    1

    到目前爲止,以下是我的方案:MySQL的指數不工作(使用案例的具體情況)

    參數由用戶控制:(這些參數由儀表板控制,但是出於測試目的,我創建SQL參數,以便改變它們的值)

    SET @device_param := "all devices"; 
        SET @date_param_start_bar_chart := '2016-09-01'; 
        SET @date_param_end_bar_chart := '2016-09-19'; 
        SET @country_param := "US"; 
        SET @channel_param := "all channels"; 
    

    查詢運行在後端

    SELECT 
        country_code, 
        channel_report_tag, 
        SUM(count_more_then_30_min_play) AS '>30 minutes', 
        SUM(count_15_30_min_play) AS '15-30 Minutes', 
        SUM(count_0_15_min_play) AS '0-15 Minutes' 
    FROM 
        channel_play_times_cleaned 
    WHERE IFNULL(country_code, '') = 
        CASE 
        WHEN @country_param = "all countries" 
        THEN IFNULL(country_code, '') 
        ELSE @country_param 
        END 
        AND IFNULL(channel_report_tag, '') = 
        CASE 
        WHEN @channel_param = "all channels" 
        THEN IFNULL(channel_report_tag, '') 
        ELSE @channel_param 
        END 
        AND iFnull(device_report_tag, '') = 
        CASE 
        WHEN @device_param = "all devices" 
        THEN iFnull(device_report_tag, '') 
        ELSE @device_param 
        END 
        AND playing_date BETWEEN @date_param_start_bar_chart 
        AND @date_param_end_bar_chart 
    GROUP BY channel_report_tag 
    ORDER BY SUM(count_more_then_30_min_play) DESC 
    limit 10 ; 
    

    ,我已經應用的指數

    CREATE INDEX my_index 
    ON channel_play_times_cleaned (
        country_code, 
        channel_report_tag, 
        device_report_tag, 
        playing_date, 
        channel_report_tag 
    ) 
    

    我按照這個鏈接:My SQL Index Cook-Book Guide創建我的索引。

    但是,執行上述查詢時關鍵字EXPLAIN告訴我沒有使用索引。

    enter image description here

    我想我在做什麼錯在這裏?

    回答

    2
    1. 您可以在前3條件中使用函數和大小寫表達式。簡單的字段索引不能用於加速這種查找。

    2. MySQL可能會使用playing_date標準的索引,但該字段不是引用索引中最左邊的部分,因此引用的索引也不適用於該索引。

    如果我是你,我會從其中標準中刪除所述邏輯並移動到這一點通過構建,使得具有的情況下的條件解決,僅發射必要的SQL的SQL語句的應用層。

    +0

    你是對的。問題是我使用的是pentaho社區儀表板編輯器,它只允許我提供sql查詢作爲數據源,因此我不得不將這個邏輯應用於數據庫級別。有沒有其他方法可以實現相同的功能? –

    +0

    我沒有看到任何方式使用case表達式的條件索引,甚至沒有索引生成的列。你可以聲明一個索引來支持日期範圍過濾器和group by子句,所以MySQL可以使用索引來滿足一些標準。 – Shadow

    +0

    +1 @Shadow,但我認爲有一種方法可以用優化器可以使用的術語來表達謂詞,並使用適當的索引(儘管我認爲不僅僅需要一個索引,因此可以選擇適當的索引在計劃階段)。 –

    1

    WHERE子句中的CASE表達式強制執行全表掃描。顯然,他們必須去...但是如何?

    你必須像優化器一樣思考,並記住它的工作是儘可能地避免工作。

    考慮這個查詢:

    SELECT * FROM users 
    WHERE first_name LIKE '%a%'; 
    

    每一行都必須閱讀查找包含字母「A」的所有FIRST_NAME值。非常慢。

    現在,這一個:

    SELECT * FROM users 
    WHERE first_name LIKE '%a%' 
        AND 2 < 1; 
    

    對於每一行,你問的服務器再次檢查FIRST_NAME並只包括行,其中2比1

    是一個較小的數它慢,還是快?

    速度非常快,因爲優化器檢測到Impossible WHERE。掃描行是沒有意義的,因爲2 < 1總是錯誤的。

    現在,用這個邏輯來告訴你真的想要的優化:

    不是這個:

    WHERE IFNULL(country_code, '') = 
        CASE 
        WHEN @country_param = "all countries" 
        THEN IFNULL(country_code, '') 
        ELSE @country_param 
        END 
        AND 
    

    但這:

    WHERE 
        (
        (
         @country_param = "all countries" 
        ) 
        OR 
        (
         @country_param != "all countries" 
         AND 
         country_code = @country_param 
        ) 
    ) 
        AND ... 
    

    的差異應該是鮮明的。如果@country_param =「所有國家」,則不需要第二個測試,否則,只需要具有匹配國家/地區的行,並且根據定義,所有其他行的這部分WHERE子句都是錯誤的,從而允許country_param上的索引爲用過的。

    這些OR'ed表達式中的一個或另一個是總是錯誤,並且那個將被優化掉,提前 - 從未對每一行進行評估。表達式@country_param != "all countries"應該與表達式2 < 12 > 1的處理方式相同。它不會根據行中的數據改變其真實性,因此只需在開始時對其進行一次評估。

    重複其他CASE。您幾乎不應該將列作爲參數傳遞給WHERE子句中的函數,因爲優化程序不能「向後查看」函數並形成智能查詢計劃。

    0

    其他的答案解釋了爲什麼你的查詢很慢。我會解釋你應該做什麼。

    編寫代碼以「構建」查詢。如果用戶說「所有國家」,或者它加入AND country_code = "US",它將廢除country_code的測試。沒有@變數,沒有CASE等

    然後,一個5列索引將無法工作,除了少數情況。相反,感受用戶所要求的內容,然後構建一些2列索引來涵蓋流行的案例。

    +0

    這當然是更好的解決方案。我從評論中認爲,這是一個無法構建動態查詢的環境。 –

    相關問題