Создание нескольких отфильтрованных наборов результатов объединенной таблицы для использования в агрегатных функциях - PullRequest
0 голосов
/ 05 марта 2020

У меня есть (сильно упрощенная) таблица заказов, total - сумма в долларах, содержащая:

| id | client_id |   type | total |
|----|-----------|--------|-------|
|  1 |         1 |   sale |   100 |
|  2 |         1 | refund |   100 |
|  3 |         1 | refund |   100 |

И таблица клиентов, содержащая:

| id | name |
|----|------|
|  1 | test |

Я пытаюсь создать разбивку по клиенту, метрики об общем количестве продаж, возвратах, сумме продаж, сумме возвратов и т. д. c.

Для этого я запрашиваю таблицу клиентов и присоединяюсь к таблице заказов. Таблица заказов содержит как продажи, так и возвраты, указанные в столбце type.

Моя идея состояла в том, чтобы дважды объединить заказы с помощью подзапросов и создать псевдонимы для этих отфильтрованных таблиц. Затем псевдонимы будут использоваться в агрегатных функциях для нахождения суммы, среднего и т. Д. c. Я пробовал много вариантов присоединения к таблице заказов дважды, чтобы добиться этого, но это приводит к тем же неверным результатам. Этот запрос демонстрирует эту идею:

SELECT
  clients.*,
  SUM(sales.total) as total_sales,
  SUM(refunds.total) as total_refunds,
  AVG(sales.total) as avg_ticket,
  COUNT(sales.*) as num_of_sales
FROM clients   
LEFT JOIN (SELECT * FROM orders WHERE type = 'sale') as sales
  ON sales.client_id = clients.id   
LEFT JOIN (SELECT * FROM orders WHERE type = 'refund') as refunds
  ON refunds.client_id = clients.id  
GROUP BY clients.id

Результат:

| id | name | total_sales | total_refunds | avg_ticket | num_of_sales |
|----|------|-------------|---------------|------------|--------------|
|  1 | test |         200 |           200 |        100 |            2 |

Ожидаемый результат:

| id | name | total_sales | total_refunds | avg_ticket | num_of_sales |
|----|------|-------------|---------------|------------|--------------|
|  1 | test |         100 |           200 |        100 |            1 |

Когда второе соединение включено в запрос, строки возвращаются из первого соединения возвращаются снова со вторым соединением. Они умножаются на количество строк во втором соединении. Ясно, что мое понимание присоединения и / или подзапросов неполно.

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

SELECT
  clients.*,
  SUM(orders.total) FILTER (WHERE type = 'sale') as total_sales,
  SUM(orders.total) FILTER (WHERE type = 'refund') as total_refunds,
  AVG(orders.total) FILTER (WHERE type = 'sale') as avg_ticket,
  COUNT(orders.*) FILTER (WHERE type = 'sale') as num_of_sales      
FROM clients    
LEFT JOIN orders 
  on orders.client_id = clients.id     
GROUP BY clients.id 

Каков подходящий способ создания отфильтрованных и псевдонимов версий этой объединенной таблицы?

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

1 Ответ

1 голос
/ 05 марта 2020

Вы должны выполнить (отфильтрованную) агрегацию один раз для всех нужных вам агрегатов, а затем присоединиться к результату. Поскольку вашему агрегату не нужны никакие столбцы из таблицы clients, это можно сделать в производной таблице. Это также обычно быстрее, чем группировка результатов объединения.

SELECT clients.*,
       o.total_sales, 
       o.total_refunds,
       o.avg_ticket,
       o.num_of_sales
FROM clients    
  LEFT JOIN (
    select client_id, 
           SUM(total) FILTER (WHERE type = 'sale') as total_sales,
           SUM(total) FILTER (WHERE type = 'refund') as total_refunds,
           AVG(total) FILTER (WHERE type = 'sale') as avg_ticket,
           COUNT(*) FILTER (WHERE type = 'sale') as num_of_sales      
    from orders
    group by client_id
  ) o on o.client_id = clients.id     
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...