Вычислить разницу значений между двумя временными метками и назначить разницу каждой строке в результатах

#mysql #sql

#mysql #sql

Вопрос:

DB-Fiddle

 CREATE TABLE operations (
    id int auto_increment primary key,
    time_stamp DATE,
    product VARCHAR(255),
    plan_week VARCHAR(255),
    quantity INT
);

INSERT INTO operations
(time_stamp, product, plan_week, quantity
)
VALUES 
("2020-01-01", "Product_A", "CW01", "125"),
("2020-01-01", "Product_B", "CW01", "300"),
("2020-01-01", "Product_C", "CW01", "700"),
("2020-01-01", "Product_D", "CW01", "900"),

("2020-03-15", "Product_A", "CW01", "570"),
("2020-03-15", "Product_C", "CW02", "150"),
("2020-03-15", "Product_E", "CW02", "325");
  

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

 time_stamp   |   product     |      plan_week   |    quantity   |    difference_quantity
-------------|---------------|------------------|---------------|-------------------
2020-01-01   |   Product_A   |        CW01      |       125     |          445
2020-03-15   |   Product_A   |        CW01      |       570     |          445
             |               |                  |               |
2020-01-01   |   Product_B   |        CW01      |       300     |         -300
             |               |                  |               |
2020-01-01   |   Product_C   |        CW01      |       700     |         -550
2020-03-15   |   Product_C   |        CW02      |       150     |         -550
             |               |                  |               |
2020-01-01   |   Product_D   |        CW01      |       900     |         -900
2020-03-15   |   Product_E   |        CW02      |       325     |          325
  

В приведенном выше результате я хочу вычислить difference между двумя time_stamps и назначить это difference каждой строке на product и plan_week , как вы можете видеть в столбце difference_quantity .

Пока у меня есть этот запрос, который сравнивает два time_stamps и возвращает максимальное количество между ними обоими:

 SELECT 
time_stamp,
product,
plan_week,
quantity,
MAX(quantity) OVER (PARTITION BY product) AS max_quantity
FROM operations
GROUP BY 1,2
ORDER BY 2,1;
  

Однако я понятия не имею, как я могу изменить этот запрос, чтобы вместо максимального количества отображалось difference_quantity .
У тебя есть какие-нибудь идеи?

Ответ №1:

Я мог бы решить проблему с /- signs , вычислив разницу между time_stamps в отдельной таблице, а затем использовать LEFT JOIN для этой таблицы:

DB-Fiddle

 SELECT 
op.time_stamp,
op.product,
op.plan_week,
op.quantity,
t2.difference_quantity
  
FROM operations op
LEFT JOIN

  (SELECT
  product,
  (sum(time_stamp_02) - sum(time_stamp_01)) AS difference_quantity
  FROM
    (SELECT 
    product,
    SUM(CASE WHEN time_stamp = '2020-01-01' THEN quantity ELSE 0 END) AS time_stamp_01,
    SUM(CASE WHEN time_stamp = '2020-03-15' THEN quantity ELSE 0 END) AS time_stamp_02
    FROM operations 
    GROUP BY 1) t1
  GROUP BY 1) t2 ON t2.product = op.product

GROUP BY 1,2
ORDER BY 2,1;
  

Ответ №2:

Неоптимизированное пошаговое решение. Все CTE могут быть легко сведены в один CTE.

Предполагается, что исходная таблица хранит строго два разных значения даты.

 WITH 
cte1 AS (SELECT DISTINCT time_stamp FROM operations),
cte2 AS (SELECT DISTINCT product FROM operations),
cte3 AS (SELECT * FROM cte1 CROSS JOIN cte2),
cte4 AS (SELECT cte3.*, operations.plan_week, COALESCE(operations.quantity, 0) quantity
         FROM cte3 LEFT JOIN operations USING (time_stamp, product)),
cte5 AS (SELECT *, FIRST_VALUE(quantity) OVER (PARTITION BY product ORDER BY time_stamp DESC)
                  -FIRST_VALUE(quantity) OVER (PARTITION BY product ORDER BY time_stamp ASC) difference
         FROM cte4)
SELECT * FROM cte5 WHERE plan_week IS NOT NULL ORDER BY 2,1;
  

скрипка

Ответ №3:

Это отвечает на первоначальную версию вопроса.

Вы, кажется, описываете логику следующим образом:

 SELECT o.*,
       (CASE WHEN COUNT(*) OVER (PARTITION BY product) = 2
             THEN MAX(quantity) OVER (PARTITION BY product) - MIN(quantity) OVER (PARTITION BY product)
             ELSE quantity
        END) AS difference_quantity
FROM operations o
ORDER BY product, time_stamp;
  

Комментарии:

1. Можно ли отобразить 300, 550 и 900 в виде отрицательных чисел?

2. @Akina: Я пытался, но не смог заставить это работать: dbfiddle.uk /…

3. @Michi . . . Лучше задать новый вопрос, а не аннулировать ответы, отредактировав вопрос с уже полученным ответом.

4. Значения -300 и -550 были там до того, как я отредактировал вопрос. Знак минуса -900 отсутствовал, и я отредактировал, чтобы ожидаемые результаты были полностью правильными.