Оптимизировать подзапрос в SELECT - PullRequest
1 голос
/ 01 ноября 2019

Моя таблица схема выглядит следующим образом:

enter image description here

Индексы:

  • products.id ПЕРВИЧНЫЙ КЛЮЧ
  • products.description UNIQUE
  • расход.id ПЕРВИЧНЫЙ КЛЮЧ
  • cost.product_id FOREIGN KEY для product.id

My цель - загрузить

  1. Стоимость каждого продуктатекущий месяц (AS cost_november)
  2. Стоимость каждого продукта прошлого месяца (AS cost_october)
  3. Изменение затрат текущего месяца по сравнению с прошлым(расходы за текущий месяц - расходы за последний месяц) (расходы AS)
  4. Изменение в процентах расходов за текущий месяц по сравнению с прошлым(в прошлом месяце стоит * 100 / в текущем месяце) (AScent_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;

Но действительно ли решение о вставке копии в один и тот же подзапрос три раза является решением? Теоретически для каждого продукта требуется выполнить четыре запроса. Есть ли более элегантный способ?

Благодарим за любую помощь!

Ответы [ 2 ]

1 голос
/ 01 ноября 2019

Я бы решил эту проблему с помощью условного агрегирования:

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

Вы можете избежать повторения тех же условных сумм, используя подзапрос (ваша СУБД, вероятно, все равно оптимизирует его, но это делает запрос более читабельным):

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
0 голосов
/ 01 ноября 2019

Вы можете рассчитать для всех месяцев и всех продуктов одновременно:

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;

Вы можете использовать подзапрос, если хотите выбрать только текущий месяц.

...