Нахождение совокупных итогов с условием и сгруппировать по - PullRequest
0 голосов
/ 29 апреля 2019

Вот мой случай:

Я хотел бы рассчитать количество и цену для данного товара на любую дату.

Цены рассчитываются с использованием общего количества товара и цены за единицу, поэтому цена меняется в зависимости от количества товара.

warehouse_1 указывает, что товар был отправлен с этого склада, warehouse_2 указывает, что товар был отправлен на этот склад.

Вот моя логика:

Выборка поставок для каждого товара и суммирование их количества.(1-й CTE)

Найти сумму количеств в обоих складах отдельно.(2nd CTE)

Рассчитайте окончательное количество и умножьте его на цену за единицу.

Показать результат, который состоит из идентификатора товара, количества и цены.

Я написал запрос, который правильно выполняет вычисления, НО он становится экспоненциально медленнее, когда количество данных увеличивается.(Занимает 5 секунд на моей БД с 6 тыс. Строк, почти блокирует БД на моей коллеге с 21 тыс. Строк)

Как я могу оптимизировать этот запрос?Я делаю кумулятивные вычисления для 2-го CTE для каждой строки, поступающей из 1-го CTE, и я считаю, что это требует переделки

Могу ли я использовать функцию LAG() для этого варианта использования?Я попробовал это с чем-то вроде

LAG(a.deliveryTotal) over(order by a.updated desc rows between unbounded preceding and current row)

вместо блока CASE во втором CTE, но я не могу понять, как использовать filter() или поместить условие в оператор LAG ().

Вот мой запрос:

`

with deliveriesCTE as (
select
    row_number() over(partition by it.id
order by
    dd.updated asc) as rn,
    sum(dd.quantity) as deliveryTotal,
    dd.updated as updated,
    it.id as item_id,
    d.warehouse_1 as outWH,
    d.warehouse_2 as inWH,
    d.company_code as company
from
    deliveries d
join deliveries_detail dd on
    dd.deliveries_id = d.id
join items it on
    it.id = dd.item_id
where
    ...
group by
    dd.updated,
    it.id,
    d.warehouse_1,
    d.warehouse_2,
    d.company_code
order by
    dd.updated asc),
cumulativeTotalsByUnit as (
select
    distinct on
    (a.item_id) a.rn,
    a.deliveryTotal,
    a.updated,
    a.item_id,
    a.outWH,
    a.inWH,
    a.company,
    case
        when a.rn = 1
        and a.outWH is not null then coalesce(a.deliveryTotal,
        0)
        else (
        select
            coalesce(sum(b.deliveryTotal) filter(
            where b.outWH is not null),
            0)
        from
            deliveriesCTE b
        where
            a.item_id = b.item_id
            and b.rn <= a.rn)
    end as outWHTotal,
    case
        when a.rn = 1
        and a.inWH is not null then coalesce(a.deliveryTotal,
        0)
        else (
        select
            coalesce(sum(b.deliveryTotal) filter(
            where b.inWH is not null),
            0)
        from
            deliveriesCTE b
        where
            a.item_id = b.item_id
            and b.rn <= a.rn)
    end as inWHTotal
from
    deliveriesCTE a
order by
    a.item_id,
    a.updated desc)
select
    resultView.item_id,
    resultView.quantity,
    resultView.price
from
    (
    select
        cumTotals.item_id,
        cumTotals.inWHTotal - cumTotals.outWHTotal as quantity,
        p.price * (cumTotals.inWHTotal - cumTotals.outWHTotal) as price
    from
        prices p
    join cumulativeTotalsByUnit cumTotals on
        cumTotals.item_id = p.item_id ) resultView
where
    resultView.rn = 1;

`

1 Ответ

1 голос
/ 29 апреля 2019

Трудно сказать для использования без MCV , но мое предположение о том, что вы пытаетесь сделать, состоит в том, чтобы выполнить вычисление Windowed SUM (), а не LAG ().Есть документация Здесь .

Запрос cumulativeTotalsByUnit не должен быть необходим и, вероятно, является квадратичным для выполнения сложного самоссылочного соединения.

Ваш CTE доставки долженвыглядеть так:

select
    sum(dd.quantity) over (partition by it.id ORDER BY dd.updated asc) as deliveryTotal,
    dd.updated as updated,
    it.id as item_id,
    d.warehouse_1 as outWH,
    d.warehouse_2 as inWH,
    d.company_code as company
from
    deliveries d
join deliveries_detail dd on
    dd.deliveries_id = d.id
join items it on
    it.id = dd.item_id
where
    ...
group by
    dd.updated,
    it.id,
    d.warehouse_1,
    d.warehouse_2,
    d.company_code
order by
    dd.updated asc
...