Рекурсивный запрос или цикл

У меня есть две таблицы. Во-первых, Tab1 имеет два столбца. Материал и количество (колоночный материал без дубликатов). В нем указано текущее количество материала на складе. Таблица выглядит так:

Материал количество мат1 500 мат2 400 мат3 600 мат4 100 ... ... и так далее...

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

Дата Материал количество 2023-02-28 мат1 250 2023-03-01 мат2 100 2023-03-02 мат4 50 2023-03-04 мат3 500 2023-03-05 мат1 200 2023-03-08 мат2 350 2023-03-10 мат1 200 2023-03-12 мат3 50 ... ... ... и так далее...

Обе таблицы активны (скажем, таблицы просмотра), поэтому данные будут добавлены вовремя.

Мне нужен запрос, который покажет мне дату, когда складское количество для каждого материала будет отсутствовать на складе (таким образом, количество будет суммироваться от «строки к строке», и когда сумма количества переполнит складской запас материала, запрос возвращает значение даты — таким образом, mat1 будет отсутствовать на складе 2023-03-10, а mat2 будет отсутствовать на складе 2023-03-08). Я нахожу несколько запросов LOOP и запросов «CTE», но мне не удалось построить ни один из них. Думаю, понадобится какой-то рекурсивный запрос. Есть ли подобный рекурсивный запрос? Или мне нужно что-то еще?

Кажется, вам это тоже не нужно; кумулятивный SUM должен быть всем, что вам нужно, и, возможно, CTE.

Larnu 20.02.2023 11:21
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
1
1
60
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Основываясь на примерных данных, которые вы нам предоставили, кажется, что все, что вам нужно, это кумулятивный SUM и CTE, чтобы вы могли фильтровать, когда оставшееся количество меньше или равно нулю:

WITH Cumulative AS(
    SELECT T2.Date,
           T2.Material,
           SUM(T2.quantity) OVER (PARTITION BY T2.Material ORDER BY Date) AS quantity,
           T1.quantity - SUM(T2.quantity) OVER (PARTITION BY T2.Material ORDER BY Date) AS Remaining
    FROM (VALUES(CONVERT(date,'20230228'),'mat1',250),
                (CONVERT(date,'20230301'),'mat2',100),
                (CONVERT(date,'20230302'),'mat4',50),
                (CONVERT(date,'20230304'),'mat3',500),
                (CONVERT(date,'20230305'),'mat1',200),
                (CONVERT(date,'20230308'),'mat2',350),
                (CONVERT(date,'20230310'),'mat1',200),
                (CONVERT(date,'20230312'),'mat3',50))T2(Date,Material,quantity)
         JOIN (VALUES('mat1',500),
                     ('mat2',400),
                     ('mat3',600),
                     ('mat4',100))T1 (Material,quantity) ON T2.Material = T1.Material)
SELECT Date,
       Material,
       Remaining
       --ROW_NUMBER() OVER (PARTITION BY Material ORDER BY Remaining DESC) AS RN
FROM Cumulative
WHERE Remaining <= 0;

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

Jan Janoušek 20.02.2023 11:45

Я не уверен, что понимаю то, что вы там утверждаете, @JanJanousek. Если ваши образцы данных не отражают вашу реальную проблему, вам нужно отредактировать свой вопрос, чтобы он был таким; Я не могу сделать решение на основе данных, о которых я не знаю.

Larnu 20.02.2023 11:46

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

WITH usage as (
SELECT
    Date,
        Material,
        SUM(quantity) OVER(PARTITION BY Material order by Date) as usage_up_to_date
FROM
       TAB2
),
stock_balance AS (
SELECT
      usage.Date,
      usage.Material,
      (TAB1.quantity - usage.usage_up_to_date) as balance
FROM
      usage
JOIN
      TAB1
ON
       usage.Material = TAB1.Material 
)
SELECT
      Material,
      MIN(Date) AS Date
FROM
     stock_balance
WHERE
      balance <=0
GROUP BY
     Material
Ответ принят как подходящий

Вам не обязательно писать рекурсивный запрос для этой цели. Вот нерекурсивный запрос:

select min(tab2.Date), tab1.Material
from tab1
join tab2
on tab1.Material = tab2.Material
where tab1.Quantity < (
    select sum(temp.Quantity)
    from tab2 temp
    where temp.Date <= tab2.Date and
          temp.Material = tab1.Material
)
group by tab1.Material;

Объяснение:

  • мы загружаем пары tab1 и tab2
  • сгруппированы по tab1.Material, чтобы убедиться, что мы можем получить самое раннее Date для каждого Material
  • кортежи (tab1, tab2), конечно, соединены Material
  • где общая Quantity материала меньше, чем sumQuantity материала того же типа, который использовался до этой даты

Результат:

  • мы получаем список min(tab2.Date), tab1.Material
  • представляющий самый ранний Date для каждого Material, когда он исчезнет со склада

Наблюдение: Текущая реализация указывает первую дату, когда требуется больше материала (каждого типа), чем необходимо. Если вместо этого вам нужно получить первую дату, когда количество будет равно 0 или меньше, измените < в основном операнде предложения where на <=.

Дампл данных

create table tab1(
  Material VARCHAR(32),
  Quantity INT
);

create table tab2(
  Date Date,
  Material VARCHAR(32),
  Quantity INT
);

insert into tab1(Material, Quantity) values
('a', 13),
('b', 14),
('c', 15);

insert into tab2(Date, Material, Quantity) values
('2023-01-01', 'a', 8),
('2023-01-01', 'b', 8),
('2023-01-01', 'c', 8),
('2023-01-02', 'a', 6),
('2023-01-02', 'b', 6),
('2023-01-02', 'c', 6),
('2023-01-03', 'a', 1),
('2023-01-03', 'b', 1),
('2023-01-03', 'c', 1);

Рабочий пример: http://sqlfiddle.com/#!18/c3af07/1

Обратите внимание, что в моем примере c вообще не появляется в результате, потому что, даже если он в конечном итоге имеет 0 Quantity, он никогда не опустится ниже него.

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

Jan Janoušek 20.02.2023 12:28

@JanJanoušek Они представляют одни и те же tab2 записи таблицы, но в разных ролях. Вот почему второе обозначено как temp, поэтому мы можем однозначно указать поле каждого из них. Таким образом, первый temp2 представляет дату, когда материал закончился (из-за спецификации предложения where и group by), тогда как подзапрос также использует tab2 с псевдонимом temp, представляя записи более ранних дат, чем запись ( s) из tab2 во внешнем запросе. Обратите внимание, что в подзапросе мы указываем, что temp основано на tab1 и tab2 внешнего запроса.

Lajos Arpad 20.02.2023 12:32

@JanJanousek, если есть дополнительные неясности, пожалуйста, не стесняйтесь задавать любые вопросы. Когда решение станет полностью ясным, вы можете принять его за правильный ответ.

Lajos Arpad 20.02.2023 12:32

Ух ты. Я настраиваю его на все свои данные, и это именно то, что мне нужно. Большое спасибо.

Jan Janoušek 20.02.2023 13:52

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