Сравните агрегат с другим агрегатом с помощью оконных функций - PullRequest
0 голосов
/ 15 февраля 2019

Следующий запрос (протестированный с Postgresql 11.1) оценивает для каждой комбинации клиент / продукт следующие элементы:

  • (A) сумму продаж, которую клиент потратил на этот продукт
  • (B) сумма стоимости продаж, которую клиент потратил в родительской категории этого продукта

И делит A / B, чтобы получить метрику под названием loyalty.

select
  pp.customer, pp.product, pp.category,
  pp.sales_product / pc.sales_category as loyalty
from (
    select
      t.household_key as customer,
      t.product_id as product,
      p.commodity as category,
      sum(t.sales_value) as sales_product
    from transaction_data t
    left join product p on p.product_id = t.product_id
    group by t.household_key, t.product_id, p.commodity
) pp
left join (
    select
      t.household_key as customer,
      p.commodity as category,
      sum(t.sales_value) as sales_category
    from transaction_data t
    left join product p on p.product_id = t.product_id
    group by t.household_key, p.commodity
) pc on pp.customer = pc.customer and pp.category = pc.category
;

Результаты имеют следующий вид:

customer      product    category     loyalty
---------------------------------------------
       1       tomato        food        0.01
       1         beef        food        0.02
       1   toothpaste     hygiene        0.04
       1   toothbrush     hygiene        0.03

Мой вопрос состоит в том, чтобы вместо того, чтобы полагаться на два подзапроса, которые затем соединяются слева, было бы возможно с однимвместо этого использовать оконные функции?

Я пытался сделать что-то вроде следующего, но, очевидно, это не работает, потому что, в данном случае, column "t.sales_value" must appear in the GROUP BY clause or be used in an aggregate function.Я не вижу, что можно сделать, чтобы это исправить.

-- does not work
select
  t.household_key as customer,
  t.product_id as product,
  p.commodity as category,
  sum(t.sales_value) as sales_product,
  sum(t.sales_value) over (partition by t.household_key, p.commodity) as sales_category
from transaction_data t
left join product p on p.product_id = t.product_id
group by t.household_key, t.product_id, p.commodity;

1 Ответ

0 голосов
/ 15 февраля 2019

Я не знаю, как это сделать без использования соединения или подзапроса, но вот один из способов сделать это с подзапросом, используя аналитические функции:

WITH cte AS (
    SELECT
        t.household_key AS customer,
        t.product_id AS product,
        p.commodity as category,
        SUM(t.sales_value) OVER (PARTITION BY t.household_key, t.product_id, p.commodity)
            AS sales_product,
        SUM(t.sales_value) OVER (PARTITION BY t.household_key, p.commodity)
            AS sales_category
    FROM transaction_data t
    LEFT JOIN product p
        ON p.product_id = t.product_id
)

SELECT
    t.customer,
    t.product,
    t.category
    MAX(t.sales_product) / MAX(t.sales_category) AS loyalty
FROM cte
GROUP BY
    t.customer,
    t.product,
    t.category;

Хитрость в том, чтосделать один проход по объединенным таблицам и использовать аналитическую сумму для вычисления требуемых агрегатов с двумя разными разделами, один с двумя столбцами, а другой с тремя столбцами.Затем мы можем агрегировать по 3 столбцам и просто произвольно принимать максимальное значение агрегатов для каждой группы.

...