Как вы используете две агрегатные функции SUM () в одном запросе для PostgreSQL? - PullRequest
0 голосов
/ 01 ноября 2018

У меня есть запрос PostgreSQL, который дает следующие результаты:

SELECT   o.order || '-' || osh.ordinal_number AS order, 
         o.company,
         o.order_total,
         SUM(osh.items) AS order_shipment_total,
         o.order_type
FROM     orders o
         JOIN order_shipments osh ON o.order_id = osh.order_id
WHERE    o.order = [some order number]
GROUP BY o.order,
         o.company,
         o.order_total,
         o.order_type;

order   | company | order_total | order_shipment_total | order_type
-------------------------------------------------------------------
123-1   | A corp. | null        |  125.00              | new
123-2   | B corp. | null        |  100.00              | new

Мне нужно заменить o.order_total (он не работает должным образом) и суммировать сумму столбца order_shipment_total, чтобы в приведенном выше примере каждая строка завершилась со значением 225.00. Мне нужно, чтобы результаты, приведенные выше, выглядели так:

order   | company | order_total | order_shipment_total | order_type
-------------------------------------------------------------------
123-1   | A corp. | 225.00      |  125.00              | new
123-2   | B corp. | 225.00      |  100.00              | new

Что я пробовал

1.) Чтобы заменить o.order_total, я попытался SUM(SUM(osh.items)), но получил сообщение об ошибке, что вы не можете вложить агрегатные функции.

2.) Я пытался поместить весь запрос как подзапрос и суммировать столбец order_shipment_total, но когда я это делаю, он просто повторяет сам столбец. Смотрите ниже:

SELECT   order,
         company,
         SUM(order_shipment_total) AS order_shipment_total,
         order_shipment_total,
         order_type
FROM     (
    SELECT   o.order || '-' || osh.ordinal_number AS order, 
             o.company,
             o.order_total,
             SUM(osh.items) AS order_shipment_total,
             o.order_type
    FROM     orders o
             JOIN order_shipments osh ON o.order_id = osh.order_id
    WHERE    o.order = [some order number]
    GROUP BY o.order,
             o.company,
             o.order_total,
             o.order_type
) subquery
GROUP BY order,
         company,
         order_shipment_total,
         order_type;

order   | company | order_total | order_shipment_total | order_type
-------------------------------------------------------------------
123-1   | A corp. | 125.00      |  125.00              | new
123-2   | B corp. | 100.00      |  100.00              | new

3.) Я пытался включить только те строки, по которым я действительно хочу сгруппироваться, в моем примере подзапроса / запроса, потому что я чувствую, что смог сделать это в Oracle SQL. Но когда я это делаю, я получаю сообщение об ошибке: «column [name] должно появиться в предложении GROUP BY или использоваться в статистической функции».

...
GROUP BY order,
         company,
         order_type;

ERROR:  column "[a column name]" must appear in the GROUP BY clause or be used in an aggregate function.

Как мне это сделать? Я был уверен, что ответом будет подзапрос, но я не понимаю, почему этот подход не будет работать.

Ответы [ 2 ]

0 голосов
/ 01 ноября 2018

То, что вы не совсем понимаете в своем запросе / подходе, - это то, что вам на самом деле нужны два разных уровня группировки в одинаковых результатах строки запроса. Подзапросный подход наполовину прав, но когда вы выполняете подзапрос, который группирует, внутри другого запроса, который группирует, вы можете использовать только те данные, которые вы уже получили (из подзапроса), и вы можете только сохранить их на уровне агрегирования. подробно это уже есть, или вы можете потерять точность в пользу группировки больше. Вы не можете сохранить детали И потерять детали, чтобы подвести итоги. Следовательно, запрос подзапроса (с практической точки зрения) является относительно бессмысленным, поскольку вы можете сгруппировать его до уровня, который вы хотите, одним нажатием:

SELECT groupkey1, sum(y) FROM
(SELECT groupkey1, groupkey2, sum(x) as y FROM table GROUP BY groupkey1, groupkey2)
GROUP BY groupkey1

Так же, как:

SELECT groupkey1, sum(x) FROM
table
GROUP BY groupky1

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

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

Detail:

fruit | type             | purchasedate | count
apple | golden delicious | 2017-01-01   | 3
apple | golden delicious | 2017-01-02   | 4
apple | granny smith     | 2017-01-04   ! 2
melon | honeydew         | 2017-01-01   | 1
melon | cantaloupe       | 2017-01-05   | 4
melon | cantaloupe       | 2017-01-06   | 2

Итак, это 7 вкусных золотых, 2 бабушки Смит, 1 медвяная роса, 6 дынь, а также 9 яблок и 7 дынь

Вы не можете сделать это как один запрос *, потому что вы хотите два разных уровня группировки. Вы должны сделать это в виде двух запросов, а затем (критическая точка понимания) вы должны объединить менее точные (яблоки / дыни) результаты с более точными (бабушка кузнецы / золотой вкусный / honydew / канталупа):

SELECT * FROM
(
  SELECT fruit, type, sum(count) as fruittypecount
  FROM fruit
  GROUP BY fruit, type
) fruittypesum
INNER JOIN
(
  SELECT fruit, sum(count) as fruitcount
  FROM fruit
  GROUP BY fruit
) fruitsum
ON
  fruittypesum.fruit = fruitsum.fruit

Вы получите это:

fruit | type             | fruittypecount | fruit | fruitcount
apple | golden delicious | 7              | apple | 9
apple | granny smith     | 2              | apple | 9
melon | honeydew         | 1              | melon | 7
melon | cantaloupe       | 6              | melon | 7

Следовательно, для вашего запроса, различные группы, детали и резюме:

SELECT
    detail.order || '-' || detail.ordinal_number as order,
    detail.company,
    summary.order_total,
    detail.order_shipment_total,
    detail.order_type
FROM (
    SELECT   o.order,
             osh.ordinal_number, 
             o.company,
             SUM(osh.items) AS order_shipment_total,
             o.order_type
    FROM     orders o
             JOIN order_shipments osh ON o.order_id = osh.order_id
    WHERE    o.order = [some order number]
    GROUP BY o.order,
             o.company,
             o.order_type
) detail
INNER JOIN
(
    SELECT   o.order,
             SUM(osh.items) AS order_total
    FROM     orders o
             JOIN order_shipments osh ON o.order_id = osh.order_id
    --don't need the where clause; we'll join on order number
    GROUP BY o.order,
             o.company,
             o.order_type
) summary
ON
summary.order = detail.order

В запросе Гордона используется оконная функция для достижения того же эффекта; оконная функция запускается после завершения группировки и устанавливает другой уровень группировки (PARTITION BY ordernumber), который является эффективным эквивалентом моего GROUP BY ordernumber в сводке. Сводные данные оконной функции по своей сути связаны с подробными данными через порядковый номер; подразумевается, что запрос говорит:

SELECT
  ordernumber,
  lineitemnumber,
  SUM(amount) linetotal
  sum(SUM(amount)) over(PARTITION BY ordernumber) ordertotal
GROUP BY
  ordernumber,
  lineitemnumber

.. будет иметь ordertotal, который является суммой всех linetotal в заказе: GROUP BY подготавливает данные к детализации на уровне строки, а оконная функция подготавливает данные только к уровню заказа, и повторяет общее количество, необходимое для заполнения каждой позиции. Я написал SUM, который принадлежит операции GROUP BY в заглавных буквах. sum в нижнем регистре относится к операции разбиения. он должен sum(SUM()) и не может просто сказать sum(amount), потому что количество как столбец не разрешено само по себе - оно не входит в группу по. Поскольку количество само по себе не разрешено и его нужно СУММАТЬ, чтобы группа работала, нам нужно sum(SUM()) для запуска раздела (он запускается после того, как группа сделана)

Он ведет себя точно так же, как группировка на два разных уровня и объединение, и я действительно выбрал этот способ, чтобы объяснить это, потому что он делает более понятным, как он работает относительно того, что вы уже знаете о группах и объединениях

Помните: СОЕДИНЕНИЯ заставляют наборы данных расти вбок, СОЕДИНЕНИЯ заставляют их расти вниз. Если у вас есть некоторые подробные данные, и вы хотите увеличить их в сторону, добавив дополнительные данные (сводные данные), присоединитесь к ним. (Если бы вы хотели, чтобы итоги шли внизу каждого столбца, они были бы объединены)


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

0 голосов
/ 01 ноября 2018

Вы должны быть в состоянии использовать оконные функции:

SELECT o.order || '-' || osh.ordinal_number AS order, o.company,
       SUM(SUM(osh.items)) OVER (PARTITION BY o.order) as order_total,
       SUM(osh.items) AS order_shipment_total,
       o.order_type
FROM orders o JOIN
     order_shipments osh
     ON o.order_id = osh.order_id
WHERE o.order = [some order number]
GROUP BY o.order, o.company, o.order_type;
...