Проверка последовательных дней в SQL

У меня есть набор данных, структурированный следующим образом, который содержит тысячи материалов и миллионы строк:

Материал Дата Запас Требовать Поставлять Прогнозируемый запас Целевой запас Избыток 123456 24.06.2024 60 0 0 60 24 Да 123456 26.06.2024 0 4 0 56 24 Да 123456 27.06.2024 0 4 0 52 24 Да 123456 07.02.2024 0 0 10 62 24 Да 123456 07.04.2024 0 0 10 72 24 Да 123456 07.04.2024 0 0 1 73 24 Да 123456 07.04.2024 0 1 0 72 24 Да 123456 07.04.2024 0 1 0 71 24 Да 123456 07.04.2024 0 4 0 67 24 Да 123456 07.04.2024 0 4 0 63 24 Да 123456 07.05.2024 0 4 0 59 24 Да 123456 07.11.2024 0 0 11 70 24 Да 123456 07.13.2024 0 1 0 69 24 Да 123456 07.13.2024 0 1 0 68 24 Да 123456 07.13.2024 0 1 0 67 24 Да 123456 07.14.2024 0 4 0 63 24 Да 123456 07.16.2024 0 4 0 59 24 Да 123456 07.18.2024 0 0 11 70 24 Да 123456 07.18.2024 0 4 0 66 24 Да 123456 19.07.2024 0 1 0 65 24 Да 123456 19.07.2024 0 1 0 64 24 Да 123456 07.21.2024 0 4 0 60 24 Да 123456 07.21.2024 0 1 0 59 24 Да 123456 07.21.2024 0 1 0 58 24 Да 123456 07.21.2024 0 1 0 57 24 Да 123456 25.07.2024 0 0 9 66 24 Да 123456 25.07.2024 0 4 0 62 24 Да 123456 30.07.2024 0 0 9 71 24 Да 123456 30.07.2024 0 0 9 80 24 Да 123456 30.07.2024 0 0 9 89 24 Да 123456 30.07.2024 0 0 9 98 24 Да 123456 30.07.2024 0 0 9 107 24 Да …. …. …. …. …. …. …. …. 123457 24.06.2024 30 0 0 30 24 Нет 123457 26.06.2024 0 4 0 26 24 Нет 123457 27.06.2024 0 4 0 22 24 Нет 123457 07.02.2024 0 0 10 32 24 Нет 123457 07.04.2024 0 0 10 42 24 Нет 123457 07.04.2024 0 0 1 43 24 Нет 123457 07.04.2024 0 1 0 42 24 Нет 123457 07.04.2024 0 1 0 41 24 Нет 123457 07.04.2024 0 4 0 37 24 Нет 123457 07.04.2024 0 4 0 33 24 Нет 123457 07.05.2024 0 4 0 29 24 Нет 123457 07.11.2024 0 0 11 40 24 Нет 123457 07.13.2024 0 1 0 39 24 Нет 123457 07.13.2024 0 1 0 38 24 Нет 123457 07.13.2024 0 1 0 37 24 Нет 123457 07.14.2024 0 4 0 33 24 Нет 123457 07.16.2024 0 4 0 29 24 Нет 123457 07.18.2024 0 0 11 40 24 Нет 123457 07.18.2024 0 4 0 36 24 Нет 123457 19.07.2024 0 1 0 35 24 Нет 123457 19.07.2024 0 1 0 34 24 Нет 123457 07.21.2024 0 4 0 30 24 Нет 123457 07.21.2024 0 1 0 29 24 Нет 123457 07.21.2024 0 1 0 28 24 Нет 123457 07.21.2024 0 1 0 27 24 Нет 123457 25.07.2024 0 0 9 36 24 Нет 123457 25.07.2024 0 4 0 32 24 Нет 123457 30.07.2024 0 0 9 41 24 Нет 123457 30.07.2024 0 0 9 50 24 ? 123457 30.07.2024 0 0 9 59 24 ? 123457 30.07.2024 0 0 9 68 24 ? 123457 30.07.2024 0 0 9 77 24 ? …. …. …. …. …. …. …. ….

Поскольку столбец дат пропускает и повторяет некоторые даты, мне сложно делать то, что я хочу.

Мне нужно рассчитать, будет ли прогнозируемый запас составлять 200% или более для каждых 28 дней подряд. Таким образом, в приведенном выше примере прогнозируемый запас на 200 % превышает целевой запас (24 * 200 % = 48), поэтому должен быть столбец, который проверяет текущую строку и выполняет поиск в течение следующих 28 дней (то есть не 28 строк). ), и если каждый день из этих 28 дней прогнозируемый запас превышает 48, то соответствующий материал помечается флажком. Для справки: мой запрос в настоящее время использует CTE для преобразования необработанных данных в набор данных ниже.

 Projected_stock as (
    select 
      Material,
      Date,
      Stock,
      Target_Stock,
      Demand,
      Supply,
      sum(Stock - Demand + Supply) over (partition by material order by date,  rows between unbounded preceding and current row) as Projected_Stock
    from datasource
    group by all
    order by material,date)

Спасибо.

Редактировать: Я использую блоки данных и ожидаю увидеть, что для этого материала 123456 рядом со столбцом Target_Stock есть еще один столбец Overstock, который имеет флаг во всех строках, поскольку существует более 28 последовательных дней, когда прогнозируемый запас превышает 200% целевого запаса (48)

Пожалуйста, отметьте свою СУБД и ожидаемый результат.

Sam 25.06.2024 13:03

Какие строки должны иметь флаг Overstock? Можете ли вы включить столбец id или row_number, который однозначно идентифицирует каждую строку?

Bart McEndree 25.06.2024 13:32

Я добавил колонку и еще материал. Уникальным идентификатором является «Материал», и, как правило, все строки должны иметь флаг «Избыток», если в любом периоде, присутствующем в наборе данных, имеется 28 дней избытка запасов подряд.

Kristian Tonev 25.06.2024 13:43

Почему у вас есть Overstock=NO для материала 123457 и даты 30.07.2024? Только 1 из 4 записей с этой датой ниже 48.

Bart McEndree 25.06.2024 13:57

Вы правы, должно быть? потому что мы не знаем, останется ли она в ближайшие 28 дней ниже или выше

Kristian Tonev 25.06.2024 14:09

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

Luuk 25.06.2024 14:30

Если вы действительно хотите опубликовать так много данных, вам лучше опубликовать DBFIDDLE с этими данными....

Luuk 25.06.2024 14:37
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
1
7
74
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Версии решения для Postgres и MySQL. Функция DateAdd может отличаться для вас.

PostGres рабочий пример

    WITH  Projected_stock as 
    (
        select 
          Material,
          Date,
          Stock,
          Target_Stock,
          Demand,
          Supply,
          sum(Stock - Demand + Supply) over (partition by material order by date,  rows between unbounded preceding and current row) as Projected_Stock
        from datasource
        group by all
        order by material,date),
    CTE  as
        (
            SELECT material, c.date, 
            CASE 
               WHEN MIN(CASE WHEN projected_stock>48 THEN '1' ELSE '0' END)!=MAX(CASE WHEN projected_stock>48 THEN '1' ELSE '0' END) THEN '?' 
               ELSE MIN(CASE WHEN projected_stock>48 THEN '1' ELSE '0' END) 
            END as Overstock
            FROM Projected_stock c
            WHERE c.date between  c.date AND  c.date + INTERVAL '28 day' --dateadd(DAY, 28, c.date)
            GROUP BY material, date
        ),
        SELECT p.*, c.overstock
        FROM CTE c
        LEFT JOIN Projected_stock p ON p.date=c.date and c.material=p.material

MySQL рабочий пример

    WITH  Projected_stock as 
    (
        select 
          Material,
          Date,
          Stock,
          Target_Stock,
          Demand,
          Supply,
          sum(Stock - Demand + Supply) over (partition by material order by date,  rows between unbounded preceding and current row) as Projected_Stock
        from datasource
        group by all
        order by material,date), CTE  as
        (
            SELECT material, c.Date, 
            CASE 
               WHEN MIN(CASE WHEN Projected>48 THEN '1' ELSE '0' END)!=MAX(CASE WHEN Projected>48 THEN '1' ELSE '0' END) THEN '?' 
               ELSE MIN(CASE WHEN Projected>48 THEN '1' ELSE '0' END) 
            END as Overstock
            FROM Projected_stock c
            WHERE c.Date between  c.Date AND  DATE_ADD(c.Date, INTERVAL 28 DAY) 
            GROUP BY c.material, c.Date
        ),
        SELECT p.*, c.overstock
        FROM CTE c
        LEFT JOIN Projected_stock p ON p.Date=c.Date and c.material=p.material

«Ссылки на внешние ресурсы приветствуются» (см.: Как написать хороший ответ?). В настоящее время показаны только некоторые операторы SQL, и читатель должен выяснить, почему это должно работать (или нет). (Кстати: я не сторонник отрицательных оценок (на этот раз 😉))

Luuk 25.06.2024 14:54

Запросы обновлены и теперь включают Projected_Stock из источника данных.

Bart McEndree 25.06.2024 15:02

Спасибо, Барт, для меня это работает и это именно то, что мне нужно. Исходная версия справилась со своей задачей, не знаете, зачем ее улучшать?

Kristian Tonev 25.06.2024 15:02

Какую дату вы использовали?

Bart McEndree 25.06.2024 15:03

Сначала я использовал + INTERVAL «28 дней», и это сработало. Теперь я попробовал DATE_ADD(c.Date, INTERVAL 28 DAY), но это выдает ошибку DATATYPE_MISMATCH.UNEXPECTED_INPUT_TYPE

Kristian Tonev 25.06.2024 15:55

Я думаю, что вам, вероятно, нужно что-то вроде этого. Арифметика дат - Oracle.

WITH refdate  as
(
    SELECT material, date, max(target_stock) target
    FROM Projected_stock c
    GROUP BY material, date
)
select material,date
       ,case when times_over > 0 and times_under = 0 then 'Overstocked'
             when times_over = 0 then 'Not overstocked'
             when times_over > 0 and times_under > 0 then 'Sometimes overstocked'  
from                
  (SELECT r.material,r.date
         ,count(case when p.projected_stock > 2 * target then 1 end) times_over
         ,count(case when p.projected_stock < 2 * target then 1 end) times_under
  FROM refdate r
  JOIN Projected_stock p ON p.material=c.material and p.date between r.date+1 and r.date + 28
  group by r.material,r.date
  )

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

Другие вопросы по теме

Похожие вопросы

Выполнение различных функций агрегирования для разных строк выходных данных, возвращаемых предложением group by
Получите значения столбцов A таблицы 1 и значений столбца B таблицы 2 с тем же идентификатором
Как выбрать следующее наименьшее значение и следующее наибольшее значение из определенного значения записи, которое было помечено?
Хранимая процедура: добавьте параметр для месяца или часа
Как эффективно самостоятельно присоединиться к одной и той же таблице несколько раз с порядком сортировки по возрастанию и убыванию?
Запретить SQL сопоставлять конечные пробелы и вместо этого явно сопоставлять условие
Как вы можете вычислить, является ли результат десятичным или целым числом в SQL?
ПОЛНОЕ ВНЕШНЕЕ СОЕДИНЕНИЕ НА ДВУХ ТАБЛИЦАХ MYSQL С 3 СВЯЗАННЫМИ КОЛОННАМИ
Подзапрос INSERT INTO SELECT, который использует udf и возвращает 1 значение
Файл содержит значения даты как «01.09.2015 14:30:00». Но после импорта в таблицу oracle все значения даты отображаются как «20-01-19 15:12:15.00000 PM»