T-SQL, принцип хранения, два набора данных объединяются в один набор данных с помощью FIFO

#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» в таблице ПРОДАЖ. В этом случае правильным решением должны быть три записи с правильным значением счетчика.