簡體   English   中英

使用 2 個 EXISTS 子查詢改進 sql 查詢

[英]improve sql query with 2 EXISTS sub queries

我有這個查詢(mysql):

 SELECT `budget_items`.*
    FROM `budget_items`
    WHERE (budget_category_id = 4
           AND ((is_custom_for_family = 0)
                OR (is_custom_for_family = 1
                    AND custom_item_family_id = 999))
           AND ((EXISTS
                   (SELECT 1
                    FROM balance_histories
                    WHERE balance_histories.budget_item_id = budget_items.id
                      AND balance_histories.family_id = 999
                      AND payment_date >= '2021-02-01'
                      AND payment_date <= '2021-02-28' ))
                OR (EXISTS
                      (SELECT 1
                       FROM budget_lines
                       WHERE family_id = 999
                         AND budget_id = 188311
                         AND budget_item_id = budget_items.id
                         AND amount > 0))))

它在應用程序啟動時運行多次。 需要超過 10 秒(全部)。

我有索引:

balance_histories 表:budget_item_id,family_id(也嘗試過 payment_date)

budget_lines 表:family_id、budget_id、budget_item_id

我怎樣才能提高速度? 查詢或可能是 mysql (8) 配置。

balance_histories表: 在此處輸入圖像描述

budget_lines 表: 在此處輸入圖像描述

我會以與你所擁有的相反的方式開始這個查詢。 假設您可能擁有多年的數據,但您的 EXISTS 查詢正在更具體地查看日期范圍或特定預算線,從那里開始,它可能會小得多。 一旦您擁有 DISTINCT ID,然后 go 通過合格 ID 加上附加條件回到預算項目。

為了幫助優化查詢,我會在

table              index
balance_histories  ( family_id, payment_date, budget_item_id )
budget_lines       ( family_id, budget_id, amount )
budget_items       ( id, budget_category_id, is_custom_for_family, custom_item_family_id )


select
        bi.*
    from
        -- pre-query a list of DISTINCT IDs from the balance history
        -- and budget lines that qualify. THEN join to the rest.
        ( select distinct
                bh.budget_item_id id
            from
                balance_histories bh
            where
                    bh.family_id = 999
                AND bh.payment_date >= '2021-02-01'
                AND bh.payment_date <= '2021-02-28'
        UNION
        select 
                bl.budget_item_id
            FROM 
                budget_lines bl
            WHERE 
                    bl.family_id = 999
                AND bl.budget_id = 188311
                AND bl.amount > 0 ) PQ
            JOIN budget_items bi
                on PQ.id = bi.id
                AND bi.budget_category_id = 4
                AND (       bi.is_custom_for_family = 0
                        OR 
                            (   bi.is_custom_for_family = 1
                            AND bi.custom_item_family_id = 999 )
                    )

反饋

對於許多 SQL 查詢,通常有多種方法可以獲得解決方案。 有時使用 EXISTS 效果很好,有時效果不佳。 您需要考慮數據的基數,這就是我的目標。 首先看看您要的是什么:獲取所有類別的預算項目和家庭的自定義項目是 1 或 0(全部),但如果是家庭,則只有 999 的預算項目。您對 AND/OR 的平衡是正確的. 但是,這將遍歷每條記錄,如果您有數百萬行,這就是您要掃描的內容。 只有在掃描完每一行之后,您現在才針對特定日期范圍或家庭/預算的歷史記錄進行二次查詢(針對每條符合條件的記錄)。

我的猜測是,從您的兩個 EXISTS 查詢返回的可能記錄數將會非常小。 因此,首先獲取屬於該聯合的那些 ID 的 DISTINCT 列表將是非常小的子集。 一旦找到單個“ID”,它現在將直接匹配預算項目表,並具有類別 ID/系列/自定義項目注意事項的最終過濾限制。

通過讓索引更好地匹配查詢的上下文,WHERE 子句將優化提取數據。 我已經用類似的解決方案回答了其他幾個問題,並闡明了索引以及為什么在這些問題中...... 看看例如這里還有另一個。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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