У меня есть две таблицы. Во-первых, Tab1 имеет два столбца. Материал и количество (колоночный материал без дубликатов). В нем указано текущее количество материала на складе. Таблица выглядит так:
Вторая таблица Tab2 состоит из трех столбцов. Дата, материал и количество. Сортировать по дате. В нем указано, когда потребуется каждый материал, а также в необходимом количестве для производства. Таблица выглядит так:
Обе таблицы активны (скажем, таблицы просмотра), поэтому данные будут добавлены вовремя.
Мне нужен запрос, который покажет мне дату, когда складское количество для каждого материала будет отсутствовать на складе (таким образом, количество будет суммироваться от «строки к строке», и когда сумма количества переполнит складской запас материала, запрос возвращает значение даты — таким образом, mat1 будет отсутствовать на складе 2023-03-10, а mat2 будет отсутствовать на складе 2023-03-08). Я нахожу несколько запросов LOOP и запросов «CTE», но мне не удалось построить ни один из них. Думаю, понадобится какой-то рекурсивный запрос. Есть ли подобный рекурсивный запрос? Или мне нужно что-то еще?
Основываясь на примерных данных, которые вы нам предоставили, кажется, что все, что вам нужно, это кумулятивный 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 со временем будут расти, это живая таблица. Так что просто список входа будет невозможен.
Я не уверен, что понимаю то, что вы там утверждаете, @JanJanousek. Если ваши образцы данных не отражают вашу реальную проблему, вам нужно отредактировать свой вопрос, чтобы он был таким; Я не могу сделать решение на основе данных, о которых я не знаю.
Вы можете использовать функцию окна 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
материала меньше, чем sum
Quantity
материала того же типа, который использовался до этой датыРезультат:
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? Где данные для временной таблицы?
@JanJanoušek Они представляют одни и те же tab2 записи таблицы, но в разных ролях. Вот почему второе обозначено как temp, поэтому мы можем однозначно указать поле каждого из них. Таким образом, первый temp2 представляет дату, когда материал закончился (из-за спецификации предложения where и group by), тогда как подзапрос также использует tab2 с псевдонимом temp, представляя записи более ранних дат, чем запись ( s) из tab2 во внешнем запросе. Обратите внимание, что в подзапросе мы указываем, что temp основано на tab1 и tab2 внешнего запроса.
@JanJanousek, если есть дополнительные неясности, пожалуйста, не стесняйтесь задавать любые вопросы. Когда решение станет полностью ясным, вы можете принять его за правильный ответ.
Ух ты. Я настраиваю его на все свои данные, и это именно то, что мне нужно. Большое спасибо.
Кажется, вам это тоже не нужно; кумулятивный SUM должен быть всем, что вам нужно, и, возможно, CTE.