Я пытаюсь рассчитать себестоимость проданных товаров по следующей логике: У меня есть две таблицы: SaleActions и PurchaseActions со следующей структурой:
PurcahseActions: PurchaseId, PurchaseDate, PurchaseLineId, ItemId, Quantity, Price.
SaleActions:SaleId, SaleDate, SaleLineId, ItemId, количество, цена.
Вот пример:
1 августа: 60 единиц товара X приобретаются по курсу 1 доллар США.
2 августа: куплено 5 единиц товара X по курсу 2 доллара США.
5 августа: 63 единицы товара X продаются по цене 1,5 доллара США.
Используя эти данные, я пытаюсь получить следующий набор результатов:
Это то, что я создал на данный момент:
WITH RankedPurchases AS (SELECT PurchaseId,
PurchaseDate,
LineId AS PurchaseLineId,
ItemId,
Quantity,
Price,
SUM(Quantity) OVER (PARTITION BY ItemId ORDER BY PurchaseDate, PurchaseId) AS CumulativePurchaseQuantity,
SUM(Quantity) OVER (PARTITION BY ItemId ORDER BY PurchaseDate, PurchaseId) - Quantity AS PreviousCumulativePurchaseQuantity
FROM @PurchaseActions),
RankedSales AS (SELECT SaleId,
SaleDate,
LineId AS SaleLineId,
ItemId,
Quantity,
SUM(Quantity) OVER (PARTITION BY ItemId ORDER BY SaleDate, SaleId) AS CumulativeSaleQuantity,
SUM(Quantity) OVER (PARTITION BY ItemId ORDER BY SaleDate, SaleId) - Quantity AS PreviousCumulativeSaleQuantity
FROM @SaleActions),
MatchedSales AS (SELECT s.SaleId,
s.SaleLineId,
p.PurchaseId,
p.PurchaseLineId,
s.ItemId,
s.Quantity AS SaleQuantity,
p.Quantity AS PurchaseQuantity,
p.Price,
s.PreviousCumulativeSaleQuantity,
p.PreviousCumulativePurchaseQuantity,
ROW_NUMBER() OVER (PARTITION BY s.SaleId, s.SaleLineId ORDER BY p.PurchaseDate, p.PurchaseId) AS RowNum
FROM RankedSales s
JOIN RankedPurchases p ON s.ItemId = p.ItemId
WHERE p.CumulativePurchaseQuantity > s.PreviousCumulativeSaleQuantity
AND p.PreviousCumulativePurchaseQuantity < s.CumulativeSaleQuantity)
SELECT SaleId,
SaleLineId,
PurchaseId,
PurchaseLineId,
CASE
WHEN RowNum = 1 THEN
CASE
WHEN SaleQuantity <= PurchaseQuantity THEN SaleQuantity
ELSE PurchaseQuantity
END
ELSE
CASE
WHEN SaleQuantity - (PreviousCumulativeSaleQuantity - PreviousCumulativePurchaseQuantity) <= PurchaseQuantity
THEN SaleQuantity - (PreviousCumulativeSaleQuantity - PreviousCumulativePurchaseQuantity)
ELSE PurchaseQuantity
END
END AS Quantity,
Price
FROM MatchedSales
ORDER BY ItemId, SaleId, SaleLineId, RowNum
Однако мой результат показывает количество как 60 и 5 вместо 60 и 3. Полный запрос с минимально воспроизводимыми данными: https://sqlfiddle.com/sql-server/online-compiler?id=0332881c-690e-4ca9-9e22-5cb255bf74c9.
А как рассчитывается себестоимость товара, ФИФО, ЛИФО, взвешивается? SO - это не бесплатный веб-сайт службы кодирования, вам нужно продемонстрировать некоторые усилия, а не просто написать несколько слов и посмотреть, как люди это делают.
привет @DaleK, спасибо за отзыв и редактирование. Я полностью отредактировал запрос.
@siggemannen спасибо за отзыв. Я обновил описание со всеми необходимыми деталями.
Это довольно распространенный запрос перекрывающихся диапазонов. Хотя ваша проверка условия перекрытия верна (End1 > Start2 && Start1 < End2
), расчет длины (количества) перекрытия неверен.
Вы можете рассчитать правильную величину перекрытия, используя LEAST(End1, End2) - GREATEST(Start1, Start2)
. Для вашего запроса это будет:
LEAST(CumulativePurchaseQuantity, CumulativeSaleQuantity)
- GREATEST(PreviousCumulativePurchaseQuantity, PreviousCumulativeSaleQuantity)
AS Quantity
Функции LEAST()
и GREATEST()
являются новыми в SQL Server 2022. Для более ранних версий вы можете вычислить эквивалент с помощью выражения CASE
или функции IIF()
:
IIF(CumulativePurchaseQuantity < CumulativeSaleQuantity,
CumulativePurchaseQuantity, CumulativeSaleQuantity)
- IIF(PreviousCumulativePurchaseQuantity > PreviousCumulativeSaleQuantity,
PreviousCumulativePurchaseQuantity, PreviousCumulativeSaleQuantity)
AS Quantity
Вышеупомянутое требует, чтобы вы предоставили значения PreviousCumulativeSaleQuantity
и CumulativePurchaseQuantity
в результатах MatchedSales
CTE или поочередно переместили этот расчет количества внутрь этого CTE. Вы можете обнаружить, что CTE больше не нужен, если объедините его содержимое с окончательным выбором.
Примеры результатов (с некоторыми дополнительными данными испытаний и разными столбцами):
См. эту db<>fiddle для демонстрации.
Вопросы должны быть самостоятельными, т.е. Содержимое вашей скрипки должно быть включено в ваш вопрос.