Рассчитать сумму от транзакций / событий - PullRequest
0 голосов
/ 03 февраля 2019

Не знаю, если это в правильной категории, но я пытаюсь рассчитать суммы в sql.Вот что у меня есть:

Одна таблица с транзакциями.Вот и все в принципе.Каждая транзакция представляет собой «внесение» или «снятие» продукта с полки.Там нет данных о количестве продуктов на полке, и теперь я хотел бы рассчитать количество продуктов на всех полках в течение дня.Каждый день.

Таблица:

Transaction Datetime, Source, Destination, Product ID, Product Group
2019-02-01 08:01:00, Person1, Shelf1, 1234, 1
2019-02-01 10:01:00, Shelf1, Person1, 1234, 1
2019-02-01 08:03:00, Person2, Shelf1, 5678, 1
...

Желаемая таблица:

Hour, Date, Shelf, Product Group, Amount
8, 2019-02-01, Shelf1, 1, 5
9, 2019-02-01, Shelf1, 1, 10
10, 2019-02-01, Shelf1, 1, 10

Есть идеи, как это сделать?Любое предложение будет оценено

Br Cris

Ответы [ 2 ]

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

Ниже для BigQuery Standard SQL

Идея состоит в том, чтобы сначала собрать все

  • местоположения продукта (источник и место назначения)
  • все группы продуктов
  • и, наконец, все часы / дни для создания отчета за

Затем CROSS JOIN все три выше и LEFT JOIN с часами, днем, местоположением и группой товаров основных данных при расчете суммы на основекакое местоположение представляет - источник или место назначения.

Далее все суммы сгруппированы в один и тот же час / день и, очевидно, местоположение и Product_Group
И, наконец, аналитическая функция применяется для расчета совокупных сумм

Окончательный код с выборочными данными приведен ниже

#standardSQL
WITH `project.dataset.table` AS (
  SELECT DATETIME '2019-02-01 08:01:00' Transaction_Datetime, 'Person1' Source, 'Shelf1' Destination, 1234 Product_ID, 1 Product_Group UNION ALL
  SELECT '2019-02-01 10:01:00', 'Shelf1', 'Person1', 1234, 1 UNION ALL
  SELECT '2019-02-01 08:03:00', 'Person2', 'Shelf1', 5678, 1 
), hours AS (
  SELECT EXTRACT(HOUR FROM hour) hour, DATE(hour) day
  FROM (
    SELECT 
      MIN(TIMESTAMP(Transaction_Datetime)) min_ts,
      MAX(TIMESTAMP(Transaction_Datetime)) max_ts
    FROM `project.dataset.table`
  ), UNNEST(GENERATE_TIMESTAMP_ARRAY(
    TIMESTAMP_TRUNC(min_ts, HOUR),
    TIMESTAMP_TRUNC(max_ts, HOUR),
    INTERVAL 1 HOUR)) hour
), locations AS (
  SELECT Source AS location FROM `project.dataset.table`
  UNION DISTINCT 
  SELECT Destination FROM `project.dataset.table`
), product_groups AS (
  SELECT DISTINCT Product_Group FROM `project.dataset.table`
), temp AS (
  SELECT 
    EXTRACT(HOUR FROM Transaction_Datetime) hour,
    DATE(Transaction_Datetime) day,
    Source, Destination, Product_ID, Product_Group
  FROM `project.dataset.table`
)
SELECT hour, day, location, product_group,
  SUM(delta) OVER(PARTITION BY location, product_group ORDER BY hour, day) amount
FROM (
  SELECT 
    hours.hour, hours.day, location, product_groups.product_group,
    SUM(CASE location 
      WHEN Source THEN -1
      WHEN Destination THEN 1
      ELSE 0
    END) delta 
  FROM locations, hours, product_groups
  LEFT JOIN temp t
  ON t.hour = hours.hour
  AND t.day = hours.day
  AND t.product_group = product_groups.product_group
  AND location IN (Source, Destination)
  GROUP BY hours.hour, hours.day, location, Product_Group
)
WHERE LOWER(location) LIKE 'shelf%' 
-- ORDER BY hour, day, location

с результатом

Row hour    day         location    product_group   amount   
1   8       2019-02-01  Shelf1      1       2    
2   9       2019-02-01  Shelf1      1       2    
3   10      2019-02-01  Shelf1      1       1    

Примечание: из вашего вопроса не совсем понятно, как отличить Shelf от Person - поэтому используется LOWER(location) LIKE 'shelf%'.Вы можете настроить это, чтобы использовать любую логику, которая у вас есть для этого
Если вы удалите эту строку - вы получите количество не только для полок, но и баланс продукта в «руке» каждого человека

.протестируйте с вашей таблицей - запустите ниже - не забудьте заменить `project.dataset.table` вашей полной ссылкой на таблицу

#standardSQL
WITH hours AS (
  SELECT EXTRACT(HOUR FROM hour) hour, DATE(hour) day
  FROM (
    SELECT 
      MIN(TIMESTAMP(Transaction_Datetime)) min_ts,
      MAX(TIMESTAMP(Transaction_Datetime)) max_ts
    FROM `project.dataset.table`
  ), UNNEST(GENERATE_TIMESTAMP_ARRAY(
    TIMESTAMP_TRUNC(min_ts, HOUR),
    TIMESTAMP_TRUNC(max_ts, HOUR),
    INTERVAL 1 HOUR)) hour
), locations AS (
  SELECT Source AS location FROM `project.dataset.table`
  UNION DISTINCT 
  SELECT Destination FROM `project.dataset.table`
), product_groups AS (
  SELECT DISTINCT Product_Group FROM `project.dataset.table`
), temp AS (
  SELECT 
    EXTRACT(HOUR FROM Transaction_Datetime) hour,
    DATE(Transaction_Datetime) day,
    Source, Destination, Product_ID, Product_Group
  FROM `project.dataset.table`
)
SELECT hour, day, location, product_group,
  SUM(delta) OVER(PARTITION BY location, product_group ORDER BY hour, day) amount
FROM (
  SELECT 
    hours.hour, hours.day, location, product_groups.product_group,
    SUM(CASE location 
      WHEN Source THEN -1
      WHEN Destination THEN 1
      ELSE 0
    END) delta 
  FROM locations, hours, product_groups
  LEFT JOIN temp t
  ON t.hour = hours.hour
  AND t.day = hours.day
  AND t.product_group = product_groups.product_group
  AND location IN (Source, Destination)
  GROUP BY hours.hour, hours.day, location, Product_Group
)
WHERE LOWER(location) LIKE 'shelf%' 
-- ORDER BY hour, day, location
0 голосов
/ 03 февраля 2019

Я бы использовал datetime_trunc() и поместил бы час в том же столбце, что и дата.

Но фундаментальная идея состоит в том, чтобы "переключать" строки, чтобы полки всегда были источником, и добавить индикатор длясумма (отрицательно для движения в другом направлении).

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

select datetime_trunc(transaction_datetime, hour) date yyyymmddhh,
       Shelf, Product_Group,
       sum(inc) as changes_this_hour,
       sum(sum(inc)) over (partition by shelf,  product_id, product_group order by min(transaction_datetime)) as net_amount
from ((select transaction_datetime,
              source, destination, product_id, product_group,
              1 as inc
       from t
       where source like 'Shelf%'
      )
      union all
      (select transaction_datetime,
              destination, source,  product_id, product_group,
              -1 as inc
       from t
       where destination like 'Shelf%'
      )
     ) t
group by yyyymmddhh, Shelf,  product_id, Product_Group
order by Shelf, Product_Group;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...