How to improve the performance of this sql script - sql-server

The below script takes more than 10 hours to execute..
It contains three nested cursors and i think they are the main culprit.
I have searched a lot for replacing the cursors or improve the performance of the script.And found a lot of ways to remove the cursor. e.g. simple set join operations, Marge operation etc etc. But still I am unable to implement them in my script. Please take look on the script and give some opinion on
- with out affecting the output, is it possible to remove the inner cursors or not
- if possible then how ? please give some code if possible
DECLARE #Start_Date Date = '1990-01-01'
DECLARE #End_Date Date = '2012-12-12'
DECLARE #Company_ID int = 180
declare #BP_ID [int]
DECLARE All_Client_Bp_Id CURSOR STATIC FOR
SELECT TOP 50 Bp_id FROM Client --Take All Client's BPID
OPEN All_Client_Bp_Id
FETCH NEXT FROM All_Client_Bp_Id
INTO #BP_ID
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #acrdint MONEY
DECLARE #acrdSrv MONEY
DECLARE #MaxMargLimit MONEY
DECLARE #ForceLimit MONEY
DECLARE #WarningLimit MONEY
DECLARE #MarginLimit DECIMAL(5,2)
select #MaxMargLimit=Highest_Margin_Limit*100000,#ForceLimit=Force_Sell_Limit,#WarningLimit=Margin_Warning_Limit, #MarginLimit = Margin_Limit
from Client_Margin_Constraints c
inner join (select Bp_id, max(Update_Date) as updt
from Client_Margin_Constraints
where Update_Date <= #End_Date and Bp_id = #BP_ID and Company_ID = #Company_ID
group by Bp_id) b
on c.Bp_id = b.Bp_id and c.Update_Date = b.updt
DELETE FROM Temp_Portfolio
INSERT INTO Temp_Portfolio (Client_BP_ID, tran_date, Instrument_ID, Is_Buy, Quantity, Amount, Commission, Rate, Category,Tax_Amount)
SELECT t.Client_BP_ID, t.tran_date, t.Instrument_ID, t.Is_Buy, t.Quantity, t.Amount, t.Commission,
t.Rate, t.Category, t.Tax_Amount
FROM All_Share_Txn t
WHERE t.Client_BP_ID = #BP_ID
DECLARE #Instrument_Id int,
#bqty int,
#bamt MONEY,
#brate MONEY,
#srate MONEY,
#rprofit MONEY,
#mslbqty int,
#BCost MONEY,
#SCost MONEY,
#BCqty int,
#SCqty int,
#B_Or_S CHAR(1),
#CDBL_Type CHAR(1),
#Qty INT,
#MktPrice MONEY,
#Amount MONEY,
#Commission MONEY,
#TDATE SMALLDATETIME,
#DES CHAR(30),
#TotalProfit MONEY,
#Category CHAR(1)
SET #TotalProfit = 0
DECLARE CUR_SHARE CURSOR STATIC FOR
SELECT DISTINCT Instrument_Id
FROM Temp_Portfolio
OPEN CUR_SHARE
FETCH NEXT FROM CUR_SHARE
INTO #Instrument_Id
WHILE ##FETCH_STATUS = 0
BEGIN
SET #bqty = 0
SET #bamt = 0
SET #brate = 0
SET #srate = 0
SET #rprofit = 0
SET #mslbqty = 0
SET #BCost = 0
SET #SCost = 0
SET #BCqty = 0
SET #SCqty = 0
DECLARE CUR_COST CURSOR STATIC FOR
SELECT Is_buy,Quantity,rate,Amount,Commission,Tran_date
FROM Temp_Portfolio
WHERE Client_Bp_id=#BP_ID and Instrument_Id=#Instrument_Id
ORDER BY Tran_date,Is_buy desc,Category
OPEN CUR_COST
FETCH NEXT FROM CUR_COST
INTO #B_Or_S,#Qty,#MktPrice,#Amount,#Commission,#TDATE
WHILE ##FETCH_STATUS = 0
BEGIN
IF #B_Or_S='1'
BEGIN
SET #bqty = #bqty + #Qty
SET #bamt = #bamt + #Amount + #Commission
IF #bqty > 0
SET #brate = #bamt/#bqty
END
ELSE IF #B_Or_S='0'
BEGIN
SET #srate = #MktPrice
IF #TDATE > #Start_Date and #TDATE <= #End_Date
BEGIN
SET #rprofit = #rprofit + ((#srate - #brate) * #Qty) - #Commission
SET #BCqty = #BCqty + #Qty
SET #SCqty = #SCqty + #Qty
SET #BCost = #BCost + (#Brate * #Qty)
SET #SCost = #SCost + (#Srate * #Qty) - #Commission
END
SET #bamt = #bamt - (#brate * #Qty)
SET #bqty = #bqty - #Qty
END
FETCH NEXT FROM CUR_COST
INTO #B_Or_S,#Qty,#MktPrice,#Amount,#Commission,#TDATE
END
SET #TotalProfit=#TotalProfit+#rprofit
CLOSE CUR_COST
DEALLOCATE CUR_COST
FETCH NEXT FROM CUR_SHARE
INTO #Instrument_Id
END
CLOSE CUR_SHARE
DEALLOCATE CUR_SHARE
--Select #TotalProfit as Realised_Gain
-- Equity, Purchase Power Calculation
DECLARE #Equity_All MONEY
DECLARE #Equity_Margin MONEY
DECLARE #Current_Balance MONEY
DECLARE #temp_Purchase_Power_1 MONEY
DECLARE #temp_Purchase_Power_2 MONEY
DECLARE #Purchase_Power MONEY
--Balance
select #Current_Balance = sum((case when a.Is_Share_TXN='1' and a.Is_Debit='1' then -a.amount when a.Is_Share_TXN='1' and a.Is_Debit='0' then a.amount when a.Is_Share_TXN='0' and a.Is_Debit='0' then a.amount when a.Is_Share_TXN='0' and a.Is_Debit='1' then -a.amount end) -a.Commission)
from Client_Transaction a where a.Client_Bp_id = #BP_ID and a.Transaction_Date < #End_Date and a.Company_Id=#Company_Id
group by a.Client_Bp_id--dbo.get_Current_Balance(#BP_ID, #End_Date, #Company_ID)
--Equity Margin
SELECT #Equity_Margin = SUM(CASE WHEN t_Margin.Is_Marginable_Securities = 'True' Then t_Margin.Total_Quantity * t_Margin.Market_price END),
#Equity_All = SUM(t_Margin.Total_Quantity * t_Margin.Market_price)
FROM
(
select t2.Is_Marginable_Securities, --t3.Bp_Code as Client_Code,t3.Bp_Name as Client_Name,t4.Bo_Id_DSE,t1.Instrument_ID,
sum(case when Is_buy='True' then Quantity when Is_buy='False' then -quantity end) as Total_Quantity,
--sum(case when ISNULL(t1.Mature_Date_Share,t1.tran_date) <= #Transaction_Date then (case Is_buy when '1' then quantity when '0' then -quantity end) else 0 end) as Free_Quantity,
INDX.Closing_Price AS Market_price
from dbo.View_All_Share_Transactions_with_PledgeUnpledge t1 left outer join Instrument t2 on t1.Instrument_Id=t2.Instrument_ID
left outer join (
select Closing_Price from Index_Price ip
where ip.Txn_Date=(select MAX(Txn_Date) from Index_Price ip2 where ip2.Instrument_Id=ip.Instrument_Id)
) INDX
ON t1.Instrument_ID = INDX.Closing_Price
where Client_Bp_id = #BP_ID and t1.Instrument_ID is not null and tran_date <=#End_Date and t1.Is_Pledge_Unpledge = 'False'
group by t1.Instrument_ID,t2.Is_Marginable_Securities, INDX.Closing_Price
having sum(case when Is_buy='True' then Quantity when Is_buy='False' then -quantity end)<> 0
) t_Margin --dbo.get_Equity_Margin(#BP_ID, #End_Date, #Company_ID)
SET #Equity_Margin = #Equity_Margin + #Current_Balance
SET #Equity_All = #Equity_All + #Current_Balance
SET #temp_Purchase_Power_1 = (#Equity_Margin * (#MarginLimit / 100)) + #Current_Balance
SET #temp_Purchase_Power_2 = ISNULL(#MaxMargLimit,0) + ISNULL(#Current_Balance,0)
IF (#temp_Purchase_Power_1 < #temp_Purchase_Power_2)
BEGIN
SET #Purchase_Power = #temp_Purchase_Power_1
END
ELSE
BEGIN
SET #Purchase_Power = #temp_Purchase_Power_2
END
INSERT Client_Account_Balance (
BP_ID,
Equity_All,
Equity_Margin,
Purchase_Power,
Margin_Ratio,
Total_Deposit,
Withdraw,
Charges,
Current_Balance,
Aaccured_Charges,
Max_Margin_LImit,
Margin_Limit,
Realised_Gain,
Company_ID,
Created_By,
Updated_By
)
SELECT #BP_ID, #Equity_All,#Equity_Margin ,#Purchase_Power ,
CASE WHEN (#Current_Balance - ISNULL(#acrdSrv,0)) != 0 THEN (#Equity_Margin - #Current_Balance) * (-100)/(#Current_Balance - ISNULL(#acrdSrv,0)) ELSE 0 END,
TDEP,TWDRAW,TCHARGES,BALANCE,ACC_CHARGE,#MaxMargLimit ,#MarginLimit,
#TotalProfit,
180,1,1
FROM
(SELECT SUM(DEPOSITE) AS TDEP,SUM(WDRAW) AS TWDRAW,SUM(CHARGES) AS TCHARGES,SUM(DEBCRED-Commission) AS BALANCE,SUM(ISNULL(#acrdint,0)+ISNULL(#acrdSrv,0)) AS ACC_CHARGE
FROM
(SELECT (CASE
WHEN tran_date >= #Start_Date THEN
CASE Is_Share_TXN
WHEN '0' THEN
CASE Is_Debit
WHEN '1' THEN 0
ELSE AMOUNT END
ELSE 0 END
ELSE 0 END) AS Deposite,
(CASE
WHEN tran_date >= #Start_Date THEN
CASE Is_Share_TXN
WHEN '0' THEN
CASE Is_Debit
WHEN '1' THEN
CASE T_TYPE
WHEN '1' THEN AMOUNT
WHEN '3' THEN AMOUNT
WHEN 'T' THEN AMOUNT
ELSE 0 END
ELSE 0 END
ELSE 0 END
ELSE 0 END) AS WDRAW,
(CASE
WHEN tran_date >= #Start_Date THEN
CASE Is_Share_TXN
WHEN '0' THEN
CASE Is_Debit
WHEN '1' THEN
CASE T_TYPE
WHEN '1' THEN 0
WHEN '3' THEN 0
WHEN 'T' THEN 0
ELSE AMOUNT END
ELSE 0 END
ELSE 0 END
ELSE 0 END) AS CHARGES,
(CASE Is_Share_TXN
WHEN '1' THEN
CASE Is_Debit
WHEN '1' THEN -AMOUNT
ELSE AMOUNT END
When '0' THEN
CASE Is_Debit
WHEN '1' THEN -AMOUNT
ELSE AMOUNT END
END) AS DEBCRED,Commission
FROM View_All_Transaction
WHERE tran_date <= #End_Date
AND Client_Bp_id = #BP_ID ) AS A) AS B
FETCH NEXT FROM All_Client_Bp_Id
INTO #BP_ID
END
CLOSE All_Client_Bp_Id
DEALLOCATE All_Client_Bp_Id

Hey i had a look with your script and found ..you can improve your scripts performance by excluding DISTINCT clause and create non-cluster index against column which following ORDER BY CLAUSE as well.
These 2 point will definitly help you to improve the performance.
Please let me know if you have any query.

Related

Salary PAYE brackets using a stored procedure

I am trying to insert into an Employee table and I want the if statements to insert into the Salary, NetSalary, and Deduction columns if the Employee will earn a certain amount based on the hourly rate.
If statements are meant to insert data into the relevant column when the amount falls into a particular bracket
ALTER PROCEDURE [dbo].[spSaveEmployee]
(#EmployeePersonName Varchar(20),
#JobTitle int,
#Branch int,
#Department int,
#Manager int,
#HourlyRate MONEY,
#PaymentDate datetime = '2021-10-05 00:00:00.000',
#ErrorMessage Varchar(50) OUTPUT)
AS
BEGIN
DECLARE #DefaultDate DATETIME = GETDATE()
--DECLARE #PAYE INT
--SET #PAYE = 26 / 100
DECLARE #Salary INT
--SET #Salary = #HourlyRate * 180
--DECLARE #PYE INT
--SET #PYE = 1.15
DECLARE #NetSalary MONEY
DECLARE #Deduction DECIMAL (18,2)
DECLARE #NetSalary1 MONEY
DECLARE #NetSalary2 MONEY
DECLARE #NetSalary3 MONEY
--SET #NetSalary = #Salary - #PYE
DECLARE #Deduction1 DECIMAL (18,2)
DECLARE #Deduction2 DECIMAL (18,2)
DECLARE #Deduction3 DECIMAL (18,2)
SET #Deduction1 = (#Salary * 18) / 100
SET #NetSalary1 = #Salary - #Deduction1
SET #Deduction2 = (#Salary * 26) / 100
SET #NetSalary2 = #Salary - #Deduction2
SET #Deduction3 = (#Salary * 31) / 100
SET #NetSalary3 = #Salary - #Deduction3
IF NOT EXISTS (SELECT * FROM EmployeeBook
WHERE EmployeePersonName = #EmployeePersonName)
BEGIN
INSERT INTO EmployeeBook (EmployeePersonName, JobTitle, Branch, Department,
Manager, HourlyRate, PAYE_Tax, Salary,
NetSalary, PaymentDate, Deductions)
VALUES (#EmployeePersonName, #JobTitle, #Branch, #Department,
#Manager, #HourlyRate, 1.15, #Salary,
#NetSalary, #DefaultDate, #Deduction)
SET #ErrorMessage = ''
IF #Salary <= 321000
BEGIN
SELECT #Deduction = #Deduction
SELECT #NetSalary = #NetSalary
END
ELSE IF #Salary >= 321001 AND #Salary <= 445100
BEGIN
SELECT #Deduction = #Deduction2
SELECT #NetSalary = #NetSalary2
END
ELSE IF #Salary >= 445100 AND #Salary <= 584200
BEGIN
SELECT #Deduction = #Deduction3
SELECT #NetSalary = #NetSalary3
END
END
ELSE
BEGIN
SET #EmployeePersonName = ''
SET #JobTitle = 0
SET #Branch = 0
SET #Department = 0
SET #Manager = 0
SET #HourlyRate = 0.00
SET #Salary = 0.00
SET #NetSalary = 0.00
SET #PaymentDate = #DefaultDate
SET #Deduction = 0
SET #ErrorMessage = 'Sorry could not save ' + #EmployeePersonName + ' Employee name already exists'
END
END
GO
The following does what I believe you are trying to accomplish. Issues and comments:
You set your variables after you use them - so they have no effect.
T-SQL is optimised for set-based operations therefore you should attempt to carry out your operations in a set-based manner if possible rather than procedural
Best practice is to use consistent casing for T-SQL
As Larnu points out, error handling is best handled using throw.
Best practice is to always schema prefix your database objects
SPs should always have a return statement
SPs should normally start SET NOCOUNT, XACT_ABORT ON;
Since you can validate the existence of the person straight away, I would and save any further processing time.
What if the salary falls outside the defined brackets? You might not expect it, but you should handle it hence the ELSE in the CASE expression.
I would avoid prefixing your SP's with sp as its too close to the system prefix sp_. In fact why prefix them at all?
When creating brackets like this, one end condition needs to be less than or equal and the other condition greater than (or vice-versa) to avoid a value falling into 2 brackets. Even though the case will short-circuit to the first matching expression, its best from an understanding perspective to have correct and accurate logic.
ALTER PROCEDURE [dbo].[SaveEmployee]
(
#EmployeePersonName VARCHAR(20),
#JobTitle INT,
#Branch INT,
#Department INT,
#Manager INT,
#HourlyRate MONEY,
#PaymentDate DATETIME = '2021-10-05 00:00:00.000',
#ErrorMessage VARCHAR(50) OUTPUT
)
AS
BEGIN
SET NOCOUNT, XACT_ABORT ON;
IF EXISTS (SELECT 1 FROM dbo.EmployeeBook WHERE EmployeePersonName = #EmployeePersonName) BEGIN
SET #ErrorMessage = 'Sorry could not save ' + #EmployeePersonName + ' Employee name already exists' END
RETURN;
-- Comment out 2 lines above to uncomment the 2 code line below
-- As suggested by Larnu, this would be more a typical error handling approach
--DECLARE #Error NVARCHAR(2048) = 'Sorry could not save ' + #EmployeePersonName + ' Employee name already exists';
--THROW 51000, #Error, 1;
END;
DECLARE #DefaultDate DATETIME = GETDATE(), #Salary INT = #HourlyRate * 180;
INSERT INTO dbo.EmployeeBook (EmployeePersonName, JobTitle, Branch, Department
, Manager, HourlyRate, PAYE_Tax, Salary
, NetSalary, PaymentDate, Deductions)
SELECT EmployeePersonName, JobTitle, Branch, Department
, Manager, HourlyRate, 1.15, Salary
, CASE SalaryBracket WHEN 1 THEN Salary - Deduction1
WHEN 2 THEN Salary - Deduction2
WHEN 3 THEN Salary - Deduction3
-- ELSE ??
END
, #DefaultDate
, CASE SalaryBracket WHEN 1 THEN Deduction1
WHEN 2 THEN Deduction2
WHEN 3 THEN Deduction3
-- ELSE ??
END
FROM (
VALUES (#EmployeePersonName, #JobTitle, #Branch, #Department, #Manager, #HourlyRate, 1.15, #Salary
, (#Salary * 18) / 100, (#Salary * 26) / 100, (#Salary * 31) / 100
-- Compute Salary Bracket in one place for reuse
, CASE WHEN #Salary <= 321000 THEN 1
WHEN #Salary > 321001 AND #Salary <= 445100 THEN 2
WHEN #Salary > 445100 AND #Salary <= 584200 THEN 3 END
)
) AS X (EmployeePersonName, JobTitle, Branch, Department, Manager, HourlyRate, PAYE_Tax, Salary, Deduction1, Deduction2, Deduction3, SalaryBracket);
RETURN 0;
END
GO

Remove duplicate detail transaction (header detail store procedure)

I have problem when creating header detail transaction in store procedure. Assume 1 header contain 3 details transaction. If I want to get invoice I must join some table for getting the formula.
USE [M_TENANT]
GO
/****** Object: StoredProcedure [dbo].[generate_billingv2] Script Date: 07/02/2020 09:32:42 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[generate_billingv2]
AS
--exec generate_billingv2
SET NOCOUNT ON;
BEGIN TRANSACTION
DECLARE #genbill_nochar INT
,#OPT_UTILITY_NOCHAR NVARCHAR(50)
,#NAME_CHAR VARCHAR(80)
,#opt_utility_detail NVARCHAR(50)
,#MD_BILLING_ID_INT INT
,#BILLING_ID_UTILITY INT
,#OPT_UTILITY_AMT NUMERIC
,#BILLING_ID_FORMULA INT
,#FORMULA_PRICE NUMERIC
,#FORMULA_VA NUMERIC
,#FORMULA_NAME VARCHAR(50)
,#MD_FORMULA_PERCENTAGE DECIMAL(18, 3)
,#bill_TYPE INT
,#BASE_INVOICE_AMT NUMERIC
,#OPT_UTILITY_USED NUMERIC
,#ABODEMEN NUMERIC
,#UNIT_LB DECIMAL(18, 3)
,#TAX_UTILITY NUMERIC(18, 3)
,#TOTAL_UTILITY NUMERIC(18, 3)
,#OPT_GENBILL_NOCHAR VARCHAR(50)
,#NOUNIT_CHAR CHAR(20)
,#OPT_GENBILL_STATUS_INT INT
,#OPT_GENBILL_TRX_DATE DATE
,#OPT_UTILITY_TRX_DATE DATE
,#OPT_TRANS_ID_INT INT
,#OPT_GENERATE_BY VARCHAR(50)
,#MD_FORMULA_PRICE NUMERIC
,#USAGE DECIMAL(18, 2)
,#OPT_FLOOR CHAR(10)
,#MD_FORMULA_NAME VARCHAR(50)
,#OPT_START NUMERIC
,#OPT_BILL_END NUMERIC
,#OPT_START_DATE DATE
,#OPT_END_DATE DATE
,#FORMULA_PERCENTAGE DECIMAL(18, 2)
SET #BASE_INVOICE_AMT = 0
SET #TAX_UTILITY = 0
SET #TOTAL_UTILITY = 0
SET #OPT_GENBILL_STATUS_INT = 0
SET #OPT_GENBILL_TRX_DATE = GETDATE()
SET #OPT_GENERATE_BY = 'AUTO'
DECLARE bill_header CURSOR
FOR
SELECT a.OPT_UTILITY_NOCHAR
,a.NOUNIT_CHAR
,b.UNIT_LB
,b.NAME_CHAR
,b.OPT_FLOOR
,a.OPT_TRANS_ID_INT
,a.OPT_UTILITY_TRX_DATE
,0 AS BASE_INVOICE
,0 AS TAX_UTILITY
,0 AS TOTAL_ULTILITY
FROM OPT_UTILITY a
INNER JOIN OPT_TRANS b ON a.OPT_TRANS_ID_INT = b.OPT_TRANS_ID_INT
WHERE a.OPT_GENBILL_STATUS_INT = 0
OPEN bill_header
FETCH NEXT
FROM bill_header
INTO #OPT_UTILITY_NOCHAR
,#NOUNIT_CHAR
,#UNIT_LB
,#NAME_CHAR
,#OPT_FLOOR
,#OPT_TRANS_ID_INT
,#OPT_UTILITY_TRX_DATE
,#BASE_INVOICE_AMT
,#TAX_UTILITY
,#TOTAL_UTILITY
WHILE ##FETCH_STATUS = 0
BEGIN
--HEADER TRANSACTION
--PRINT 'ini header'+'-'+ #OPT_UTILITY_NOCHAR
SET #OPT_GENBILL_NOCHAR = convert(VARCHAR, (
SELECT 'KALINV' + convert(VARCHAR(4), YEAR(GETDATE())) + REPLICATE('0', 5 - LEN(RTRIM(invoiced_count))) + RTRIM(invoiced_count)
FROM counter_table
));
INSERT INTO OPT_GENBILL (
OPT_GENBILL_NOCHAR
,OPT_UTILITY_NOCHAR
,NOUNIT_CHAR
,NAME_CHAR
,LB
,BASE_UTILITY
,TAX_UTILITY
,TOTAL_UTILITY
,OPT_GENBILL_STATUS_INT
,OPT_GENBILL_TRX_DATE
,OPT_UTILITY_TRX_DATE
,OPT_TRANS_ID_INT
,OPT_GENERATE_BY
)
VALUES (
#OPT_GENBILL_NOCHAR
,#OPT_UTILITY_NOCHAR
,#NOUNIT_CHAR
,#NAME_CHAR
,#UNIT_LB
,#BASE_INVOICE_AMT
,#TAX_UTILITY
,#TOTAL_UTILITY
,#OPT_GENBILL_STATUS_INT
,#OPT_GENBILL_TRX_DATE
,#OPT_UTILITY_TRX_DATE
,#OPT_TRANS_ID_INT
,#OPT_GENERATE_BY
)
DECLARE bill_detail CURSOR
FOR
SELECT C.MD_BILLING_ID_INT AS BILL_TYPE
,a.OPT_UTILITY_NOCHAR
,a.MD_BILLING_ID_INT
,b.MD_FORMULA_PRICE
,b.MD_FORMULA_NAME
,b.MD_FORMULA_PERCENTAGE
,b.MD_FORMULA_PJU_ABD AS ABODEMEN
,b.MD_FORMULA_VA AS FORMULA_VA
,a.OPT_BILL_END - a.OPT_UTILITY_AMT AS OPT_START
,a.OPT_BILL_END
,a.OPT_UTILITY_AMT AS USAGE
,a.OPT_START_DATE
,a.OPT_END_DATE
FROM OPT_UTILITY_DETAIL a
INNER JOIN MD_FORMULA b ON a.MD_BILLING_ID_INT = b.MD_FORMULA_ID_INT
INNER JOIN MD_BILLING_TYPE c ON b.MD_BILLING_ID_INT = c.MD_BILLING_ID_INT
WHERE a.OPT_UTILITY_NOCHAR = #OPT_UTILITY_NOCHAR
OPEN bill_detail
FETCH NEXT
FROM bill_detail
INTO #BILL_TYPE
,#OPT_UTILITY_NOCHAR
,#MD_BILLING_ID_INT
,#MD_FORMULA_PRICE
,#MD_FORMULA_NAME
,#MD_FORMULA_PERCENTAGE
,#ABODEMEN
,#FORMULA_VA
,#OPT_START
,#OPT_BILL_END
,#USAGE
,#OPT_START_DATE
,#OPT_END_DATE
--IF exists(select TOP 1* from OPT_GENBILL where OPT_UTILITY_NOCHAR=#OPT_UTILITY_NOCHAR)
--BEGIN
-- ROLLBACK TRANSACTION
-- DEALLOCATE bill_detail
-- RAISERROR('invoice sudah pernah generate ',16,-1,#OPT_UTILITY_NOCHAR)
-- RETURN
--END
IF ##FETCH_STATUS <> 0
PRINT ' <<None>>'
WHILE ##FETCH_STATUS = 0
BEGIN
--SET #BASE_INVOICE_AMT = CASE WHEN #BILL_TYPE=3 AND #USAGE<=40 THEN
-- ((#ABODEMEN*(#FORMULA_VA/1000)*#FORMULA_PRICE)+ ((#ABODEMEN*(#FORMULA_VA/1000)*#FORMULA_PRICE)*#FORMULA_PERCENTAGE))
-- when #bill_TYPE=3 and #USAGE>40 then
-- ((#USAGE*(#FORMULA_VA/1000)*#FORMULA_PRICE)+ ((#USAGE*(#FORMULA_VA/1000)*#FORMULA_PRICE)*#FORMULA_PERCENTAGE))
-- when #bill_TYPE=2 then ((#USAGE*#FORMULA_PRICE)+#ABODEMEN)
-- when #bill_TYPE=1 then #UNIT_LB*#FORMULA_PRICE
-- else 0 end
--print #OPT_UTILITY_NOCHAR
-- print #BILL_TYPE
-- print #USAGE
-- print #ABODEMEN
-- print #FORMULA_PRICE
-- print #FORMULA_VA
-- print #FORMULA_PERCENTAGE
INSERT INTO OPT_GENBILL_DETAIL (
OPT_UTILITY_NOCHAR
,NOUNIT_CHAR
,OPT_START_DATE
,OPT_END_DATE
,OPT_BILL_START
,OPT_BILL_END
,OPT_UTILITY_AMT
,OPT_GENBILL_NOCHAR
)
SELECT #OPT_UTILITY_NOCHAR
,#NOUNIT_CHAR
,OPT_START_DATE
,OPT_END_DATE
,OPT_BILL_END - OPT_UTILITY_AMT
,OPT_BILL_END
,CASE
WHEN #BILL_TYPE = 3
AND #USAGE <= 40
THEN ((#ABODEMEN * (#FORMULA_VA / 1000) * #FORMULA_PRICE) + ((#ABODEMEN * (#FORMULA_VA / 1000) * #FORMULA_PRICE) * #FORMULA_PERCENTAGE))
WHEN #bill_TYPE = 3
AND #USAGE > 40
THEN ((#USAGE * (#FORMULA_VA / 1000) * #FORMULA_PRICE) + ((#USAGE * (#FORMULA_VA / 1000) * #FORMULA_PRICE) * #FORMULA_PERCENTAGE))
WHEN #bill_TYPE = 2
THEN ((#USAGE * #FORMULA_PRICE) + #ABODEMEN)
ELSE 0
END
,#OPT_GENBILL_NOCHAR
FROM OPT_UTILITY_DETAIL
WHERE OPT_UTILITY_NOCHAR = #OPT_UTILITY_NOCHAR
FETCH NEXT
FROM bill_detail
INTO #BILL_TYPE
,#OPT_UTILITY_NOCHAR
,#MD_BILLING_ID_INT
,#MD_FORMULA_PRICE
,#MD_FORMULA_NAME
,#MD_FORMULA_PERCENTAGE
,#ABODEMEN
,#FORMULA_VA
,#OPT_START
,#OPT_BILL_END
,#USAGE
,#OPT_START_DATE
,#OPT_END_DATE
END
CLOSE bill_detail
DEALLOCATE bill_detail
UPDATE counter_table
SET invoiced_count = invoiced_count + 1
UPDATE OPT_UTILITY
SET OPT_GENBILL_STATUS_INT = 1
WHERE OPT_UTILITY_NOCHAR = #OPT_UTILITY_NOCHAR
-- Get the next vendor.
FETCH NEXT
FROM bill_header
INTO #OPT_UTILITY_NOCHAR
,#NOUNIT_CHAR
,#UNIT_LB
,#NAME_CHAR
,#OPT_FLOOR
,#OPT_TRANS_ID_INT
,#OPT_UTILITY_TRX_DATE
,#BASE_INVOICE_AMT
,#TAX_UTILITY
,#TOTAL_UTILITY
END
CLOSE bill_header;
DEALLOCATE bill_header;
COMMIT TRANSACTION
RETURN
But the result 3 detail generating 9 details rows.

SQL Server 2012 - Update column if condition is met (CASE) else do not update

In the below example, the columns 'SecondColumn' and 'ThirdColumn' will always update each time the block of code runs. 'FirstColumn' will update to the value stored in the variable #NumberOfRows (so long as it is >0).
The problem I have is that when #NumberOfRows is 0 or less, 'FirstColumn' will be set to NULL.
Is it possible to tweak this so that if #NumberOfRows is 0 or less, then the 'FirstColumn' does not update at all rather than setting the column to NULL?
DECLARE
#NumberOfRows INT = 0
,#NewValue DATETIME = GETDATE()
,#Other INT = 99
BEGIN
UPDATE x
SET x.FirstColumn = (CASE WHEN #NumberOfRows > 0 THEN #NewValue END)
,x.SecondColumn = SYSDATETIMEOFFSET()
,x.ThirdColumn = #Other
FROM TestTable x
WHERE x.ID = 100
END
The simplest thing to do is update the field with the current value.
DECLARE
#NumberOfRows INT = 0
,#NewValue DATETIME = GETDATE()
,#Other INT = 99
BEGIN
UPDATE x
SET x.FirstColumn = (CASE WHEN #NumberOfRows > 0 THEN #NewValue ELSE x.FirstColumn END)
,x.SecondColumn = SYSDATETIMEOFFSET()
,x.ThirdColumn = #Other
FROM TestTable x
WHERE x.ID = 100
END
Do not perform the update of the given column if #NumberOfRows <= 0:
DECLARE
#NumberOfRows INT = 0
,#NewValue DATETIME = GETDATE()
,#Other INT = 99
BEGIN
IF #NumberOfRows > 0
BEGIN
UPDATE x
SET x.FirstColumn = #NewValue
,x.SecondColumn = SYSDATETIMEOFFSET()
,x.ThirdColumn = #Other
FROM TestTable x
WHERE x.ID = 100
END
ELSE
BEGIN
UPDATE x
SET x.SecondColumn = SYSDATETIMEOFFSET()
,x.ThirdColumn = #Other
FROM TestTable x
WHERE x.ID = 100
END
END

transform a function into a simple query

I have the following function that returns parcel's delay:
ALTER FUNCTION [dbo].[Delay]
(#DeliveryTime int, #DeliveryHour int = 0, #Product_ID int, #Country varchar(2), #ZipCode varchar(10)
)
RETURNS int
AS
BEGIN
DECLARE #Delay int, #Guaranteed Int, #MaxDeliveryTime int, #MaxHour int, #test int
if #Country = 'FR'
begin
select top 1 #Guaranteed = pz.TProduct_Guaranteed,
#MaxDeliveryTime = pz.TProduct_delay,
#MaxHour = pz.TProduct_HourMax
from TProductZone PZ
where (pz.Country_ID = '0' OR pz.Country_ID = cast(#Country as varchar(2)))
And (Zone_ID = '0' or Zone_ID = left(#ZipCode,2))
And p.tproduct_id = #Product_ID
Order by pz.country_id desc, zone_id desc
end
Else
Begin
select top 1 #Guaranteed = pz.TProduct_Guaranteed,
#MaxDeliveryTime = pz.TProduct_delay,
#MaxHour = pz.TProduct_HourMax
from TProductZone PZ
where (pz.Country_ID = '0' OR pz.Country_ID = cast(#Country as varchar(2)))
And (Zone_ID = '0')
And p.tproduct_id = #Product_ID
Order by pz.country_id desc, zone_id desc
End
if #Guaranteed = 1
Begin
if #DeliveryTime >= #MaxDeliveryTime
Begin
set #Delay = #DeliveryTime-#MaxDeliveryTime
if #DeliveryHour > #MaxHour
begin
set #Delay = #Delay+1
end
End
else
Begin
set #Delay = 0
end
End
Else
Begin
set #Delay = 0
End
return #Delay
END
This function is used as following:
Update TShipping
Set Delay = Delay(S.TShipping_Deliverytime, cast(left(isnull(TD.Tracking_Hour,0),2) as int), S.TProduct_ID, S.Country_Code, S.ZipCode)
From TShipping S
inner Join TrackingEndDelay TD On TD.TShipping_ID = S.TShipping_ID
WHERE IsNull(TShipping_Delivered,0) = 1
And DateDiff(day,TShipping_Date,getdate()) < 90
I want to transform this function (and the call of the function) in a unique simple query in order to decrease the response time.
This is the query I did:
Update TShipping
Set Delay =
case
WHEN TProduct_Guaranteed=0 or (TShipping_Deliverytime - TProduct_delay < 0) then 0
WHEN (TShipping_Deliverytime - TProduct_delay >= 0) and cast(left(isnull(TD.Tracking_Hour,0),2) as int) <= isnull(TProduct_HourMax,24) then TShipping_Deliverytime - TProduct_delay
when (TShipping_Deliverytime - TProduct_delay >= 0) and cast(left(isnull(TD.Tracking_Hour,0),2) as int) > isnull(TProduct_HourMax,24) then TShipping_Deliverytime - TProduct_delay +1
end
from tshipping TS
inner Join TrackingEndDelay TD On TD.TShipping_ID = TS.TShipping_ID
left join dbo.TShipping_DateInDelivery DID on ts.tshipping_id=DID.tshipping_id
outer apply (select top 1 pz.TProduct_Guaranteed, pz.TProduct_delay, pz.TProduct_HourMax
from TShipping S
inner join TProductZone PZ on S.tproduct_id = pz.tproduct_id
where (pz.Country_ID = '0' OR pz.Country_ID = S.Country_Code)
And (Zone_ID = '0' or (Zone_ID = left(S.TShipping_ZipCode,2) and S.Country_Code='FR'))
and TS.TShipping_ID=S.TShipping_ID
Order by pz.country_id desc, zone_id desc) r1
WHERE IsNull(TShipping_Delivered,0) = 1
And DateDiff(day,TShipping_Date,getdate()) < 90
The problem is that I increase the response time instead of decreasing it. Has someone another better way to do it faster??
Thank you for your help!!

SQL Server order by part of string

One of my tables have values like this..
Year 1
Year 9
Year 4
Kindy [can be any word without numbers]
Pre-School [can be any word without numbers]
Year 8
Year 22
Year 15....
How can I select Them in alphabetically first and then by numerically in ascending order like this..
Kindy [can be any word without numbers]
Pre-School [can be any word without numbers]
Year 1
Year 4
Year 8
Year 9
Year 15
Year 22
I could not extract the integer and order in this case as some of the years don't have it..
UPDATE
MY ANSWER -- MISSING DISTINCT
SELECT YearLevel FROM Student
ORDER BY
CASE WHEN YearLevel NOT LIKE '%[0-9]%' THEN 0
ELSE CAST(RIGHT(YearLevel, LEN(YearLevel) - 5) AS int)
END
Try this in the ORDER BY clause:
ORDER BY
CASE WHEN col = 'Kindy' then 0
WHEN col = 'Pre-School' then 1
ELSE CAST(SUBSTRING(col,6,LEN(col)) AS INT) + 1
END
In first you must create following function for get text part and number part of your string :
CREATE FUNCTION [dbo].[GetNumbersFromText](#String varchar(2000))
RETURNS INT
AS BEGIN
DECLARE #Count INT = 0,
#IntNumbers VARCHAR(1000) = '',
#FindNumber BIT = 0
WHILE #Count <= LEN(#String) BEGIN
IF SUBSTRING(#String,#Count,1) >= '0' AND SUBSTRING(#String,#Count,1) <= '9' BEGIN
SET #IntNumbers = #IntNumbers + SUBSTRING(#String,#Count,1)
SET #FindNumber = 1
END ELSE IF (#FindNumber = 1) BEGIN
BREAK
END
SET #Count = #Count + 1
END
RETURN CAST(#IntNumbers AS INT)
END
CREATE FUNCTION [dbo].[GetTextPartOfText](#String varchar(2000))
RETURNS INT
AS BEGIN
DECLARE #Count INT = 0,
#Text VARCHAR(1000) = '',
WHILE #Count <= LEN(#String) BEGIN
IF SUBSTRING(#String,#Count,1) >= '0' AND SUBSTRING(#String,#Count,1) <= '9' BEGIN
BREAK
END ELSE BEGIN
SET #Text = #Text + SUBSTRING(#String,#Count,1)
END
SET #Count = #Count + 1
END
RETURN #Text
END
In second you use following query for your sort:
SELECT *
FROM YourTable
ORDER BY [dbo].[GetTextPartOfText](TextColumn),[dbo].[GetNumbersFromText](TextColumn), TextColumn
SELECT YearLevel FROM student
GROUP BY YearLevel
ORDER BY
(CASE
WHEN YearLevel LIKE 'Year%'
THEN 'Year' + CONVERT(varchar,LEN(YearLevel)) + YearLevel
ELSE YearLevel
END)

Resources