How can I avoid the infinite loop? - sql

Getting an infinite loop. the cursor fetches one single record for testing purposes but it cannot leave the fetch loop. It is the first time I am using microsoft sql. I am not sure about the syntax and the positioning of the fetch statement.
I am trying to update invoice details , that is for one invoice there are more than one items. Please help.
Begin--1
Declare #Det_inv_cnt int, ---------count invoice in detail
#Cnt_item5 int, --------count item number 5
#Cnt_item6 int, --------count item number 6
#sprice money, --------sum vatable amt
#vat_amt money, --------vat amt
#inv int, --------invoice
#customer int, --------customer id
#sdate datetime, --------date
#edate datetime --------date
begin--2
Declare inv_id Cursor For (Select invoice from plat.dbo.iheader
Where customer in (Select id from plat.dbo.customer
Where custype = 1
and id = 2601)
and invoice = 2628
OPEN inv_id
FETCH NEXT FROM inv_id INTO #inv -----------------------------next invoice from cursor
WHILE ##FETCH_STATUS=0
BEGIN--------begin4
set #sprice = 0;
set #vat_amt = 0;
set #det_inv_cnt = 0;
set #cnt_item5 = 0;
set #cnt_item6 = 0;
set #customer = (select customer from plat.dbo.iheader where #inv = invoice);
set #Det_inv_cnt = (select count(1)
from plat.dbo.idetail d
where d.item in ('2','4')
and d.invoice = #inv)
If (#Det_inv_cnt>0) ---------------------------------------end if 10
Begin -----begin5 end if 10
--calculate vatable amt and vat round(0.15*sum(a.price),2)
set #sprice = (Select sum(price)
From plat.dbo.idetail
Where invoice = #inv
And item in ('2','4'));
set #vat_amt = (Select (0.15*(sum(price)))
From plat.dbo.idetail
Where invoice = #inv
And item in ('2','4'));
set #cnt_item5 = (Select count(1)
From plat.dbo.idetail
where item = '5'
and invoice = #inv
and customer = #customer);
set #sdate = (select sdate
From idetail
Where item ='2'
or item = '4'
And invoice = #inv);
set #edate = (select edate
From idetail
Where item ='2'
or item = '4'
And invoice = #inv);
--select details to insert new record
INSERT INTO idetail
( invoice
, item
, descriptio
, qty
, price
, date
, customer
, sdate
, edate
--,timestamp
,profileid
, id_item_code
, id_taxable_amt
, id_item_instock
, id_extended
, id_category_id
, id_subcategory_id
, id_rg_id
, id_svc_id
, id_sbd_id
, id_rate_id
, id_prorated )
values ( #inv --invoice
,'TAX#1003' --item
,'#VAT (Rs. '+ #sprice +' x 15%)' --descriptio
,1 --qty
,#vat_amt --price
,#sdate --date
,#customer --customer
,#sdate --sdate
,#edate --edate
--,CURRENT_TIMESTAMP
,-1 --profileid
,0 --itemcode
,#SPRICE --taxable amt
,null --id_item_instock, int,
,#VAT_AMT --id_extended, money,
,1002 --id_category_id, int,
,0 --id_subcategory_id, int,
,0 --id_rg_id, int,
,0 --id_svc_id, int,
,0 --id_sbd_id, int,
,0 --id_rate_id, int,
,null); --id_prorated, char(1)
/*From dbo.plat.idetail
Where item ='2'
or item = '4'
And invoice = #inv;
*/
Begin --11
If #Cnt_item5 > 0 ------------------------------------------------------if cnt_item 5 >0
Begin------6
--delete old VAT record
Delete from idetail
where invoice = #inv
and item = '5'
and customer = #customer;
--update roundoff item number 6
set #cnt_item6 = (Select count(1)
from plat.dbo.idetail w
where w.invoice = #inv
and w.customer = #customer
and w.item = '6')
If #Cnt_item6>0 -------------------------------------------if cnt_item6>0
Begin---7
update plat.dbo.idetail
set item = '5'
where invoice = #inv
and customer = #customer
and item = '6';
End;--7
Else -------------------------------------------------------------else cnt_item6
Begin--8
Insert into err_log(
El_customer,
El_Invoice,
El_msg,
El_date)
Values (
#customer,
#inv,
('Residential Script - Count of Item 6 returned 0'),
getdate());
End;--8
End; --end 6 of begin cnt5>0
End;--11
-- End;---5
---update header with vat_amt
Begin--9
Update plat.dbo.iheader
Set taxtotal = #vat_amt
where invoice = #inv
and customer = #customer;
end;--9
end;--5
Else ----------else 10 dev_inv_cnt
/* no item found the insert in table*/
begin--10
Insert into err_log(
El_customer,
El_Invoice,
El_msg,
El_date)
Values (
#customer
,#inv
,('Residential - Count of Item 2 and 4 returned 0')
,Getdate());
end;--10
--End; --------end begin 4 while
FETCH NEXT FROM inv_id INTO #inv --get nxt record for cursor invt id
End; ----begin 4
CLOSE inv_id;
DEALLOCATE inv_id;
--GO
end;---begin 2
End;--1

The FETCH NEXT FROM Inv_IdINTO #invnext instruction is within the ELSE branch of the former IF statement. It needs to be put after the ELSE branch

You're only fetching from the cursor once and your while loop is checking the status of that fetch. Since it presumably succeeded, that condition is always evaluated as true.
For what it's worth, here's the template that I use for my cursors:
declare cursorName cursor fast_forward local for
-- your select goes here
OPEN cursorName
WHILE (1=1)
BEGIN
FETCH NEXT FROM cursorName INTO --variable list here
if (##FETCH_STATUS <> 0)
BREAK;
-- do stuff here
END
CLOSE cursorName
DEALLOCATE cursorName

Related

How to get records that between m records back and n records forward from a reference row - Non-sequential data

My scenario is as follows:
I have a reference record, say, ProductId = 1
The records each have a non-unique ItemTypeId
I would like to fetch records that exists between the following points
START POINT being 2 records BACKWARDS of type ItemTypeId = 1, from record of ProductId =1
END POINT being 3 records FORWARDS of type ItemTypeId = 1, from record of ProductId = 1
The query should get ALL data between the two points, inclusively
Here's a picture that illustrates this better than my words:
How would I structure my query to do this?
Any better way to do it without temp tables?
Thank-you!
Note that for this to work at all, you need that record ID to be an actual column in the table. Rows have no inherent order in a table.
With that in place, you can use LAG and LEAD to get what you want:
CREATE TABLE #t
(
RecordId INT IDENTITY(1,1),
ProductId INT,
ItemType INT
);
INSERT INTO #t(ProductId, ItemType)
VALUES
(5,1),(3,1),(7,3),(6,1),(2,7),
(1,1),(7,3),(8,1),(10,3),(9,5),
(11,1),(19,1),(17,4),(13,3);
WITH c1 AS
(
SELECT ProductId,
RecordId,
LAG(RecordId,2) OVER (ORDER BY RecordId) AS Back2,
LEAD(RecordId,3) OVER (ORDER BY RecordId) AS Forward3
FROM #t
WHERE ItemType = (SELECT ItemType FROM #t WHERE ProductId = 1)
),c2 AS
(
SELECT c1.Back2, c1.Forward3 FROM c1
WHERE c1.ProductId = 1
)
SELECT #t.*
FROM #t
INNER JOIN c2 ON #t.RecordId BETWEEN c2.Back2 AND c2.Forward3;
If you wanna do without using temp tables as you ask, the following solution work.
But it is not very nice i agree.
Well this is what i done :
CREATE DATABASE TEST;
USE TEST
CREATE TABLE PRODUCT
(
ProductId INT,
ItemType INT
)
INSERT INTO PRODUCT
VALUES
(5,1),
(3,1),
(7,3),
(6,1),
(2,7),
(1,1),
(7,3),
(8,1),
(10,3),
(9,5),
(11,1),
(19,1),
(17,4),
(13,3)
DECLARE product_cursor CURSOR FOR
SELECT * FROM PRODUCT;
OPEN product_cursor
DECLARE
#ProductId INT,
#ItemId INT,
#END_FETCH INT,
#countFrom INT,
#countTo INT
DECLARE #TableResult TABLE
(
RProductId INT,
RItemId INT
)
FETCH NEXT FROM product_cursor
INTO #ProductId, #ItemId
SET #END_FETCH = 0
SET #countFrom = 0
SET #countTo = 0
WHILE ##FETCH_STATUS = 0 AND #END_FETCH = 0
BEGIN
IF #ItemId = 1 AND (#countFrom = 0 AND #countTo = 0)
BEGIN
SET #countFrom = 3
SET #countTo = 3
END
ELSE
BEGIN
IF #countFrom > 0
BEGIN
--SELECT 'INSERTION : ' ,#ProductId,#ItemId
INSERT INTO #TableResult VALUES(#ProductId, #ItemId)
IF #ItemId = 1
BEGIN
SET #countFrom -= 1
--SELECT 'CountFrom : ', #countFrom
END
END
ELSE
BEGIN
IF #countTo > 0
BEGIN
--SELECT 'INSERTION : ' ,#ProductId,#ItemId
INSERT INTO #TableResult VALUES(#ProductId, #ItemId)
IF #ItemId = 1
BEGIN
SET #countTo -= 1
--SELECT 'CountTO : ', #countTo
END
END
ELSE
BEGIN
SET #END_FETCH = 1
END
END
END
FETCH NEXT FROM product_cursor
INTO #ProductId, #ItemId
END
CLOSE product_cursor
DEALLOCATE product_cursor
SELECT * FROM #TableResult
And this is the result i got :
RProductId RItemId
3 1
7 3
6 1
2 7
1 1
7 3
8 1
10 3
9 5
11 1
19 1
But i prefer the solution of #James Casey.
By the way, why won't you use temp table ?

Invoice creation in sql - create a stored procedure

I want to create a procedure in which the following details should be displayed:-
I have created this query but I am not getting the right results. I have attached the table schema.
TABLES WE ARE USING:- InvoiceData, CustomerDetails and InvoiceItems
Table Schema ->
1. InvoiceItems TABLE1
2. CustomerDetails Table2
3. InvoiceDetails enter image description here
There are 2 sections for invoice:-
In the first section of the Invoice, Below details should be displayed.
Invoice Information section
In the second section of the invoice, the below details should be displayed:-
Invoice Items description section
I am attaching the query below:-
alter Procedure SaveInvoiceDetails
(
#CustomerId varchar(50),
#InvoiceNumber varchar(50),
#InvoiceDate date,
#InvoiceMonth int,
#FromDate date,
#ToDate date,
#Rate int,
#Quantity int,
#ActualAmount int,
#ZoneId int
)
as
set nocount on;
begin
Declare #TotalRows int
Declare #NumPages int
set #TotalRows = 0
Select ROW_NUMBER() over (order by C.CustomerId) as InvoiceRow,
C.CustomerId, I.InvoiceNumber, I.InvoiceDate, I.FromDate, I.ToDate,
I.InvoiceMonth, I.Rate, I.ActualAmount, I.Quantity, C.ZoneId,
C.BillingAmount
into #tempInvoice
from ConsumerMST_LKO C
inner join InvoiceDetails I
on C.CustomerId = I.CustomerId
inner join INVOICEITEMS II
on I.InvoiceNumber = II.INVOICEID
where InvoiceNumber = #InvoiceNumber AND InvoiceDate = #InvoiceDate AND
InvoiceMonth = #InvoiceMonth
AND FromDate =#FromDate AND ToDate = #ToDate AND ActualAmount =
#ActualAmount
set #TotalRows = ##ROWCOUNT
If #TotalRows = 0
Begin
set #TotalRows = #TotalRows + 1
Insert #tempInvoice
(
InvoiceNumber,
InvoiceDate,
InvoiceMonth,
ZoneId,
Rate,
Quantity,
BillingAmount,
FromDate,
ToDate
)
VALUES
(#TotalRows
, ''
,''
,''
,0
,0
,0
,0
,''
,0)
End
End
SELECT * FROM #tempInvoice ORDER BY InvoiceRow asc
return
So I am expecting that you got all the records in your #tempInvoice from your procedure. And face problem in only generating invoice number with the format you provided.
For each region you already have a specific code generated at your end ( I am guessing).
#regioncode ---- this table contain record of zoneid and zoneocde for each area
If you don't have any table for region then prepare one before going forward as this needs you everywhere in your code section.
At the end pf your procedure need to update this to return your required result.
; with cte as (
select row_number() over (partition by ZoneId order by InvoiceDate) as Slno, * from #tempInvoice as t inner join #regioncode as r on t.zoneid=r.zoneid
)
select zonecode + '_' + cast(slno as varchar(10)) as Uniquecode, * from cte

Used Cursor inside cursor for update it is showing o rows effected, logic is working when i tried manually

stored procedure
Used Cursor inside cursor for update it is showing 0 rows effected, logic is working when i tried manually, declaring and closing done properly.
any changes do i need to do
or any alternatives than cursor.
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
ALTER PROCEDURE [dbo].[POS_Discount_Report]
#OutletId INT = NULL,
#FromDate DATE = NULL,
#ToDate DATE = NULL,
#DiscountPercent DECIMAL = NULL
AS
begin
SELECT #CutOffInvoiceAmount = AVG(InvoiceAmount) FROM POS_SalesReceiptMaster WHERE StampDate BETWEEN #FromDate AND #ToDate
DECLARE Receipt_cursor CURSOR FOR
SELECT Id FROM POS_SalesReceiptMaster WHERE StampDate BETWEEN #FromDate AND #ToDate AND InvoiceAmount <= #CutOffInvoiceAmount
OPEN Receipt_cursor
FETCH NEXT FROM Receipt_cursor
INTO #ReceiptId
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE Item_cursor CURSOR FOR
SELECT Id FROM Updated_SalesReceiptItems WHERE ReceiptId = #ReceiptId
OPEN Item_cursor
FETCH NEXT FROM Item_cursor
INTO #ID
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #Percentage = Percentage, #ItemPrice = Price FROM
Updated_SalesReceiptItems WHERE Id = #ID
IF #Percentage = 5
BEGIN
SELECT #UpdatePercentage = Tax5 FROM Updated_Master
Where Percentage = #DiscountPercent
END
ELSE
BEGIN
#UpdatePercentage = 5
END
UPDATE Updated_SalesReceiptItems
SET ProductId = Product.ProductId,
Actualprice = Product.Actualprice,
Quantity = Product.Qty,
ProductName = Product.ProductName,
unit = Product.unit,
CategoryName= Product.CategoryName,
Percentage= Product.Percentage,
Amount = Product.Amount FROM
(SELECT TOP 1 PM.ProductId, ProductCode,
dbo.fn_Get_ProductPrice_By_Outlet(ProductId,#OutletId)
AS
Actualprice,
(CASE WHEN ( dbo.fn_Get_ProductPrice_By_Outlet(#OutletId, ProductId) != 0)
THEN (#ItemPrice / dbo.fn_Get_ProductPrice_By_Outlet(#OutletId, ProductId))
ELSE 0
END) AS Qty,
ProductName, Unit, CategoryName, #UpdatePercentage AS Percentage,
dbo.fn_Get_ProductPrice_By_Outlet(#OutletId, ProductId) * (#UpdatePercentage/100) AS TaxAmount
FROM dbo.Products_Master PM
INNER JOIN ProductCategory_Master CM ON PM.CategoryId = CM.CategoryId
INNER JOIN tax_master TM ON PM.TaxId = TM.Id
WHERE (#ItemPrice) % nullif(dbo.fn_Get_ProductPrice_By_Outlet(#OutletId, ProductId),0) = 0
AND Percentage = #UpdatePercentage) Product
WHERE Id = #ID
end
FETCH NEXT FROM Item_cursor
INTO #ID
END
CLOSE Item_cursor;
DEALLOCATE Item_cursor;
FETCH NEXT FROM Receipt_cursor
INTO #ReceiptId
END
CLOSE Receipt_cursor;
DEALLOCATE Receipt_cursor;
END
Okay, this is pretty scrappy, and probably won't work without some fixing, but it should give you the general pattern for doing all of this in a single query?
ALTER PROCEDURE POS_Discount_Report (
#OutletId INT = NULL,
#FromDate DATE = NULL,
#ToDate DATE = NULL,
#DiscountPercent DECIMAL = NULL)
AS
BEGIN
DECLARE #CutOffInvoiceAmount NUMERIC(19,2); --?? seems to be missing from original procedure
SELECT #CutOffInvoiceAmount = AVG(InvoiceAmount) FROM POS_SalesReceiptMaster WHERE StampDate BETWEEN #FromDate AND #ToDate; --What happens if one or both of these is NULL?
--CTEs
WITH Receipt AS (
SELECT Id FROM POS_SalesReceiptMaster WHERE StampDate BETWEEN #FromDate AND #ToDate AND InvoiceAmount <= #CutOffInvoiceAmount),
Item AS (
SELECT Id FROM Updated_SalesReceiptItems s INNER JOIN Receipt r ON s.ReceiptId = r.Id),
PercentQuery AS (
SELECT i.Id, u.[Percentage], u.Price FROM Updated_SalesReceiptItems u INNER JOIN Item i ON u.Id = i.Id),
UpdatePercent AS (
SELECT p.Id, p.[Percentage], p.Price, CASE WHEN p.[Percentage] = 5 THEN u.Tax5 ELSE 5 END AS UpdatePercentage FROM PercentQuery p INNER JOIN Updated_Master u ON u.[Percentage] = #DiscountPercent)
UPDATE
u
SET
ProductId = pm.ProductId,
Actualprice = dbo.fn_Get_ProductPrice_By_Outlet(ProductId, #OutletId),
Quantity =
CASE
WHEN (dbo.fn_Get_ProductPrice_By_Outlet(#OutletId, ProductId) != 0)
THEN (#ItemPrice / dbo.fn_Get_ProductPrice_By_Outlet(#OutletId, ProductId))
ELSE 0
END,
ProductName = pm.ProductName,
unit = pm.unit, --not sure on the alias here, as it's missing in the original query
CategoryName = pm.CategoryName,
[Percentage] = u.UpdatePercentage,
Amount = dbo.fn_Get_ProductPrice_By_Outlet(#OutletId, ProductId) * (u.UpdatePercentage / 100) --although this was TaxAmount originally??!
FROM
dbo.Products_Master pm
INNER JOIN ProductCategory_Master cm ON cm.CategoryId = pm.CategoryId
INNER JOIN tax_master tm ON tm.Id = pm.TaxId
INNER JOIN UpdatePercent up ON up.Id = pm.Id
INNER JOIN Updated_SalesReceiptItems u ON u.Id = up.Id
WHERE
(p.Price) % NULLIF(dbo.fn_Get_ProductPrice_By_Outlet(#OutletId, pm.ProductId), 0) = 0
AND [Percentage] = UpdatePercentage;
END;
Basically, I use nested common-table expressions to perform the same action as your original cursors, but these are now set-based. This means I can JOIN the results to the table to be updated and perform all of the updates in a single hit.
I almost certainly got some of this wrong, as I could see a number of parts in your original query that just seemed incorrect?

How to implement FIFO in sql

I working on FIFO implementation in sql.
I have Batch number concept in my application.
If suppose I am selling on inventory then my application should tell me that which inventory is the first come.
Lets. Say I purchased Inventory 'A' on 4th-Aug, 5th-Aug & 6th-Aug
On 4th Aug - A Inventory has batch number BT002 - 10 (Qty)
On 5th Aug - A's Inventory has batch number BT003 - 15 (Qty)
On 6th Aug - A's Inventory has batch number BT001 - 10 (Qty)
So, Now I am having stock Now in my hand as following :
A Inventory
BT002 - 10 - 4-Aug
BT003 - 15 - 5-Aug
BT001 - 10 - 6-Aug
Now If I want to sell that Inventory to anybody then my application should tell me that I should sell
BT002 (Batch number) inventory first beacause it came first.
That was the concept I am using in my application.
Now I want to sell 15 Qty from 'A' (Inventory).
Then O/p Should be like this :
BT002 - 10
BT003 - 5
Here's My Query :
SELECT ISNULL(SUM(qty),0) AS Qty,batch_no,accept_date FROM RS_GIN_Master
GROUP BY batch_no,accept_date
HAVING ISNULL(SUM(qty),0) <= 15
ORDER BY accept_date asc
O/p Of Given Query :
How can I get O/P like this :
BT002 - 10
BT003 - 5
Any Help will be appreciated.
Thank you in Advance.
This should work for you:
Working sample on Fiddle
CREATE FUNCTION [dbo].[GetBatchAmounts]
(
#requestedAmount int
)
RETURNS
#tBatchResults TABLE
(
Batch nvarchar(50),
Amount int
)
AS
BEGIN
/*This is just a mock of ersults of your query*/
DECLARE #RS_GIN_Master TABLE(
Qty int,
batch_no NVARCHAR(max),
accept_date DATETIME
)
insert into #RS_GIN_Master(Qty,batch_no,accept_date)
SELECT 10,'BT002', CAST(CAST(2014 AS varchar) + '-' + CAST(8 AS varchar) + '-' + CAST(4 AS varchar) AS DATETIME)
insert into #RS_GIN_Master(Qty,batch_no,accept_date)
SELECT 10,'BT003', CAST(CAST(2014 AS varchar) + '-' + CAST(8 AS varchar) + '-' + CAST(5 AS varchar) AS DATETIME)
insert into #RS_GIN_Master(Qty,batch_no,accept_date)
SELECT 10,'BT001', CAST(CAST(2014 AS varchar) + '-' + CAST(8 AS varchar) + '-' + CAST(6 AS varchar) AS DATETIME)
/*---------------------------*/
DECLARE #Qty int
DECLARE #batch_no NVARCHAR(max)
DECLARE #accept_date DATETIME
DECLARE myCursor CURSOR FOR
SELECT Qty, batch_no, accept_date FROM #RS_GIN_Master ORDER BY accept_date ASC
OPEN myCursor
FETCH NEXT FROM myCursor INTO #Qty, #batch_no,#accept_date
WHILE (##FETCH_STATUS = 0 AND #requestedAmount > 0 )
BEGIN
Declare #actualQty int
IF #requestedAmount > #Qty
SET #actualQty = #Qty
ELSE
SET #actualQty = #requestedAmount
INSERT INTO #tBatchResults (batch, Amount)
SELECT #batch_no, #actualQty
set #requestedAmount = #requestedAmount - #actualQty
FETCH NEXT FROM myCursor INTO #Qty, #batch_no,#accept_date
END /*WHILE*/
CLOSE myCursor
DEALLOCATE myCursor
RETURN
END
Just make sure to replace the marked part of the function with your query...
You need to create a stored procedure in your database and taking the quantity from your stock table. And you should also have the id of each record to update that records from where u have taken that qty.
Alter PROCEDURE sp_UpdateStockForSale
#batchNO varchar(10),
#qty decimal(9,3)
AS
BEGIN
Create Table #tmpOutput(ID int identity(1,1), StockID int, batchNo varchar(10), qty decimal(9,3));
SET NOCOUNT ON;
DECLARE #ID int;
DECLARE #Stock Decimal(9,3);
DECLARE #TEMPID int;
Select #TEMPID=(Max(ID)+1) From RS_GIN_Master Where qty > 0 And batch_no = #batchNO;
While (#qty > 0) BEGIN
Select #ID=ID, #Stock=qty From RS_GIN_Master Where qty > 0 And batch_no = #batchNO AND ID < #TEMPID Order By accept_date Desc;
--If Outward Qty is more than Stock
IF (#Stock < #qty) BEGIN
SET #qty = #qty - #Stock;
SET #Stock = 0;
END
--If Outward Qty is less than Stock
ELSE BEGIN
SET #Stock = #Stock - #qty;
SET #qty = 0;
END
Insert Into #tmpOutput(StockID,batchNo,qty)Values(#ID,#batchNO,#Stock);
SET #TEMPID = #ID;
--This will update that record don't need it now.
--Update RS_GIN_Master Set qty = #Stock Where ID=#ID
END
Select StockID, batchNo, qty From #tmpOutput;
END
GO
The above example is not compiled but, you can get the logic how you can retrieve the records from your stock table according to FIFO method. You can use accept_date instead of ID in RS_GIN_Master table. but, i would prefer to make it unique so, if i want to get a specific record then it can be possible.
One query .. like this
This should be tweaked for you case as you have groups and other stuff, is only for example purposes.
;with qty as (
select 15 as value
)
,l as (
select
ROW_NUMBER () over (order by accept_date desc) rn
,*
from xxx
)
,q as (
select
batch_no
,accept_date
,case when value>qty then value-qty else 0 end as remainder
,case when value>qty then qty else value end as used
,rn
from l
cross join qty
where rn=1
union all
select
r.batch_no
,r.accept_date
,case when q.remainder>r.qty then q.remainder-r.qty else 0 end as remainder
,case when q.remainder>r.qty then r.qty else q.remainder end as used
,r.rn
from q
join l r
on q.rn+1 = r.rn
where q.remainder!=0
)
select *
from q
where used != 0
and the fiffle for it http://sqlfiddle.com/#!6/9b063/34/0
Below should work for you
Create table RS_GIN_Master
(id int,
qty int,
batch_no varchar(5),
accept_date Datetime
)
GO
Insert into RS_GIN_Master (id, qty, batch_no, accept_date)
values(1,10,'BT001','2018-04-06')
Insert into RS_GIN_Master (id, qty, batch_no, accept_date)
values(2,10,'BT002','2018-04-04')
Insert into RS_GIN_Master (id, qty, batch_no, accept_date)
values(3,15,'BT003','2018-05-06')
GO
----------------------------
CREATE PROC FIFO
#TakenQty int
AS
BEGIN
WITH cte AS (SELECT *, SUM(qty) OVER (ORDER BY accept_date, id ASC) as CumQty FROM RS_GIN_Master WHERE qty>0)
SELECT TOP ((SELECT COUNT(*) FROM cte WHERE CumQty <#TakenQty)+1) batch_no, accept_date,
CASE
WHEN CumQty<#TakenQty THEN qty
ELSE #TakenQty -(CumQty-Qty)
END AS TakenOut
FROM cte
END
Result
| batch_no | accept_date | TakenOut |
|----------|----------------------|----------|
| BT002 | 2018-04-04T00:00:00Z | 10 |
| BT001 | 2018-04-06T00:00:00Z | 5 |
http://www.sqlfiddle.com/#!18/f7ee7/1

SQL Query to retrieve the last records till the quantity purchased reaches the total quantity in stock

I have a table that have the ItemCode and Quantity in stock and another table that contains the purchases.
I want a query to get the Quantity in stock (ex. Qty = 5) and to take the purchase table to get the purchase invoices by descending order and take the Item Prices.
The Query has to keep retrieving records from the Purchase table according to the Quantity till we reach sum of Quantity in stock = 5.
ex.
**Purchase No ItemCode Qty Cost Price**
2 123 2 100
3 123 10 105
6 123 2 100
8 123 1 90
9 123 2 120
---------------------------------------------
**ItemCode Qty in Stock**
123 5
--------------------------------------------
In this example I want the query to retrieve for me the last 3 invoices (9,8 and 6) because the Qty (2+1+2 = 5)
Is there any suggestion .
Thank you in advance
This script should do the job.
/* SQL SCRIPT BEGIN */
create table #tmp (PurchaseNo int, ItemCode int, Qty int)
insert into #tmp (PurchaseNo, ItemCode, Qty)
select
p1.PurchaseNo, p1.ItemCode, sum(t.Qty) as Qty
from
Purchases p1
join
(
select
p2.PurchaseNo,
p2.ItemCode, p2.Qty
from
Purchases p2
) t on p1.PurchaseNo <= t.PurchaseNo and p1.ItemCode = t.ItemCode
group by p1.PurchaseNo, p1.ItemCode
order by p1.ItemCode, sum(t.Qty) asc
select * From #tmp
where
ItemCode = 123
and
Qty < 5
union
select top 1 * From #tmp
where
ItemCode = 123
and
Qty >= 5
order by PurchaseNo desc
drop table #tmp
/* SQL SCRIPT END */
Hi This can be the solution :
Here I have Used Result Table which will store the result.
I have used three tables Purchage(PurchageNo,ItemCode,Qty) , Stock(ItemCode,QtyInStock) and result(PurchageNo).
Full Workable Code is Here:
DECLARE #ItemCode int;
DECLARE #AvailableQty int;
SET #ItemCode = 123 ;
SET #AvailableQty = (select QtyInStock from Stock where ItemCode = #ItemCode);
SELECT
RowNum = ROW_NUMBER() OVER(ORDER BY PurchageNo),*
INTO #PurchageTemp
FROM Purchage
DECLARE #MaxRownum INT;
SET #MaxRownum = (select COUNT(*)from #PurchageTemp);
DECLARE #Iter INT;
SET #Iter = 1;
DECLARE #QtySum int=0;
DECLARE #QtySumTemp int=0;
DECLARE #CurrentItem int;
WHILE (#Iter <= #MaxRownum and #QtySum <= #AvailableQty)
BEGIN
set #QtySumTemp=#QtySum;
set #QtySumTemp = #QtySumTemp + (SELECT Qty FROM #PurchageTemp WHERE RowNum = #Iter and ItemCode=#ItemCode);
IF #QtySumTemp <= #AvailableQty
BEGIN
set #QtySum=#QtySumTemp;
set #CurrentItem= (SELECT PurchageNo FROM #PurchageTemp WHERE RowNum = #Iter and ItemCode=#ItemCode);
insert into [Result] values (#CurrentItem);
END
SET #Iter = #Iter + 1
END
DROP TABLE #PurchageTemp