Invoice creation in sql - create a stored procedure - sql

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

Related

Display Customer and all of their order dates on single row

I have a customers table and an orders table. I want to display the customer and all of his/her order dates on one row, rather than multiple rows. Here is what I have and what I'm looking for:
Basic code to get results:
select customerid, name, orderdate
from customer_table c inner join
order_table o
on c.customerid = o.customerid
this will work at the most you cant show it on different columns having nulls:
select customer_id,name,LISTAGG(orderdate, ', ') WITHIN GROUP (ORDER BY orderdate)
from(select customerid, name, orderdate
from customer_table c inner join
order_table o
on c.customerid = o.customerid );
You can use the following because you're bound by the 12 order limit. If you expand to an unknown number of orders with no upper limit, then you would need to use dynamic SQL and even then it would be tricky, because you would also need to dynamically create unique column names.
This query batches by customer and sets the order values. They will be in dated order and set as NULL if there's no more orders. It kinda assumes that at least one customer has 12 orders. You'll get a column of all NULLS if that's not the case
IF OBJECT_ID('tempdb..#Results') IS NOT NULL DROP TABLE #Results
IF OBJECT_ID('tempdb..#sortedRows') IS NOT NULL DROP TABLE #SortedRows
DECLARE #CustomerList TABLE(CustomerID INT, RowNo INT);
INSERT INTO #CustomerList SELECT DISTINCT CustomerID, ROW_NUMBER() OVER(ORDER BY CustomerID) RowNo FROM Customer_Table (NOLOCK)
DECLARE #Count INT = (SELECT COUNT(DISTINCT CustomerID) RowNumber FROM #CustomerList)
DECLARE #Counter INT = 0
DECLARE #CustToProcess INT
CREATE TABLE #Results(CustomerID INT, [Name] VARCHAR(50), OrderDate1 DATETIME,
OrderDate2 DATETIME, OrderDate3 DATETIME, OrderDate4 DATETIME, OrderDate5 DATETIME,
OrderDate6 DATETIME, OrderDate7 DATETIME, OrderDate8 DATETIME, OrderDate9 DATETIME,
OrderDate10 DATETIME, OrderDate11 DATETIME, OrderDate12 DATETIME)
INSERT INTO #Results(CustomerID, Name) SELECT DISTINCT CustomerID, Name FROM Customer_Table
SELECT ROW_NUMBER() OVER(PARTITION BY c.CustomerID ORDER BY OrderDate) RowNo,
c.CustomerID, c.Name, t.OrderDate INTO #SortedRows
FROM Customer_Table c (NOLOCK) JOIN Order_Table t ON c.CustomerID = t.CustomerID
WHILE #Counter < #Count
BEGIN
SET #Counter += 1
SET #CustToProcess = (SELECT CustomerID FROM #CustomerList WHERE RowNo = #Counter)
PRINT #CustToProcess
SELECT * INTO #RowsForProcessing FROM #SortedRows WHERE CustomerID = #CustToProcess
UPDATE #Results SET OrderDate1 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 1) WHERE CustomerID = #CustToProcess
UPDATE #Results SET OrderDate2 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 2) WHERE CustomerID = #CustToProcess
UPDATE #Results SET OrderDate3 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 3) WHERE CustomerID = #CustToProcess
UPDATE #Results SET OrderDate4 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 4) WHERE CustomerID = #CustToProcess
UPDATE #Results SET OrderDate5 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 5) WHERE CustomerID = #CustToProcess
UPDATE #Results SET OrderDate6 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 6) WHERE CustomerID = #CustToProcess
UPDATE #Results SET OrderDate7 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 7) WHERE CustomerID = #CustToProcess
UPDATE #Results SET OrderDate8 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 8) WHERE CustomerID = #CustToProcess
UPDATE #Results SET OrderDate9 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 9) WHERE CustomerID = #CustToProcess
UPDATE #Results SET OrderDate10 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 10) WHERE CustomerID = #CustToProcess
UPDATE #Results SET OrderDate11 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 11) WHERE CustomerID = #CustToProcess
UPDATE #Results SET OrderDate12 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 12) WHERE CustomerID = #CustToProcess
DROP Table #RowsForProcessing
END
SELECT * FROM #Results
Try this if you use MS SQL Server:
-- 1st, get the number of columns
declare #columnnumber int = 1
select #columnnumber = max(a.count)
from (
select c.cid,c.name, count(o.orderdate) as count
from
customer_table c
join order_table o
on c.cid = o.cid
group by c.cid,c.name)a
print #columnnumber
-- Compose the column names for Pivot
declare #columnname varchar(max) = ''
declare #int int = 1
while #int <= #columnnumber
begin
set #columnname = #columnname + '[date' + cast(#int as varchar(10))+ '],'
set #int = #int + 1
end
set #columnname = '('+left(#columnname,len(#columnname)-1)+')'
print #columnname
--Pivot !!! + Dynamic SQL
declare #str varchar(max)
set #str =
'SELECT *
FROM
(SELECT c.cid,c.name, o.orderdate,concat(''date'',row_number() over (partition by c.cid,c.name order by o.orderdate)) rnk
FROM customer_table c
join order_table o
on c.cid = o.cid) AS s
PIVOT
(
min(s.orderdate)
FOR s.rnk IN '+ #columnname+
' ) AS PivotTable'
print #str
execute (#str)
Please change the column name. I used cid as your customerid.
Output:
cid name date1 date2 date3
12 John 2017-03-04 2017-05-26 2017-12-01
4 Nancy 2017-02-01 NULL NULL
Like others said, it's in principle impossible (in pure SQL) to generate this not knowing how many orders you have for a single customer.
I like #nikhil-sugandh's answer, it works great if you are OK with having all orders comma-separated in a single column.
If you insist on having multiple columns, you can build on that answer, by replacing LISTAGG with ARRAY_AGG and postprocessing it. It will be MUCH more efficient than e.g. the proposed solution with multiple joins. You can also use ARRAY_SLICE to handle cases when there are more orders than you are prepared for.
Example (note, I added an extra order to demonstrate handling more-than-expected orders
create or replace table customer_table(customerId int, name varchar)
as select * from values
(12,'John'),(4,'Nancy');
create or replace table order_table(orderId int, customerId int, orderDate date)
as select * from values
(1,12,'3/4/2017'),(2,12,'5/26/2017'),(3,12,'12/1/2017'),(4,4,'2/1/2017'),(5,12,'1/1/2019');
with subq as (
select c.customerid, name,
array_agg(orderdate) within group (order by orderdate) as orders
from customer_table c
inner join order_table o on c.customerid = o.customerid
group by c.customerid, c.name
)
select customerid, name,
orders[0]::date AS order1, orders[1]::date AS order2,
array_to_string(array_slice(orders, 2, 999), ' , ') AS overflow
from subq;
------------+-------+------------+------------+-------------------------+
CUSTOMERID | NAME | ORDER1 | ORDER2 | OVERFLOW |
------------+-------+------------+------------+-------------------------+
4 | Nancy | 2017-02-01 | [NULL] | |
12 | John | 2017-03-04 | 2017-05-26 | 2017-12-01 , 2019-01-01 |
------------+-------+------------+------------+-------------------------+
first create a view like this:
create view order_view as
select
count(*) over (partition by customerId order by orderDate) as ord,
CustomerId,
orderdate
from order_table
then you can use this query:
select c.customerid,
o1.orderdate,
o2.orderdate
o3.orderdate
.
.
.
o12.orderdate
from customer_table c
left join order_view o1
on c.customerid = o1.customerid and ord = 1
left join order_view o2
on c.customerid = o2.customerid and ord = 2
left join order_view o3
on c.customerid = o3.customerid and ord = 3
.
.
.
left join order_view o12
on c.customerid = o12.customerid and ord = 12

How can I avoid the infinite loop?

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

Replace Cursor Next number assignment Operation with Set based equivalent

Good day all,
I have the following cursor query and would like to replace it with a set based query to address performance issues.
DECLARE #EmpIDM CHAR(21);
DECLARE #EmpName CHAR(21);
DECLARE #EMPCatID INT;
DECLARE Assign_Emp SCROLL CURSOR
FOR
SELECT DISTINCT EMP
, EMPNAME
FROM HR_EMPLOYEES
SET NOCOUNT ON
OPEN Assign_Emp;
FETCH NEXT
FROM Assign_Emp
INTO #EmpIDM
, #EmpName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #EMPCatID = (
SELECT TOP 1 CategoryID
FROM Categories
)
UPDATE Categories
SET CategoryID = (CategoryID + 1) /*Increment Category ID for next Insert*/
INSERT INTO Table1 (
EmpNumber
, EmployeeName
, EmployeeCategoryID
)
VALUES (
#EmpIDM
, #EmpName
, #EMPCatID
)
FETCH NEXT
FROM Assign_Emp
INTO #EmpIDM
, #EmpName
END
CLOSE Assign_Emp;
CLOSE Assign_Emp;
SET NOCOUNT OFF
My challenge is adapting the following code segment into a set based operation
SET #EMPCatID = (
SELECT TOP 1 CategoryID
FROM Categories
)
UPDATE Categories
SET CategoryID = (CategoryID + 1) /*Increment Category ID for next Insert*/
I humbly appreciate any insight on how I can achieve this.
Many Thanks,
Re-write using temp. table with identity column:
declare #offset int
select #offset = isnull(max(CategoryID),0) from Categories
create table #data (
EmpNumber CHAR(21),
EmployeeName CHAR(21),
EmployeeCategoryID int identity
)
INSERT INTO #data (
EmpNumber
, EmployeeName)
SELECT DISTINCT EmpIDM
, EmpName
FROM HR_EMPLOYEES
insert into Table1 (
EmpNumber
, EmployeeName
, EmployeeCategoryID
) select
EmpNumber
, EmployeeName
, EmployeeCategoryID + #offset
from #data
update Categories
set CategoryID = (select max(EmployeeCategoryID) from #data) + #offset

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

Loop through a recordset and use the result to do another SQL select and return the results

I am completely new to stored procedure. This time, I need to create a stored procedure in MS SQL.
Let's say I have the following table.
Table name: ListOfProducts
--------------------------
SomeID, ProductID
34, 4
35, 8
35, 11
How do I pass in a SomeID. Use this SomeID to select a recordset from table, ListOfProducts. Then loop through this record set.
Let's say I pass in SomeID = 35.
So, the record set will return 2 records with SomeID 35. In the loop, I will get ProductID 8 and 11, which will be used to do another select from another table.
The stored procedure should return the results from the 2nd select.
How can I do this in MS SQL stored procedure?
Sorry, for this newbie question. Thanks for any help.
If you want looping through the records. You can do like:
--Container to Insert Id which are to be iterated
Declare #temp1 Table
(
tempId int
)
--Container to Insert records in the inner select for final output
Declare #FinalTable Table
(
Id int,
ProductId int
)
Insert into #temp1
Select Distinct SomeId From YourTable
-- Keep track of #temp1 record processing
Declare #Id int
While((Select Count(*) From #temp1)>0)
Begin
Set #Id=(Select Top 1 tempId From #temp1)
Insert Into #FinalTable
Select SomeId,ProductId From ListOfProducts Where Id=#Id
Delete #temp1 Where tempId=#Id
End
Select * From #FinalTable
There is probably no point in writing an explicit loop if you don't need to preform some action on the products that can't be done on the whole set. SQL Server can handle stuff like this much better on its own. I don't know what your tables look like, but you should try something that looks more like this.
CREATE PROC dbo.yourProcName
#SomeID int
AS
BEGIN
SELECT
P.ProductId,
P.ProductName
FROM
Product P
JOIN
ListOfProducts LOP
ON LOP.ProductId = P.ProductId
WHERE
LOP.SomeId = #SomeID
END
I had to do something similar in order to extract hours from a select resultset start/end times and then create a new table iterating each hour.
DECLARE #tCalendar TABLE
(
RequestedFor VARCHAR(50),
MeetingType VARCHAR(50),
RoomName VARCHAR(MAX),
StartTime DATETIME,
EndTime DATETIME
)
INSERT INTO #tCalendar(RequestedFor,MeetingType,RoomName,StartTime,EndTime)
SELECT req as requestedfor
,meet as meetingtype
,room as rooms
,start as starttime
,end as endtime
--,u.datetime2 as endtime
FROM mytable
DECLARE #tCalendarHours TABLE
(
RequestedFor VARCHAR(50),
MeetingType VARCHAR(50),
RoomName VARCHAR(50),
Hour INT
)
DECLARE #StartHour INT,#EndHour INT, #StartTime DATETIME, #EndTime DATETIME
WHILE ((SELECT COUNT(*) FROM #tCalendar) > 0)
BEGIN
SET #StartTime = (SELECT TOP 1 StartTime FROM #tCalendar)
SET #EndTime = (SELECT TOP 1 EndTime FROM #tCalendar)
SET #StartHour = (SELECT TOP 1 DATEPART(HOUR,DATEADD(HOUR,0,StartTime)) FROM #tCalendar)
SET #EndHour = (SELECT TOP 1 DATEPART(HOUR,DATEADD(HOUR,0,EndTime)) FROM #tCalendar)
WHILE #StartHour <= #EndHour
BEGIN
INSERT INTO #tCalendarHours
SELECT RequestedFor,MeetingType,RoomName,#StartHour FROM #tCalendar WHERE StartTime = #StartTime AND EndTime = #EndTime
SET #StartHour = #StartHour + 1
END
DELETE #tCalendar WHERE StartTime = #StartTime AND EndTime = #EndTime
END
Do something like this:
Declare #ID int
SET #ID = 35
SELECT
p.SomeID
,p.ProductID
FROM ListOfProducts p
WHERE p.SomeID = #ID
-----------------------
--Or if you have to join to get it
Declare #ID int
SET #ID = 35
SELECT
c.SomeID
,p.ProductID
,p.ProductName
FROM ListOfProducts p
INNER JOIN categories c on p.ProductID = c.SomeID
WHERE p.SomeID = #ID
You can use option with WHILE loop and BREAK/CONTINUE keywords
CREATE PROC dbo.yourProcName
#SomeID int
AS
BEGIN
IF OBJECT_ID('tempdb.dbo.#resultTable') IS NOT NULL DROP TABLE dbo.#resultTable
CREATE TABLE dbo.#resultTable
(Col1 int, Col2 int)
DECLARE #ProductID int = 0
WHILE(1=1)
BEGIN
SELECT #ProductID = MIN(ProductID)
FROM ListOfProducts
WHERE SomeID = #SomeID AND ProductID > #ProductID
IF #ProductID IS NULL
BREAK
ELSE
INSERT dbo.#resultTable
SELECT Col1, Col2
FROM dbo.yourSearchTable
WHERE ProductID = #ProductID
CONTINUE
END
SELECT *
FROM dbo.#resultTable
END
Demo on SQLFiddle