Левая стыковка боковая для условных сумм - PullRequest
0 голосов
/ 24 февраля 2019

У меня есть набор данных о покупках с клиентом, продуктом и категорией.

customer     product     category    sales_value
       A     aerosol     air_care             10
       B     aerosol     air_care             12
       C     aerosol     air_care              7
       A     perfume     air_care              8
       A     perfume     air_care              2
       D     perfume     air_care             11
       C      burger         food             13
       D       fries         food              6
       C       fries         food              9

Я хочу для каждого продукта соотношение между стоимостью продаж, потраченной на этот продукт, и стоимостью продаж, потраченной на этот продукт.Категория продукта - по клиентам, которые купили продукт хотя бы один раз.

Еще один способ сказать это: взять клиентов, которые купили fries хотя бы один раз, и для всех из них вычислить A) сумму продажстоимость, потраченная на fries и B) сумма продаж, потраченная на food.

Промежуточная таблица будет иметь следующий вид:

product    category  sum_spent_on_product           sum_spent_on_category    ratio
                                                 by_people_buying_product
aerosol    air_care                    29                              39     0.74
perfume    air_care                    21                              31     0.68
 burger        food                    13                              22     0.59
  fries        food                    15                              28     0.53

Пример: люди, купившиеaerosol хотя бы раз потратил 1800 на этот продукт.Те же самые люди потратили 3600 на категорию air_care (к которой относится aerosol).Таким образом, соотношение для aerosol составляет 0,5.

Я пытался решить эту проблему с помощью left join lateral и вычислять заданные промежуточные результаты для каждого product, но не могу обернуть головувокруг того, как включить условие only for customers who bought this specific product:

select
    distinct (product_id)
  , category
  , c.sales_category
from transactions t
left join lateral (
  select
    sum(sales_value) as sales_category
  from transactions
  where category = t.category
  group by category
) c on true
;

Приведенный выше запрос перечисляет сумму, потраченную на категорию продукта для каждого продукта, но без обязательного условия продукта-покупателя.

left join lateral правильный путь?Есть ли другое решение в простом SQL?

Ответы [ 2 ]

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

Я бы выбрал оконную функцию для вычисления общей суммы расходов каждого клиента в каждой категории:

SELECT
  customer, product, category, sales_value,
  sum(sales_value) OVER (PARTITION BY customer, category) AS tot_cat
FROM transactions;

 customer | product | category | sales_value | tot_cat 
----------+---------+----------+-------------+---------
 A        | aerosol | air_care |       10.00 |   20.00
 A        | perfume | air_care |        8.00 |   20.00
 A        | perfume | air_care |        2.00 |   20.00
 B        | aerosol | air_care |       12.00 |   12.00
 C        | aerosol | air_care |        7.00 |    7.00
 C        | fries   | food     |        9.00 |   22.00
 C        | burger  | food     |       13.00 |   22.00
 D        | perfume | air_care |       11.00 |   11.00
 D        | fries   | food     |        6.00 |    6.00

Тогда нам просто нужно подвести итог.Проблема в том, что покупатель покупал один и тот же продукт несколько раз.В вашем примере, клиент A купил парфюм дважды.Чтобы преодолеть эту проблему, давайте сгруппируем по клиенту, продукту и категории одновременно (и суммируем столбец sales_value):

SELECT
  customer, product, category, SUM(sales_value) AS sales_value,
  SUM(SUM(sales_value)) OVER (PARTITION BY customer, category) AS tot_cat
FROM transactions
GROUP BY customer, product, category

 customer | product | category | sales_value | tot_cat 
----------+---------+----------+-------------+---------
 A        | aerosol | air_care |       10.00 |   20.00
 A        | perfume | air_care |       10.00 |   20.00 <-- this row summarizes rows 2 and 3 of previous result
 B        | aerosol | air_care |       12.00 |   12.00
 C        | aerosol | air_care |        7.00 |    7.00
 C        | burger  | food     |       13.00 |   22.00
 C        | fries   | food     |        9.00 |   22.00
 D        | perfume | air_care |       11.00 |   11.00
 D        | fries   | food     |        6.00 |    6.00

Теперь нам просто нужно сложить sales_value и tot_cat, чтобы получить промежуточную таблицу результатов.,Я использую общее табличное выражение, чтобы получить предыдущий результат под именем t:

WITH t AS (
  SELECT
    customer, product, category, SUM(sales_value) AS sales_value,
    SUM(SUM(sales_value)) OVER (PARTITION BY customer, category) AS tot_cat
  FROM transactions
  GROUP BY customer, product, category
)
SELECT
  product, category,
  sum(sales_value) AS sales_value, sum(tot_cat) AS tot_cat,
  sum(sales_value) / sum(tot_cat) AS ratio
FROM t
GROUP BY product, category;

 product | category | sales_value | tot_cat |         ratio          
---------+----------+-------------+---------+------------------------
 aerosol | air_care |       29.00 |   39.00 | 0.74358974358974358974
 fries   | food     |       15.00 |   28.00 | 0.53571428571428571429
 burger  | food     |       13.00 |   22.00 | 0.59090909090909090909
 perfume | air_care |       21.00 |   31.00 | 0.67741935483870967742
0 голосов
/ 24 февраля 2019

Я хочу, чтобы для каждого продукта соотношение между стоимостью продаж, потраченной на этот продукт, и стоимостью продаж, потраченной на категорию этого продукта, клиентами, которые купили продукт хотя бы один раз.

Если я правильно понимаю, вы можете суммировать продажи по лицам и категориям, чтобы получить итоговую сумму по категории.В Postgres вы можете хранить массив продуктов и использовать их для сопоставления.Таким образом, запрос выглядит так:

select p.product, p.category,
       sum(p.sales_value) as product_only_sales, 
       sum(pp.sales_value) as comparable_sales
from purchases p join
     (select customer, category, array_agg(distinct product) as products, sum(sales_value) as sales_value
      from purchases p
      group by customer, category
     ) pp
     on p.customer = pp.customer and p.category = pp.category and p.product = any (pp.products)
group by p.product, p.category;

Здесь - это дБ <> скрипка.

РЕДАКТИРОВАТЬ:

Данные допускают дублирование вдата для продукта.Это отбрасывает вещи.Решение заключается в предварительной агрегации по продуктам для каждого клиента:

select p.product, p.category, sum(p.sales_value) as product_only_sales, sum(pp.sales_value) as comparable_sales
from (select customer, category, product, sum(sales_value) as sales_value
      from purchases p
      group by customer, category, product
     ) p join
     (select customer, category, array_agg(distinct product) as products, sum(sales_value) as sales_value
      from purchases p
      group by customer, category
     ) pp
     on p.customer = pp.customer and p.category = pp.category and p.product = any (pp.products)
group by p.product, p.category

Здесь - это скрипта db <> для этого примера.

...