#tsql #fifo #warehouse
Вопрос:
Я хотел бы объединить данные между двумя наборами данных с помощью FIFO. Я думаю, что принцип склада-хороший пример для объяснения того, что мне нужно.
КУПИТЬ стол:
ID_КУПИТЬ | Дата | Предмет | Рассчитывать |
---|---|---|---|
39 | 2022-01-01 00:00 | Карандаш | 1000 |
41 | 2022-02-02 00:00 | Карандаш | 2000 |
44 | 2022-03-03 10:00 | Карандаш | 3000 |
45 | 2022-03-03 12:00 | Карандаш | 3500 |
47 | 2022-04-04 00:00 | Карандаш | 4000 |
51 | 2022-05-05 00:00 | Клей | 5000 |
53 | 2022-06-06 00:00 | Книга | 6000 |
ПРОДАМ стол:
ID_SELL | Дата | Предмет | Рассчитывать |
---|---|---|---|
40 | 2022-01-01 16:00 | Карандаш | 1000 |
42 | 2022-02-02 17:00 | Карандаш | 1200 |
43 | 2022-02-02 18:00 | Карандаш | 800 |
46 | 2022-03-03 14:00 | Карандаш | 6500 |
48 | 2022-04-04 15:00 | Карандаш | 2100 |
49 | 2022-04-04 16:00 | Карандаш | 1100 |
52 | 2022-05-05 20:00 | Клей | 2000 |
Ожидаемые данные:
ID | ID_КУПИТЬ | ID_SELL | Рассчитывать |
---|---|---|---|
1 | 39 | 40 | 1000 |
2 | 41 | 42 | 1200 |
3 | 41 | 43 | 800 |
4 | 44 | 46 | 3000 |
5 | 45 | 46 | 3500 |
6 | 47 | 48 | 2100 |
7 | 47 | 49 | 1100 |
8 | 51 | 52 | 2000 |
Описание ожидаемых данных:
- Идентификатор строки = 1 —> 2022-01-01 Я купил 1000 карандашей и продал 1000 карандашей несколько часов назад.
- Строки ID = 2 и 3 —> 2022-02-02 Я купил 2000 карандашей и продал 1200 карандашей клиенту № 1 (ID = 2) и 800 карандашей клиенту № 2 (ID = 3).
- Идентификатор строки = 4 и 5 —> 2022-03-03 Я купил 6500 карандашей в двух транзакциях (3000 и 3500) и продал их в одной транзакции (6500). В этом случае лимит в строке определяется транзакцией покупки.
- Идентификатор строки = 6 и 7 —> 2022-04-04 Я купил 4000 карандашей и продал 3200 в двух транзакциях (2100 1100). Теперь у меня на складе есть 800 карандашей (4000 — 3200 = 800), предназначенных для следующей транзакции (- ов). В этом случае лимит в строке определяется транзакцией продажи.
- В ожидаемых данных нет книг, поскольку они не были проданы.
Я попытался решить эту проблему с помощью курсора, основанного на «Таблице ПРОДАЖ». Когда я делал это, я заметил, что мне пришлось использовать курсор внутри курсора. Второй курсор должен был бы основываться на «таблице ПОКУПОК» плюс на том, что не было указано в таблице ожидаемых данных, и на следующих шагах мне придется использовать IF. Может быть, есть более простой способ решить эту проблему? CTE?
Ответ №1:
Объедините данные о покупке продаже и рассчитайте совокупное «количество на складе».:
if object_id('tempdb..#tmp_fifo_count') is not null drop table #tmp_fifo_count
select
b.ID_BUY,
s.ID_SELL,
(b.BuyedCummulativeCount - s.SoldCummulativeCount) as CountInStock,
row_number() over(partition by b.ID_BUY order by s.[Date] asc) as RN, -- flag first sold over limit
case
when (b.BuyedCummulativeCount - s.SoldCummulativeCount) < 0 and b.BuyedCummulativeCount >= s.[Count]
then (b.BuyedCummulativeCount - s.SoldCummulativeCount) -- correction to case over limit (for n:m relationship)
else 0
end
case
when b.[Count] >= s.[Count] then s.[Count] -- i have more in stock then i sold
else b.[Count] -- i cannot sell over limit
end as [Count]
into #tmp_fifo_count
from (
select *, (select sum([Count]) from #tmp_buy b2 where b2.[Date] <= b0.[Date]) as BuyedCummulativeCount
from #tmp_buy b0) as b
left join (
select *, (select sum([Count]) from #tmp_sell s2 where s2.[Date] <= s0.[Date]) as SoldCummulativeCount
from #tmp_sell s0) as s
on b.[Date] <= s.[Date]
Получить результат:
-- output
select
row_number() over(order by ID_BUY, ID_SELL) as ID,
ID_BUY,
ID_SELL,
[Count]
--case when CountInStock >= 0 then CountInStock else 0 end as CountInStock
from #tmp_fifo_count
where (CountInStock >= 0 or RN = 1) -- Fully covered by one buy or not Fully covered by one buy
and ID_SELL is not null -- they were sold
Комментарии:
1. Почти идеально. Товар в таблице «ПОКУПКА» должен совпадать с товаром в таблице «ПРОДАЖА». Я изменил ваш запрос, добавив table1.item = table2.item. Это сработало. Проблема возникает, однако, когда в таблице ПРОДАЖИ у меня, например, только одна запись с числом = 100 (карандаши), а таблица ПОКУПКИ остается неизменной. В результате я получаю набор из 5 записей, т. Е. запись из таблицы продаж сопоставляется с каждой записью из таблицы ПОКУПОК. Это неверно, так как оно должно совпадать только с первой записью в таблице ПОКУПОК (для карандашей). У вас есть идея, как изменить предлагаемое решение?
2. Та же проблема возникает и тогда, когда у меня есть только одна запись «количество карандашей = 5000» в таблице ПРОДАЖ. В этом случае правильным решением должны быть три записи с правильным значением счетчика.