【问题标题】:Optimize subquery in SELECT优化SELECT中的子查询
【发布时间】:2020-02-27 23:34:58
【问题描述】:

我的表schema如下:

索引:

  • products.id 主键
  • products.description 唯一
  • expenses.id 主键
  • expenses.product_id product.id 的外键

我的目标是加载

  1. 当月每种产品的成本(AS cost_november)
  2. 上个月每种产品的成本(AS cost_october)
  3. 当月成本与上月相比的变化
    (当月成本 - 上月成本)(AS 成本)
  4. 当月费用与上一月相比的百分比变化
    (上月费用 * 100 / 当月费用)(AS percent_diff)

我已经编写了完全符合此功能的 SQL:

SELECT description, (SUM(cost) - IFNULL(
(
    SELECT SUM(cost)
    FROM expenses
    WHERE month = 9 AND year = 2019 AND product_id = e.product_id
    GROUP BY product_id
), 0)) AS costs,

SUM(cost) * 100 / 
(
    SELECT SUM(cost)
    FROM expenses
    WHERE month = 9 AND year = 2019 AND product_id = e.product_id
    GROUP BY product_id
) AS percent_diff,

SUM(cost) AS costs_october,

IFNULL(
(
    SELECT SUM(cost)
    FROM expenses
    WHERE month = 9 AND year = 2019 AND product_id = e.product_id
    GROUP BY product_id
), 0) AS costs_september

FROM expenses e
JOIN products p ON (e.product_id = p.id)
WHERE month = 10 AND year = 2019
GROUP BY product_id
ORDER BY product_id;

但是将相同的子查询复制粘贴三次真的是解决方案吗?理论上,每个产品需要运行四个查询。有没有更优雅的方式?

感谢您的帮助!

【问题讨论】:

  • 您从未有过月内成本变化吗?
  • 请列出您目前拥有的索引。
  • @P.Salmon 费用/成本以后不会改变。

标签: mysql sql join query-optimization innodb


【解决方案1】:

您可以一次计算所有月份和所有产品:

SELECT year, month,
       SUM(costs) as curr_month_costs,
       LAG(SUM(costs)) OVER (ORDER BY year, month) as prev_month_costs,
       (SUM(costs) -
        LAG(SUM(costs)) OVER (ORDER BY year, month) 
       ) as diff,
       LAG(SUM(costs)) OVER (ORDER BY year, month) * 100 / SUM(costs)
FROM expenses e JOIN
     products p
     ON e.product_id = p.id
GROUP BY product_id, year, month
ORDER BY year, month, product_id;

如果您只想选择当前月份,可以使用子查询。

【讨论】:

  • 我想我不能使用 LAG() 因为我还在使用 MySQL 5.7.26
【解决方案2】:

我会通过条件聚合来解决这个问题:

select 
    p.description,
    sum(case when e.month = 11 then e.cost else 0 end) costs_november,
    sum(case when e.month = 10 then e.cost else 0 end) costs_october,
    sum(case when e.month = 11 then e.cost else -1 * e.cost end) costs,
    sum(case when e.month = 10 then e.cost else 0 end)
        * 100
        / nullif(
            sum(case when e.month = 11 then e.cost else 0 end),
            0
        ) percent_diff
from expenses e
inner join products p on p.id = e.product_id
where e.year = 2019 and e.month in (10, 11)
goup by e.product_id

您可以通过使用子查询来避免重复相同的条件求和(您的 RDBMS 可能无论如何都会对其进行优化,但这往往会使查询更具可读性):

select 
    description,
    costs_november,
    costs_october,
    costs_november - costs_october costs,
    costs_october * 100 / nullif(costs_november, 0) percent_diff
from (
    select 
        p.description,
        sum(case when e.month = 11 then e.cost else 0 end) costs_november,
        sum(case when e.month = 10 then e.cost else 0 end) costs_october
    from expenses e
    inner join products p on p.id = e.product_id
    where e.year = 2019 and e.month in (10, 11)
    goup by e.product_id
) t

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-12-15
    • 2022-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-01
    • 1970-01-01
    相关资源
    最近更新 更多