使用 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 table: budget_item_id, family_id(也试过 payment_date)
budget_lines table: family_id, budget_id, budget_item_id
如何提高速度?查询或可能 mysql (8) 配置。
balance_historiestable:
budget_linestable:
我会以与您所拥有的相反的方式开始此查询。假设您可能拥有多年的数据,但您的 EXISTS 查询正在更具体地查看 date-range 或特定预算行,从那里开始,它可能会小得多。一旦您拥有不同的 ID,然后返回到符合条件的 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.但是,这将遍历每条记录,如果您有数百万行,这就是您要扫描的内容。只有在扫描完每一行之后,您现在才针对特定日期范围或 family/budget.
的历史记录进行二次查询(对于符合条件的每条记录)
我的猜测是,从您的两个 EXISTS 查询返回的可能记录数将会非常小。因此,首先获取属于该联合的那些 ID 的 DISTINCT 列表将是非常小的子集。一旦找到单个“ID”,它现在将直接匹配预算项目 table 并具有类别 ID/系列/自定义项目注意事项的最终过滤限制。
通过使索引更好地匹配查询的上下文,WHERE 子句将优化提取数据。我已经用类似的解决方案回答了其他几个问题,并澄清了索引以及为什么在那些... , and another here.
我有这个查询 (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 table: budget_item_id, family_id(也试过 payment_date)
budget_lines table: family_id, budget_id, budget_item_id
如何提高速度?查询或可能 mysql (8) 配置。
balance_historiestable:
budget_linestable:
我会以与您所拥有的相反的方式开始此查询。假设您可能拥有多年的数据,但您的 EXISTS 查询正在更具体地查看 date-range 或特定预算行,从那里开始,它可能会小得多。一旦您拥有不同的 ID,然后返回到符合条件的 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.但是,这将遍历每条记录,如果您有数百万行,这就是您要扫描的内容。只有在扫描完每一行之后,您现在才针对特定日期范围或 family/budget.
的历史记录进行二次查询(对于符合条件的每条记录)我的猜测是,从您的两个 EXISTS 查询返回的可能记录数将会非常小。因此,首先获取属于该联合的那些 ID 的 DISTINCT 列表将是非常小的子集。一旦找到单个“ID”,它现在将直接匹配预算项目 table 并具有类别 ID/系列/自定义项目注意事项的最终过滤限制。
通过使索引更好地匹配查询的上下文,WHERE 子句将优化提取数据。我已经用类似的解决方案回答了其他几个问题,并澄清了索引以及为什么在那些...