Click here to Skip to main content
15,887,421 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I have two tables. Purchase table and Closing Stock Table.

PURCHASE TABLE

Purchase table

AND CLOSING STOCK TABLE

Closing Stock table

I want to return all the rows from purchase table based on Transaction_Date desc till the sum(Purchase_qty) exceeds the Closing_Stock from CLOSING STOCK TABLE table.

Suppose for Material_Code AB01 in Purchase table closing_stock is 42 in CLOSING STOCK TABLE table. In that case rows returned will be as below.

Output

As you can see sum(Purchase_qty) of first 4 records is 43 so it exceed 42. I don't want to show the last column. So logic will be when sum(Purchase_qty) exceed closing_stock stop there.

What I have tried:

I have tried below query but that doesn't achieve the result. Any inputs is highly appreciated.

SQL
SELECT A.Material_Code,A.Transaction_Date, A.Purchase_qty, 
sum(A.Purchase_qty) OVER (ORDER BY Material_Code) AS total 
from Purchase A 
left join Closing_Stock B ON
A.Material_Code = B.Material_Code
where A.Purchase_qty <= B.Closing_Stock
order by A.Transaction_Date desc
Posted
Updated 28-Aug-17 19:22pm

Thanks for providing example data and schema, that was really helpful.

I've provided 2 possible queries to get out what you need. Option A is a normal group by query, and option B is a common table expression.

I've simulated your schema using temp tables, you should be able to replace #PurchaseTable and the other temp table with your tables names.

Not sure if any of it needs much explanation but Option A does the filtering of whether or not the total quantity is greater than closing stock in the HAVING clause.

Option B utilizes a common table expression (Using Common Table Expressions[^]) and handles the filtering of the data as a subquery in the where clause

SQL
IF OBJECT_ID('tempdb..#PurchaseTable') IS NOT NULL DROP TABLE #PurchaseTable
CREATE TABLE #PurchaseTable
(
	Id INT NULL,
	Material_Code VARCHAR(25) NULL,
	Batch VARCHAR(25) NULL,
	Purchase_qty INT NULL,
	Transaction_num VARCHAR(25) NULL,
	Transaction_Date DATETIME NULL
)

IF OBJECT_ID('tempdb..#ClosingStock') IS NOT NULL DROP TABLE #ClosingStock
CREATE TABLE #ClosingStock
(
	Id INT NULL,
	Material_Code VARCHAR(25) NULL,
	Batch VARCHAR(25) NULL,
	Closing_Stock INT NULL
)


INSERT INTO #ClosingStock ( Id ,Material_Code ,Batch ,Closing_Stock) VALUES  ( 1 , 'AB01' ,'E01' ,42 )
INSERT INTO #ClosingStock ( Id ,Material_Code ,Batch ,Closing_Stock) VALUES  ( 3 , 'AB02' ,'E02' ,77)
INSERT INTO #ClosingStock ( Id ,Material_Code ,Batch ,Closing_Stock) VALUES  ( 4 , 'AB03' ,'E03' ,44 )
INSERT INTO #ClosingStock ( Id ,Material_Code ,Batch ,Closing_Stock) VALUES  ( 5 , 'AB04' ,'E05' ,55 )
INSERT INTO #ClosingStock ( Id ,Material_Code ,Batch ,Closing_Stock) VALUES  ( 6 , 'AB05' ,'E05' ,142 )
INSERT INTO #ClosingStock ( Id ,Material_Code ,Batch ,Closing_Stock) VALUES  ( 7 , 'AB06' ,'E05' ,98 )

INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (1,'AB01','E01', 22, 'GR1' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (2,'AB01','E01', 12, 'GR2' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (3,'AB01','E01', 9, 'GR3' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (4,'AB01','E01', 11, 'GR4' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (5,'AB05','E05', 11, 'GR5' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (6,'AB05','E05', 22, 'GR6' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (7,'AB05','E05', 44, 'GR7' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (8,'AB05','E05', 29, 'GR8' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (9,'AB05','E05', 33, 'GR9' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (10,'AB05','E05', 34, 'GR10' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (11,'AB03','E03', 34, 'GR11' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (12,'AB03','E03', 6, 'GR12' ,'2017-08-28')



--Option A
SELECT 
	A.Material_Code, 
	A.Batch, 
	SUM(A.Purchase_qty) AS Total 
FROM #PurchaseTable AS A
GROUP BY A.Material_Code,  A.Batch
HAVING SUM(A.Purchase_qty) > (SELECT B.Closing_Stock FROM #ClosingStock AS B WHERE B.Material_Code = A.Material_Code AND B.Batch = A.Batch)


GO
--Option B
WITH StockCte (Material_Code, Batch, Total)
AS
(
	SELECT 
		A.Material_Code, 
		A.Batch, 
		SUM(A.Purchase_qty) AS Total 
	FROM #PurchaseTable AS A
	GROUP BY A.Material_Code,  A.Batch
)
SELECT * FROM StockCte AS A WHERE A.Total > (SELECT B.Closing_Stock FROM #ClosingStock AS B WHERE B.Material_Code = A.Material_Code AND B.Batch = A.Batch)


EDIT:

Updated based on OP's comment

SQL
--Stores all possible batch keys
DECLARE @BatchKeys TABLE (
	Id INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
	MaterialCode varchar(25) NULL,
	Batch varchar(25) NULL
);

--Temp storage of purchase id's by @BatchKeys, gets cleared after each batch key loop
DECLARE @PurchaseIds TABLE (
	Id INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
	PurchaseId INT NULL
)

--Stores all acceptable purchase id's below threshold
DECLARE @FinalPurchaseIds TABLE (
	Id INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
	PurchaseId INT NULL
);

INSERT INTO @BatchKeys( MaterialCode, Batch )
SELECT Material_Code, Batch FROM #ClosingStock

DECLARE @BatchId INT = (SELECT MIN(Id) FROM @BatchKeys)

WHILE @BatchId IS NOT NULL
BEGIN
	--//BEGIN Loop of batch keys
	DECLARE @MaterialCode VARCHAR(25) = (SELECT MaterialCode FROM @BatchKeys WHERE Id = @BatchId);
	DECLARE @Batch VARCHAR(25) = (SELECT Batch FROM @BatchKeys WHERE Id = @BatchId);
	
	DECLARE @ClosingTotal INT = (SELECT A.Closing_Stock FROM #ClosingStock AS A WHERE A.Material_Code = @MaterialCode AND A.Batch = @Batch)

	-- Add all purchase id's to table for analyzing
	INSERT INTO @PurchaseIds ( PurchaseId )	
		SELECT Id FROM #PurchaseTable AS A WHERE A.Material_Code = @MaterialCode AND A.Batch = @Batch
	
	DECLARE @PurchaseId INT = (SELECT MIN(Id) FROM @PurchaseIds)
	DECLARE @TotalPurchaseAmount INT = 0;

	WHILE @PurchaseId IS NOT NULL
	BEGIN
		PRINT 'Material Code: ' + @MaterialCode + ' Batch Code: ' + @Batch + ' - Total Purchase Qty ' + CAST(@TotalPurchaseAmount AS VARCHAR(20));
		--//BEGIN Loop of purchase id's 
		DECLARE @TargetPurchaseId INT = (SELECT A.PurchaseId FROM @PurchaseIds AS A WHERE A.Id = @PurchaseId)
		DECLARE @PurchaseAmount INT = (SELECT A.Purchase_qty FROM #PurchaseTable AS A WHERE A.Id = @TargetPurchaseId)

		-- Decide here if we are over the limit
		IF @PurchaseAmount + @TotalPurchaseAmount > @ClosingTotal
		BEGIN
			BREAK;
		END

		SET @TotalPurchaseAmount = @PurchaseAmount + @TotalPurchaseAmount;

		-- THis is just so we can keep track of what Id's are within the threshold
		INSERT INTO @FinalPurchaseIds (PurchaseId )
		SELECT @TargetPurchaseId
		
		
		--//END Loop of purchase id's
		SELECT @PurchaseId = MIN(Id) FROM @PurchaseIds WHERE Id > @PurchaseId
	END
        -- This table is temp storage of purchase id's by material/batch. So clear it out for the next loop so we don't total up qty's from other material/batch codes.
	DELETE FROM @PurchaseIds
	--//END Loop of batch keys
    SELECT @BatchId = MIN(Id) FROM @BatchKeys WHERE Id > @BatchId
END;

--Lists all purchase records below Closing_Stock
SELECT A.*, B.Closing_Stock FROM #PurchaseTable AS A
JOIN #ClosingStock AS B ON B.Material_Code = A.Material_Code AND A.Batch = B.Batch 
WHERE A.Id IN (SELECT PurchaseId FROM @FinalPurchaseIds)

--Validation query to show total qty with CLosing Stock count
SELECT A.Material_Code, A.Batch, SUM(A.Purchase_qty), B.Closing_Stock FROM #PurchaseTable AS A
JOIN #ClosingStock AS B ON B.Material_Code = A.Material_Code AND A.Batch = B.Batch 
WHERE A.Id IN (SELECT PurchaseId FROM @FinalPurchaseIds)
GROUP BY A.Material_Code, A.Batch, B.Closing_Stock
 
Share this answer
 
v4
Comments
Member 9017207 28-Aug-17 22:29pm    
THis is something totally different what I expected in the result. I wanted to show all rows till the sum(purchase_qty) exceeds closing stock. Even show next row. BUt that's it.
David_Wimbley 29-Aug-17 0:00am    
Sorry i didn't read your question carefully. I've updated my answer, although personally I think you need to alter your schema.

With the result set you want, you've got to analyze each Records in your purchase table row by row in order to know if its over the closing threshold. Right now with the sample data it runs quick...but you have a huge table with a ton of data...analyzing stuff row by row is going to be a bottleneck for you.

My suggestion would be to alter your schema to store a running total by MaterialCode and Batch, whether in a new table or your purchases table, and then you'd be able to simply say something like SELECT * FROM PurchasesTable AS A WHERE A.TotalQty < (SELECT Closing_Qty FROM ClosingQty AS B WHERE B.MaterialCode = A.MaterialCode AND B.Batch = A.BAtch) That way the query would perform much faster, not have to analyze your data row by row and hopefully not become a bottleneck in your app.

Just a suggestion, you can certainly do as you see fit but the above edit in my answer should provide, if i understood you correctly this time, what you need
I had to borrow the code to create the temporary tables from David [^] :) The below query will sum the purchase quantity and then filter out the record that exceeded the closing stock. Per your example on AB01, I don't see an easy way to display 4 rows and hide the fifth because 43 > 42. Maybe someone can help you throw an extra logic in it.

SQL
IF OBJECT_ID('tempdb..#PurchaseTable') IS NOT NULL DROP TABLE #PurchaseTable
CREATE TABLE #PurchaseTable
(
	Id INT NULL,
	Material_Code VARCHAR(25) NULL,
	Batch VARCHAR(25) NULL,
	Purchase_qty INT NULL,
	Transaction_num VARCHAR(25) NULL,
	Transaction_Date DATETIME NULL
)
 
IF OBJECT_ID('tempdb..#ClosingStock') IS NOT NULL DROP TABLE #ClosingStock
CREATE TABLE #ClosingStock
(
	Id INT NULL,
	Material_Code VARCHAR(25) NULL,
	Batch VARCHAR(25) NULL,
	Closing_Stock INT NULL
)
 

INSERT INTO #ClosingStock ( Id ,Material_Code ,Batch ,Closing_Stock) VALUES  ( 1 , 'AB01' ,'E01' ,42 )
INSERT INTO #ClosingStock ( Id ,Material_Code ,Batch ,Closing_Stock) VALUES  ( 3 , 'AB02' ,'E02' ,77)
INSERT INTO #ClosingStock ( Id ,Material_Code ,Batch ,Closing_Stock) VALUES  ( 4 , 'AB03' ,'E03' ,44 )
INSERT INTO #ClosingStock ( Id ,Material_Code ,Batch ,Closing_Stock) VALUES  ( 5 , 'AB04' ,'E05' ,55 )
INSERT INTO #ClosingStock ( Id ,Material_Code ,Batch ,Closing_Stock) VALUES  ( 6 , 'AB05' ,'E05' ,142 )
INSERT INTO #ClosingStock ( Id ,Material_Code ,Batch ,Closing_Stock) VALUES  ( 7 , 'AB06' ,'E05' ,98 )
 
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (1,'AB01','E01', 22, 'GR1' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (2,'AB01','E01', 17, 'GR2' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (3,'AB01','E01', 9, 'GR3' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (4,'AB01','E01', 11, 'GR4' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (5,'AB05','E05', 11, 'GR5' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (6,'AB05','E05', 22, 'GR6' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (7,'AB05','E05', 44, 'GR7' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (8,'AB05','E05', 29, 'GR8' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (9,'AB05','E05', 33, 'GR9' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (10,'AB05','E05', 34, 'GR10' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (11,'AB03','E03', 34, 'GR11' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (12,'AB03','E03', 6, 'GR12' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (13,'AB01','E01', 6, 'GR13' ,'2017-08-28')

;WITH tempSUM AS (
select 
    id, Material_Code, Batch, Purchase_qty, Transaction_num,Transaction_Date,
    sum(Purchase_qty) over (partition by Material_Code order by Material_Code, id DESC) running_total
from #PurchaseTable
)
SELECT s.*, Closing_Stock FROM tempSUM s JOIN
#ClosingStock c ON s.Material_Code=c.Material_Code AND s.Batch = c.Batch
WHERE Closing_Stock > running_total


Output:
[Output]
HTML
id	Material_Code	Batch	Purchase_qty	Transaction_num	Transaction_Date	running_total	Closing_Stock
13	AB01	E01	6	GR13	2017-08-28 00:00:00.000	6	42
4	AB01	E01	11	GR4	2017-08-28 00:00:00.000	17	42
3	AB01	E01	9	GR3	2017-08-28 00:00:00.000	26	42
12	AB03	E03	6	GR12	2017-08-28 00:00:00.000	6	44
11	AB03	E03	34	GR11	2017-08-28 00:00:00.000	40	44
10	AB05	E05	34	GR10	2017-08-28 00:00:00.000	34	142
9	AB05	E05	33	GR9	2017-08-28 00:00:00.000	67	142
8	AB05	E05	29	GR8	2017-08-28 00:00:00.000	96	142
7	AB05	E05	44	GR7	2017-08-28 00:00:00.000	140	142


Resources:
sql - Running total by grouped records in table - Stack Overflow[^]
 
Share this answer
 
v2
Comments
Karthik_Mahalingam 31-Aug-17 3:52am    
5
Bryian Tan 31-Aug-17 23:50pm    
Thank you Sir!!!

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900