I want to make two Queries into one by a variable - sql

I want to make two queries into one by a variable
ALTER PROCEDURE [dbo].[Balance]
#userId nvarchar(200)
AS
BEGIN
DECLARE #parchesQueryAdd decimal(18,2),#parchesQueryRemove decimal(18,2), #topupQuery decimal(18,2), #Balance decimal(18,2), #totalamount decimal(18,2)
/****** this two Querys starts ******/
SET #parchesQueryAdd = (SELECT SUM(Amount * CurrentBalanceCurrency) from UserBalance where BalanceForId = #userId and AmountType = 10)
SET #parchesQueryRemove = (SELECT SUM(Amount * CurrentBalanceCurrency) from UserBalance where BalanceForId = #userId and AmountType = 20)
/****** End ******/
SET #Balance = #parchesQueryAdd - #parchesQueryRemove
SET #topupQuery = (SELECT SUM(Amount * Quentity) from TopUpRecords where TopupById = #userId)
SET #totalamount= #Balance - #topupQuery
PRINT #totalamount
END

you could just use a single query for both the sum or the total
select sum( case when AmountType = 10
then Amount * CurrentBalanceCurrency else 0 end ) parchesQueryAdd
, sum( case when AmountType = 20
then Amount * CurrentBalanceCurrency else 0 end ) parchesQueryRemove
, sum( case when AmountType = 10
then Amount * CurrentBalanceCurrency else 0 end ) -
sum( case when AmountType = 20
then Amount * CurrentBalanceCurrency else 0 end ) totQuery
from UserBalance where BalanceForId = #userId

You can use condition aggregate function to set the #Balance instead of two query.
DECLARE
#parchesQueryAdd decimal(18,2),
#parchesQueryRemove decimal(18,2),
#topupQuery decimal(18,2),
#Balance decimal(18,2),
#totalamount decimal(18,2)
SELECT #Balance = SUM(CASE WHEN AmountType = 10 THEN Amount * CurrentBalanceCurrency ELSE 0 END)
- SUM(CASE WHEN AmountType = 20 THEN Amount * CurrentBalanceCurrency ELSE 0 END)
FROM UserBalance
WHERE BalanceForId = #userId
GROUP BY BalanceForId
SET #topupQuery = (SELECT SUM(Amount * Quentity) from TopUpRecords where TopupById = #userId)
SET #totalamount= #Balance - #topupQuery
PRINT #totalamount

From my understanding,
create table #oneVariable (type int, amt int, bal int)
insert #oneVariable values(10,10,20),(10,10,20),(20,10,20),(30,10,20)
select type, sum(amt*bal) sumOfAmt_Bal from #oneVariable group by type
-- type 10 has amt*bal 400. And, type 20 has 200.
-- So according by you formula, Balance will be 200.
-- Whereas, type 30 has been eliminated.
declare #balance int
SET #Balance = (
select sum(
case type
when 10 then amt * bal
when 20 then -(amt * bal)
end
) sumOfAmt_Bal
from #oneVariable
)
print #balance

Related

Rewrite SQL without cursor for Data Warehouse

I have SQL code (main core) below that works fine on SQL Server. How to rewrite it for T-SQL Azure SQL DW, which doesn't like cursor? I didn't find appropriate example and can't do it myself.
CREATE PROCEDURE calc_balance
CREATE TABLE output_table (Amount FLOAT, ValueStart FLOAT, ValueStop FLOAT);
DECLARE cursor_1 CURSOR FOR
SELECT Criteria, Amount, ValueStart, ValueStop
FROM Input_table
OPEN cursor_1
WHILE #fetchstatus = 0
BEGIN
FETCH NEXT FROM cursor_1 INTO #Criteria, #Amount, #ValueStart, #ValueStop;
#ValueStart1 = #balance;
#ValueStop1 = #ValueStart1 + #Amount;
IF (#Criteria = 1) AND (#balance> -100)
BEGIN
#Amount = 0;
#ValueStop1 = #ValueStart;
END;
#balance = #balance + #Amount + #ValueStart;
INSERT INTO output_table
VALUES (#Amount, #ValueStart1, #ValueStop1);
END;
You may want to try this.
with cte as (
select 0 as ctr, 0 as balance, Criteria, Amount, ValueStart, ValueStop, (select count(1) from Input_table) as ct from Input_table
union all
select ctr +1, balance + case when Criteria = 1 and balance >-100 then 0 else Amount end + ValueStart
, Criteria, Amount, balance
, case when Criteria = 1 and balance >-100 then balance + Amount else ValueStart end
, ct
from cte
where ctr < ct
)
Insert into output_table VALUES
(select Amount,ValueStart, ValueStop from cte where ctr>0)

How to avoid using while/cursor to achieve the same query results? [duplicate]

This question already has answers here:
Calculate a Running Total in SQL Server
(15 answers)
Closed 7 years ago.
I want to avoid looping from the below query.Now it takes long to retrieve the results from a table with 100000 records.
#iMAX int,
#rowId int,
#currCode varchar(20),
#lastCode varchar(20),
#amount money,
#total money
SET #rowId=1
SET #iMAX=(Select count(RowId) from Table1)
WHILE(#rowId<=#iMAX)
-- WHILE iteration
BEGIN
SELECT #Code=Code,#amount=Amount FROM Table1 WHERE RowId=#rowId
if #lastCode is null or #lastCode <> #currCode
begin
set #total = 0
set #lastCode = #currCode
end
set #total = #total + #amount
update Table1 set Balance = OpBalance + #total where RowId = #rowId
SET #rowId=#rowId+1
END
;WITH cte AS
(
SELECT *
, total = SUM(Amount) OVER (ORDER BY RowId)
, prevCode = LAG(Code) OVER (ORDER BY RowId)
FROM dbo.Table1
)
UPDATE cte
SET Balance = OpBalance +
CASE WHEN prevCode IS NULL OR prevCode != Code
THEN 0
ELSE total
END

distribute value to all rows while updating table

I have table structure like tblCustData
ID UserID Fee FeePaid
1 12 150 0
2 12 100 0
3 12 50 0
And value to be update in FeePaid Column such that if i have value in #Amt variable in 200 Then it should update any two rows
Output should be like
ID UserID Fee FeePaid
1 12 150 150
2 12 100 50
3 12 50 0
FeePaid should not be grater than Fee Column But if i pass 350 in #Amt variable it should produce output like
ID UserID Fee FeePaid
1 12 150 200
2 12 100 100
3 12 50 50
Only if #Amt is exceeding the total value in Fee column
I can not think beyond this query
Update tblCustData
Set FeePaid=#Amt
Where UserID=12
First with CTE syntax we prepare a table with sums distribution and then using unique field Code update the main table using CASE to handle all possible ways (including first row with remainder).
Declare #Amt int;
SET #Amt=250;
with T as
(
SELECT ROW_NUMBER() OVER (ORDER BY Fee desc) as rn, *
FROM tblCustData WHERE UserId=12
)
,T2 as
(
SELECT *,
ISNULL((SELECT SUM(Fee-FeePaid) FROM T WHERE T1.RN<RN),0) as PrevSum
FROM T as T1
)
UPDATE
A
SET A.FeePaid = A.FeePaid+ CASE WHEN (B.PrevSum+B.Fee-B.FeePaid<=#Amt)
AND (B.RN<>1)
THEN B.Fee-B.FeePaid
WHEN (B.PrevSum+B.Fee-B.FeePaid<=#Amt) AND (B.RN=1)
THEN #Amt-B.PrevSum
WHEN B.PrevSum>=#Amt
THEN 0
WHEN B.PrevSum+B.Fee-B.FeePaid>#Amt
THEN #Amt-B.PrevSum
END
FROM
tblCustData A
JOIN T2 B ON A.Code = B.Code
GO
SQLFiddle demo
Try ..
declare #t table (id int identity, UserId int, Fee money, FeePaid money)
insert into #t (UserID, Fee, FeePaid)
values
(12, 150, 0)
,(12, 100, 0)
,(12, 50 , 0)
declare #amt money = 200; -- change to 400 to test over paid
declare #Fees money;
select #Fees = sum(Fee) from #t;
declare #derivedt table (deid int, id int, UserId int, Fee money, FeePaid money)
insert into #derivedt (deid, id, UserId, Fee, FeePaid)
select row_number() over (order by case when #amt <= #Fees then id else -id end asc), id, UserId, Fee, FeePaid
from #t
-- order by case when #amt <= #Fees then id else -id end asc
; with cte(deid, id, UserId, Fee, FeePaid, Remainder)
as
(
select 0 as deid, 0 as id, 0 as UserId, cast(0.00 as money) as Fee, cast(0.00 as money) as FeePaid , #Amt as Remainder
from #derivedt
where id = 1
union all
select t.deid, t.id, t.UserId, t.Fee, case when cte.Remainder > t.Fee then t.Fee else cte.Remainder end as FeePaid
, case when cte.Remainder > t.Fee then cte.Remainder - t.Fee else 0 end as Remainder
from #derivedt t inner join cte cte on t.deid = (cte.deid + 1)
)
update origt
set FeePaid = det.FeePaid
from #t origt
inner join
(
select cte1.deid, cte1.id, cte1.UserId, cte1.Fee, cte1.FeePaid + ISNULL(cte2.Remainder, 0) as FeePaid
from cte cte1
left outer join (select top 1 deid, Remainder from cte order by deid desc) cte2
on cte1.deid = cte2.deid
where cte1.deid > 0
) det
on origt.id = det.id
select *
from #t
Modified to continuous update of value..
-- Create table once and insert into table once
create table #t (id int identity, UserId int, Fee money, FeePaid money)
insert into #t (UserID, Fee, FeePaid)
values
(12, 150, 0)
,(12, 100, 0)
,(12, 50 , 0)
-- ===============================
-- Run multiple times to populate #t table
declare #amt money = 100; -- change to 400 to test over paid
declare #Fees money;
select #Fees = sum(Fee - FeePaid) from #t;
declare #derivedt table (deid int, id int, UserId int, Fee money, FeePaid money)
insert into #derivedt (deid, id, UserId, Fee, FeePaid)
select row_number() over (order by case when #amt <= #Fees then id else -id end asc), id, UserId, (Fee - FeePaid) as Fee, FeePaid
from #t
-- order by case when #amt <= #Fees then id else -id end asc
; with cte(deid, id, UserId, Fee, FeePaid, Remainder)
as
(
select 0 as deid, 0 as id, 0 as UserId, cast(0.00 as money) as Fee, cast(0.00 as money) as FeePaid , #Amt as Remainder
from #derivedt
where id = 1
union all
select t.deid, t.id, t.UserId, t.Fee, case when cte.Remainder >= t.Fee then t.Fee else cte.Remainder end as FeePaid
, case when cte.Remainder >= t.Fee then cte.Remainder - t.Fee else 0 end as Remainder
from #derivedt t inner join cte cte on t.deid = (cte.deid + 1)
)
update origt
set FeePaid = origt.FeePaid + det.FeePaid
from #t origt
inner join
(
select cte1.deid, cte1.id, cte1.UserId, cte1.Fee, cte1.FeePaid + ISNULL(cte2.Remainder, 0) as FeePaid, cte1.Remainder
from cte cte1
left outer join (select top 1 deid, Remainder from cte order by deid desc) cte2
on cte1.deid = cte2.deid
where cte1.deid > 0
) det
on origt.id = det.id
select *
from #t
-- Drop temp table after
-- drop table #t
Apart from your code, I added an identity column to your table. See the code.
DECLARE #TAB TABLE(ID INT IDENTITY(1,1),USERID INT, FEE INT, FEEPAID INT)
INSERT INTO #TAB VALUES (12,150,0),(12,100,0),(12,50,0)
DECLARE #AMOUNT INT = 230,
#AMOUNTNEW INT = 0,
#B INT = 1,
#S INT = 1,#E INT = (SELECT COUNT(*) FROM #TAB)
WHILE #S <= #E
BEGIN
UPDATE LU
SET LU.FEEPAID = CASE WHEN #AMOUNT >= FEE THEN FEE ELSE #AMOUNT END
FROM #TAB LU
WHERE LU.ID = #S
SET #AMOUNT = #AMOUNT - (SELECT FEE FROM #TAB WHERE ID = #S)
IF #AMOUNT <= 0
SET #S = #E
SET #S = #S + 1
END
SELECT * FROM #TAB
Result:
I hope the idea is clear, we can work from here.

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 Server stored procedure statements using table clause

I am new to SQL Server and just joined a company where somebody had created the following stored procedure which I cannot understand.
I would like all of you to help me to understand what is going on in this procedure and what can be the alternate way of doing it.
Procedure [dbo].[SP_GetUserIncompleteCheckList]
(
#GroupID as int,
#BranchID as numeric)
As
Begin
Declare #DateStart as datetime
if #GroupID = 1
begin
set #DateStart = '08/31/2010' --'09/01/2010' 'Getdate()-30
end
else
begin
set #DateStart = Getdate()-30
end
--Select ResponseDate,isNull(Submit,'Incomplete') as Status from CheckList_Response Where BranchID=#BranchID and isNull(Submit,'y') <> 'Complete' and Month(ResponseDate) = Month(GetDate()) and Year(ResponseDate) = Year(GetDate()) order by ResponseDate
declare
#T table (ResponseDate Datetime,UserResponse int,DailyCount int,WeeklyCount int,MonthlyCount int,QuaterlyCount int,HalfYearlyCount int)
insert into #T (ResponseDate,UserResponse,DailyCount,WeeklyCount,MonthlyCount,QuaterlyCount,HalfYearlyCount)
Select ResDate,
isNull((Select UserResponse from VUserResponseBranchGroupwise Where ResponseDate=A.ResDate AND CLGroup = #GroupID and BranchID = B.BranchID),0) as UserResponse,
(Select Count(CLID) From CheckList where Frequency = 'Daily' and CLGroup=#GroupID) as DailyCount,
Case Weekly
When 1 Then (Select Count(CLID) From CheckList where Frequency = 'Weekly' and CLGroup=#GroupID) Else 0
End as WeeklyCount,
Case Monthly
When 1 Then (Select Count(CLID) From CheckList where Frequency = 'Monthly' and CLGroup=#GroupID) Else 0
End as MonthlyCount,
Case Quaterly
When 1 Then (Select Count(CLID) From CheckList where Frequency = 'Quarterly' and CLGroup=#GroupID) Else 0
End as QuaterlyCount,
Case HalfYearly
When 1 Then (Select Count(CLID) From CheckList where Frequency = 'Half Yearly' and CLGroup=#GroupID) Else 0
End as HalfYearlyCount
--isNull(Submit,'Incomplete') as Status
--,RoleStatus1,RoleStatus2,RoleStatus3,RoleStatus4,RoleStatus5 */
from dbo.CheckList_DateType A
,dbo.CheckList_Response B
Where A.ResDate=B.ResponseDate
AND B.ResponseDate > #DateStart
AND isNull(B.Submit,'Incomplete') <> 'Complete'
AND B.BranchID = #BranchID
Select ResponseDate,
case UserResponse
when 0 Then 'Incomplete'
else 'Partial'
end as Status
from #T
Where UserResponse < (DailyCount+WeeklyCount+MonthlyCount+QuaterlyCount+HalfYearlyCount)
order by ResponseDate
As far as I understand its a temporary table or something...
It's a variable of type TABLE
Same as:
DECLARE #COUNTER INT // variable of type INT
DECLARE #STR VARCHAR(5) // variable of type STRING
DECLARE #TAB TABLE(COLUMN1 INT) // variable of type TABLE
You can assign values to variables using SET statements.
Example:
SET #COUNTER = 1;
But for tables, INSERT statement will do
INSERT INTO #TAB(COLUMN1) VALUES(123)
It is a table variable.
Have a look at
Table Variables In T-SQL
DECLARE #local_variable
(Transact-SQL)