SQL 질문과 답변 게시판
회원 가입도 안하고 눈팅만 하다가 이렇게 도움을 구하고자 염치없이 글을 올립니다.
몇년전에 개발을 완료해서 사용하고 있던 회사로부터 재고금액 계산을 선입선출로 해놓지 않았다고
심한 컴플레인을 받고..ㅠㅠ..수정을 하려고 하는데..전혀 감이 잡히지 않습니다.
워낙 회계를 모르는 터라..몇일째 고생하고 있습니다.
아래의 구조로 [매입 테이블] 과 [매출 테이블] 이 구성되어 있습니다.
선입선출 방식으로 재고수량, 재고금액을 구하는 쿼리를 작성하고자 합니다.
혹시 임시 테이블이 필요한지 아니면 쿼리만으로 되는지..전문가 분들의 도움을 부탁드립니다.
매입과 매출 모두 한개의 테이블에 저장되어 있습니다.
--실제 테이블 구조 --
Orders 테이블( 전표 마스타 테이블로써 OrderType 이 "B"==> 매입, "S" ==> 매출)
OrderDetails (전표 서브 테이블로써 상품 목록을 갖고 있으며, OrderID 로 묶여 있음)
-- 예제 테이블 구조--
매입기록
------------------------------------
ProductCode, Qty, Price, OrderDate
------------------------------------
AAAAA 10 500 2010-09-01
AAAAA 10 600 2010-09-02
BBBBB 10 700 2010-09-03
AAAAA 10 800 2010-09-04
AAAAA 10 700 2010-09-05
BBBBB 10 800 2010-09-06
------------------------------------
매출기록
------------------------------------
ProductCode, Qty, Price, OrderDate
------------------------------------
AAAAA 11 1000 2010-09-06
AAAAA 12 1000 2010-09-07
BBBBB 5 1200 2010-09-07
BBBBB 6 1200 2010-09-08
------------------------------------
위와 같은 구조의 경우, 재고수량과 재고금액을 선입선출 방식으로 구하고 싶습니다.
전문가 분들의 도움을 부탁드립니다.
감사합니다.
조언 감사합니다. ^^.. 원가는 구하지 않고 전체 재고수량과 금액만 구하는 것입니다.
한번에 선입선출을 이용한 재고수량과 금액을 구하려는 건 아니고요, 재고수량은 VIEW 를 이용해서 만들어 놓으려고 합니다.
재고수량만 구한 VIEW 가 있다고 가정했을 때..문제는 현재고의 매입가격을 구해서 곱하는 것인데
루프문을 돌면서 재고수량이 마지막 매입수량보다 클 경우, 일부는 마지막 매입가격으로 또다른 일부는 그 전 매입가격으로 계산하는 것이
너무 속도 저하를 가져올 건 뻔하여..좀 더 효율적인 쿼리 방법을 알고 싶습니다.
가능하다면 그 부분만 예제를 적어주시면 큰 도움이 될 것 같습니다.
-----------------------------------------------------------------------------------------------------
/*
저희쪽에서도비슷한내용이있어서..
약간수정했습니다.. 쿼리로만할려다실패하고그냥함수를이용해서했는데…
속도는고만고만합니다..
그냥배치성으로표준원가계산과회계에재고원가제공용으로이용하는거라.
한달에한번작업이라그냥튜닝하지않고그냥쓰고있네요..
혹다른고수님들께서이부분고쳐야하거나개선할점이있으시면부탁드립니다.
재귀쿼리로도가능할건데그게잘안되네요…
*/
-----------------------------------------------------------------------------------------------------
DROP TABLE TEST
GO
SELECT *
INTO TEST
FROM (
SELECT 'B' OrderType, 'AAAAA' ProductCode ,10 Qty ,500 Price ,'2010-09-01' OrderDate UNION ALL
SELECT 'B' OrderType, 'AAAAA' ,10 ,600 ,'2010-09-02' UNION ALL
SELECT 'B' OrderType, 'BBBBB' ,10 ,700 ,'2010-09-03' UNION ALL
SELECT 'B' OrderType, 'BBBBB' ,12 ,800 ,'2010-09-03' UNION ALL
SELECT 'B' OrderType, 'AAAAA' ,10 ,800 ,'2010-09-04' UNION ALL
SELECT 'B' OrderType, 'AAAAA' ,10 ,700 ,'2010-09-05' UNION ALL
SELECT 'B' OrderType, 'BBBBB' ,10 ,800 ,'2010-09-06' UNION ALL
SELECT 'S' OrderType, 'AAAAA' ,11 ,1000 ,'2010-09-06' UNION ALL
SELECT 'S' OrderType, 'AAAAA' ,12 ,1000 ,'2010-09-07' UNION ALL
SELECT 'S' OrderType, 'BBBBB' ,5 ,1200 ,'2010-09-07' UNION ALL
SELECT 'S' OrderType, 'BBBBB' ,6 ,1200 ,'2010-09-08' ) R
------------------------------------------------------------
--
------------------------------------------------------------
DROP VIEW V_INPUT_QTYCOST
GO
CREATE VIEW V_INPUT_QTYCOST
AS
SELECT top 100 percent ProductCode , OrderDate , Price
, ROW_NUMBER() OVER (PARTITION BY ProductCode , OrderDate ORDER BY ProductCode , OrderDate DESC ) ITEM_SEQ
, SUM(Qty) Qty
FROM TEST
WHERE OrderType = 'B'
GROUP BY ProductCode , OrderDate , Price
ORDER BY ProductCode , OrderDate DESC
------------------------------------------------------------
--
------------------------------------------------------------
DROP FUNCTION [dbo].[Ufn_ItemQTY_CoST ]
GO
CREATE FUNCTION [dbo].[Ufn_ItemQTY_CoST ]
(
@ProductCode VARCHAR(10) ,
@ManagementDate VARCHAR(10) ,
@AgItemQTY INT
)
RETURNS INT
AS
BEGIN
declare @ItemQTY INT
declare @ItemSEQ INT
declare @ItemCOST INT
declare @ItemAMT INT
declare @CNT INT
declare @ManagementDateItemSEQ VARCHAR(50)
set @ItemSEQ = 1
set @ManagementDateItemSEQ = 'zzzzzzzzzzzzzzzzz'
SET @ItemAMT = 0
select @CNT = COUNT(*)
from V_INPUT_QTYCOST
where ProductCode = @ProductCode
and OrderDate <= @ManagementDate
WHILE (@CNT > 0 )
BEGIN
select top 1 @ManagementDate = OrderDate
, @ItemSEQ = ITEM_SEQ
, @ManagementDateItemSEQ = OrderDate + ltrim(convert(varchar,ITEM_SEQ))
, @ItemQTY = Qty
, @ItemCOST = Price
from V_INPUT_QTYCOST
where ProductCode = @ProductCode
and OrderDate <= @ManagementDate
and OrderDate + ltrim(convert(varchar,ITEM_SEQ)) < @ManagementDateItemSEQ
order by OrderDate desc , ITEM_SEQ desc
IF @AgItemQTY <= @ItemQTY
BEGIN
SET @ItemAMT = @ItemAMT + ( @AgItemQTY * @ItemCOST )
BREAK ;
END
ELSE
BEGIN
SET @ItemAMT = @ItemAMT + ( @ItemQTY * @ItemCOST )
END
SET @AgItemQTY = @AgItemQTY - @ItemQTY
END;
return @ItemAMT
END
-----------------------------------------------------------------------------------------------------
DECLARE @AG_DATE VARCHAR(10) SET @AG_DATE = '2010-09-04' SELECT RES.ProductCode ,RES.QTY , dbo.Ufn_ItemQTY_CoST(RES.ProductCode, @AG_DATE ,RES.QTY ) QTY_AMT FROM ( SELECT ProductCode , SUM(CASE WHEN OrderType = 'B' THEN QTY ELSE QTY * -1 END ) QTY FROM TEST WHERE OrderDate <= @AG_DATE GROUP BY ProductCode ) RES


선입선출은 단순 조회로는 상당히 복잡합니다.
입고 히스토리를 가지고 원가 계산을 해야하는데 입고만 있는게 아니라 입고반품도 있을수 있구요...
이런 부분을 가만 하고 남은 입고히스토리를 가지고 계산해야 하는데...
위 내용으로 보면 재고를 테이블 전체를 매번 sum해야 재고를 알수 있는 형태인데 자료건수가 얼마 안된다면 문제는 없지만
쌓이는 건수가 많아지면 시간이 갈수록 속도는 늘여질듯 합니다...
선입선출은 남은 재고를 기준으로 최근 매입된 건을 역으로 할당하시면 재고 금액을 알수 있구요...
매출 원가를 구하신다면 재고+매출수량을 가지고 위와 같은 방식으로 할당한다음 재고원가를 빼면 매출 우너가를 알수있습니다.
물론 원칙은 매출 원가 = 전월재고원가 + 매입원가 - 당월재고원가 이겠죠...
원가를 가져오는 부분은 그냥 함수로 만드시는게 편할듯 하구요. 속도는 감수해야 할듯...
필요에 따라서는 월 재고테이블을 만들고 이용하시는 방법도 있을듯 합니다. 월재고 테이블은 마감후 생성. 마감이 되면 매입매출은 수정 못하게 하는 시스템이 되어야 합니다. 물론 수정시는 마감을 다시 해야 하는 관리상 문제도 있구요.. 이걸 제대로 하지 않으면 원가 자료가 틀어지게 된다는건 당연하구요.