Related
I have a T-SQL function as listed below that needs to return a specific value when specific substrings are in the target string.
I've tried to research this, but I'm seeing inconsistencies in whether it is CHARINDEX(string, searchstring) (seems to be correct) or CHARINDEX(searchstring, string), and have looked into the LIKE and PATINDEX alternatives.
All three methods have resulted in no matches in obvious cases (most likely I just did something wrong), or it works, but also gives false positives.
In the example below, case '1ST' and '10ST' both return as if both were found as if it was '1ST10ST'.
I'm sure it is some simple format I'm missing, if you could point out the error.
Answers for LIKE, CHARINDEX and PATINDEX are welcome.
Function:
CREATE OR ALTER FUNCTION SDACalculateAttributeStaticBonus
(#intAttribute VARCHAR)
RETURNS int
BEGIN
DECLARE #intRunningTotal int = 0;
IF( CHARINDEX(#intAttribute, '1ST') > 0 )
BEGIN
SET #intRunningTotal = #intRunningTotal + 1;
END
IF( CHARINDEX(#intAttribute, '2ST') > 0 )
BEGIN
SET #intRunningTotal = #intRunningTotal + 2;
END
IF( CHARINDEX(#intAttribute, '3ST') > 0 )
BEGIN
SET #intRunningTotal = #intRunningTotal + 3;
END
IF( CHARINDEX(#intAttribute, '4ST') > 0 )
BEGIN
SET #intRunningTotal = #intRunningTotal + 4;
END
IF( CHARINDEX(#intAttribute, '5ST') > 0 )
BEGIN
SET #intRunningTotal = #intRunningTotal + 5;
END
IF( CHARINDEX(#intAttribute, '6ST') > 0 )
BEGIN
SET #intRunningTotal = #intRunningTotal + 6;
END
IF( CHARINDEX(#intAttribute, '7ST') > 0 )
BEGIN
SET #intRunningTotal = #intRunningTotal + 7;
END
IF( CHARINDEX(#intAttribute, '8ST') > 0 )
BEGIN
SET #intRunningTotal = #intRunningTotal + 8;
END
IF( CHARINDEX(#intAttribute, '9ST') > 0 )
BEGIN
SET #intRunningTotal = #intRunningTotal + 9;
END
IF( CHARINDEX(#intAttribute, '10ST') > 0 )
BEGIN
SET #intRunningTotal = #intRunningTotal + 10;
END
RETURN #intRunningTotal;
END
Current test statements (in case they're wrong somehow):
SELECT DISTINCT '1ST' AS Strength, dbo.SDACalculateAttributeStaticBonus('1ST') AS StrengthBonus
FROM HeroesViewMultiLine
SELECT DISTINCT '2ST' AS Strength, dbo.SDACalculateAttributeStaticBonus('2ST') AS StrengthBonus
FROM HeroesViewMultiLine
SELECT DISTINCT '3ST' AS Strength, dbo.SDACalculateAttributeStaticBonus('3ST') AS StrengthBonus
FROM HeroesViewMultiLine
SELECT DISTINCT '4ST' AS Strength, dbo.SDACalculateAttributeStaticBonus('4ST') AS StrengthBonus
FROM HeroesViewMultiLine
SELECT DISTINCT '5ST' AS Strength, dbo.SDACalculateAttributeStaticBonus('5ST') AS StrengthBonus
FROM HeroesViewMultiLine
SELECT DISTINCT '6ST' AS Strength, dbo.SDACalculateAttributeStaticBonus('6ST') AS StrengthBonus
FROM HeroesViewMultiLine
SELECT DISTINCT '7ST' AS Strength, dbo.SDACalculateAttributeStaticBonus('7ST') AS StrengthBonus
FROM HeroesViewMultiLine
SELECT DISTINCT '8ST' AS Strength, dbo.SDACalculateAttributeStaticBonus('8ST') AS StrengthBonus
FROM HeroesViewMultiLine
SELECT DISTINCT '9ST' AS Strength, dbo.SDACalculateAttributeStaticBonus('9ST') AS StrengthBonus
FROM HeroesViewMultiLine
SELECT DISTINCT '10ST' AS Strength, dbo.SDACalculateAttributeStaticBonus('10ST') AS StrengthBonus
FROM HeroesViewMultiLine
Aside from the issue with not defining a parameter length, I would not be implementing logic like this - or a scalar UDF at all.
Although you don't show it, presumably since your function is testing for each case, you can pass a string with multiple qualifying values which you want to sum.
You should always try to write table valued functions which are orders of magnitude faster, especially when used with larger resultsets.
Based on what you have shown, try the following:
create or alter function SDACalculateAttributeStaticBonus (#intAttribute varchar(50))
returns table
as
return (
select [Value]=Sum(try_convert(int,value))
from string_split(replace(#intAttribute, 'ST', ','), ',')
);
Use as you would any other table and select from it, join to it or apply it:
select [Value] from dbo.SDACalculateAttributeStaticBonus('10ST');
Result: 10
select [value] from dbo.SDACalculateAttributeStaticBonus('1ST2ST3ST');
Result: 6
SELECT DISTINCT '10ST' AS Strength, b.[value] AS StrengthBonus
FROM HeroesViewMultiLine
CROSS APPLY dbo.SDACalculateAttributeStaticBonus('10ST')b;
Because your parameter length might be declared higher than your input string length otherwise it will be 1 as the default value.
Besides that, I would use ELSE IF instead of multiple IF in your case, because it can get better performance.
create or alter FUNCTION SDACalculateAttributeStaticBonus(
#intAttribute VARCHAR(10)
)
RETURNS int
BEGIN
DECLARE #intRunningTotal int = 0;
IF( CHARINDEX(#intAttribute, '1ST') > 0 )
BEGIN
SET #intRunningTotal = #intRunningTotal + 1;
END
ELSE IF( CHARINDEX(#intAttribute, '2ST') > 0 )
BEGIN
SET #intRunningTotal = #intRunningTotal + 2;
END
ELSE IF( CHARINDEX(#intAttribute, '3ST') > 0 )
BEGIN
SET #intRunningTotal = #intRunningTotal + 3;
END
ELSE IF( CHARINDEX(#intAttribute, '4ST') > 0 )
BEGIN
SET #intRunningTotal = #intRunningTotal + 4;
END
ELSE IF( CHARINDEX(#intAttribute, '5ST') > 0 )
BEGIN
SET #intRunningTotal = #intRunningTotal + 5;
END
ELSE IF( CHARINDEX(#intAttribute, '6ST') > 0 )
BEGIN
SET #intRunningTotal = #intRunningTotal + 6;
END
ELSE IF( CHARINDEX(#intAttribute, '7ST') > 0 )
BEGIN
SET #intRunningTotal = #intRunningTotal + 7;
END
ELSE IF( CHARINDEX(#intAttribute, '8ST') > 0 )
BEGIN
SET #intRunningTotal = #intRunningTotal + 8;
END
ELSE IF( CHARINDEX(#intAttribute, '9ST') > 0 )
BEGIN
SET #intRunningTotal = #intRunningTotal + 9;
END
ELSE IF( CHARINDEX(#intAttribute, '10ST') > 0 )
BEGIN
SET #intRunningTotal = #intRunningTotal + 10;
END
RETURN #intRunningTotal;
END
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
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!!
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.
I want to convert my datetime into Persian datetime in SQL Server. My datetime is in MM/DD/YYYY format. Is there any function in SQL Server to do this as when I want hijri datetime I use this
SELECT CONVERT(VARCHAR(40), GETDATE(), 131) -- Output is 14/08/1432 5:02:01:197PM
I'm using SQL Server 2008.
Best method in SQL Server 2016
Example:
SELECT FORMAT(GETDATE(), 'yyyy/MM/dd-HH:mm:ss', 'fa')
Answer:
1398/10/08-05:37:59
I know it is too late for answering this question, but I've submitted the function that I'm using for a long time without any bug, all of other methods which I've ever seen have problem with intercalary years:
CREATE FUNCTION [CalculatePersianDate] ( #intDate DATETIME )
RETURNS NVARCHAR(max)
BEGIN
DECLARE #shYear AS INT ,#shMonth AS INT ,#shDay AS INT ,#intYY AS INT ,#intMM AS INT ,#intDD AS INT ,#Kabiseh1 AS INT ,#Kabiseh2 AS INT ,#d1 AS INT ,#m1 AS INT, #shMaah AS NVARCHAR(max),#shRooz AS NVARCHAR(max),#DayCnt AS INT
DECLARE #DayDate AS NVARCHAR(max)
SET #intYY = DATEPART(yyyy, #intDate)
IF #intYY < 1000 SET #intYY = #intYY + 2000
SET #intMM = MONTH(#intDate)
SET #intDD = DAY(#intDate)
SET #shYear = #intYY - 622
SET #DayCnt = 5
IF ( ( #intYY - 1992 ) % 4 = 0) SET #Kabiseh1 = 0 ELSE SET #Kabiseh1 = 1
IF ( ( #shYear - 1371 ) % 4 = 0) SET #Kabiseh2 = 0 ELSE SET #Kabiseh2 = 1
SET #m1 = 1
SET #d1 = 1
SET #shMonth = 10
SET #shDay = 11
IF ( ( #intYY - 1993 ) % 4 = 0 ) SET #shDay = 12
WHILE ( #m1 != #intMM ) OR ( #d1 != #intDD )
BEGIN
SET #d1 = #d1 + 1
SET #DayCnt = #DayCnt + 1
IF
(#d1 = 32 AND (#m1 = 1 OR #m1 = 3 OR #m1 = 5 OR #m1 = 7 OR #m1 = 8 OR #m1 = 10 OR #m1 = 12))
OR
(#d1 = 31 AND (#m1 = 4 OR #m1 = 6 OR #m1 = 9 OR #m1 = 11))
OR
(#d1 = 30 AND #m1 = 2 AND #Kabiseh1 = 1)
OR
(#d1 = 29 AND #m1 = 2 AND #Kabiseh1 = 0)
BEGIN
SET #m1 = #m1 + 1
SET #d1 = 1
END
IF #m1 > 12
BEGIN
SET #intYY = #intYY + 1
SET #m1 = 1
END
IF #DayCnt > 7 SET #DayCnt = 1
SET #shDay = #shDay + 1
IF
(#shDay = 32 AND #shMonth < 7)
OR
(#shDay = 31 AND #shMonth > 6 AND #shMonth < 12)
OR
(#shDay = 31 AND #shMonth = 12 AND #Kabiseh2 = 1)
OR
(#shDay = 30 AND #shMonth = 12 AND #Kabiseh2 = 0)
BEGIN
SET #shMonth = #shMonth + 1
SET #shDay = 1
END
IF #shMonth > 12
BEGIN
SET #shYear = #shYear + 1
SET #shMonth = 1
END
END
IF #shMonth=1 SET #shMaah=N'فروردین'
IF #shMonth=2 SET #shMaah=N'اردیبهشت'
IF #shMonth=3 SET #shMaah=N'خرداد'
IF #shMonth=4 SET #shMaah=N'تیر'
IF #shMonth=5 SET #shMaah=N'مرداد'
IF #shMonth=6 SET #shMaah=N'شهریور'
IF #shMonth=7 SET #shMaah=N'مهر'
IF #shMonth=8 SET #shMaah=N'آبان'
IF #shMonth=9 SET #shMaah=N'آذر'
IF #shMonth=10 SET #shMaah=N'دی'
IF #shMonth=11 SET #shMaah=N'بهمن'
IF #shMonth=12 SET #shMaah=N'اسفند'
IF #DayCnt=1 SET #shRooz=N'شنبه'
IF #DayCnt=2 SET #shRooz=N'یکشنبه'
IF #DayCnt=3 SET #shRooz=N'دوشنبه'
IF #DayCnt=4 SET #shRooz=N'سهشنبه'
IF #DayCnt=5 SET #shRooz=N'چهارشنبه'
IF #DayCnt=6 SET #shRooz=N'پنجشنبه'
IF #DayCnt=7 SET #shRooz=N'جمعه'
--SET #DayDate = #shRooz + " " + LTRIM(STR(#shDay,2)) + " " + #shMaah + " " + STR(#shYear,4)
--پنجشنبه 17 اردیبهشت 1394
/*
SET #DayDate = LTRIM(STR(#shDay,2)) + " " + #shMaah + " " + STR(#shYear,4)
--17 اردیبهشت 1394
SET #DayDate = STR(#shYear,4) + "/"+LTRIM(STR(#shMonth,2)) + "/" + LTRIM(STR(#shDay,2))
--1394/2/17
--1394/02/17
*/
SET #DayDate = REPLACE(RIGHT(STR(#shYear, 4), 4), ' ', '0') + '/'+ REPLACE(STR(#shMonth, 2), ' ', '0') + '/' + REPLACE(( STR(#shDay,2) ), ' ', '0')
RETURN #DayDate
END
It is really easy to customize the result of the function.
adopted from: this page
I know it is too late but maybe useful for others like me having this trouble.
You should write a SQL Function for this conversion like this:
Converting Gregorian to Persian Date
and then use it like this:
SELECT dbo.[UDF_Gregorian_To_Persian]('2013-08-24')
Try this:
select format(getdate() , 'yyyy/MM/dd', 'fa-ir')
You can use the following code to convert the date. This practical and important method has been added to the 2012 version of SQL and can be used.
SELECT FORMAT(GETDATE(), 'yyyy/MM/dd-HH:mm:ss', 'fa')
Result: 1400/02/08-05:08:51
SELECT cast( FORMAT(GETDATE(), 'yyyyMMdd', 'fa') as int)
Result: 14000208
And you can use Format as Follow to get Higri Date:
SELECT FORMAT(GETDATE(), N'yyyy/MM/dd', N'ar')
Result: 1443/06/19
Out of the box, no.
You'd have to write your own UDF, however there is one on CodePlex and another
I believe the best available solution is to use SQLCLR-Jalali-Date-Utility. It has a straightforward installation guide and easy to use functions. Moreover, you can define the format of the converted date without any limitation. in fact, you can use the standard time formatting to define the shape of converted dates.
There are several examples provided inside the GitHub page.
select dbo.GregorianToJalali(GETDATE(),'yyyy/MM/dd hh:mm:ss tt') -- returns 1395/07/01 03:04:33 ب ظ
this is persian Calendar function in SQL 2016+
ALTER FUNCTION [dbo].[PCalendar](#date datetime)
RETURNS #ret TABLE (
ly int,
y int,
m int,
mname nvarchar(15),
d int,
dy int,
dw int,
dname nvarchar(10),
hh int,
mm int,
ss int,
mss int,
dt datetime,
t nvarchar(3))
as
BEGIN
DECLARE #format varchar(19);
set #format = 'yyyy/MM/dd HH:mm:ss';
DECLARE #y int;
DECLARE #m int;
DECLARE #d int;
DECLARE #dy int;
DECLARE #dw int;
DECLARE #hh int;
DECLARE #mm int;
DECLARE #ss int;
DECLARE #ms int;
DECLARE #ldt varchar(8);
set #y = DATEPART(YEAR, FORMAT(#date, #format, 'fa')) ;
set #m = DATEPART(MONTH, FORMAT(#date, #format, 'fa'));
set #d = DATEPART(DAY, FORMAT(#date, #format, 'fa')) ;
set #dy = DATEPART(DAYOFYEAR, FORMAT(#date, #format, 'fa'));
set #dw = DATEPART(WEEKDAY, FORMAT(#date,#format, 'fa'));
set #hh = DATEPART(HOUR, #date) ;
set #mm = DATEPART(MINUTE, #date) ;
set #ss = DATEPART(SECOND, #date);
set #ms = DATEPART(MILLISECOND, #date);
set #ldt =DATEPART(year, FORMAT(#date, #format, 'en'));
DECLARE #_w nvarchar(10);
set #_w = CASE
WHEN #dw=1 THEN N'جمعه'
WHEN #dw=2 THEN N'شنبه'
WHEN #dw=3 THEN N'یکشنبه'
WHEN #dw=4 THEN N'دوشنبه'
WHEN #dw=5 THEN N'سه شنبه'
WHEN #dw=6 THEN N'چهارشنبه'
ELSE N'پنج شنبه'
END;
DECLARE #_m nvarchar(15);
set #_m = CASE
WHEN #m=1 THEN N'فروردین'
WHEN #m=2 THEN N'اردیبهشت'
WHEN #m=3 THEN N'خرداد'
WHEN #m=4 THEN N'تیر'
WHEN #m=5 THEN N'مرداد'
WHEN #m=6 THEN N'شهریور'
WHEN #m=7 THEN N'مهر'
WHEN #m=8 THEN N'آبان'
WHEN #m=9 THEN N'آذر'
WHEN #m=10 THEN N'دی'
WHEN #m=11 THEN N'بهمن'
ELSE N'اسفند'
END;
set #_m = #_m+N' ماه';
INSERT INTO #ret
SELECT
IIF(#y % 33 in (1,5,9,13,17,22,26,30) , 1 , 0) as ly,
#y as y,
#m as m,
#_m as mname,
#d as d,
#dy as dy,
#dw as dw,
#_w as dname,
#hh as hh,
#mm as mm,
#ss as ss,
#ms as mss,
#date as dt,
IIF(#hh > 12 , N'ب.ظ','ق.ظ') as t;
RETURN;
END
You can convert it to shamsi using this functions.
The first function.
CREATE FUNCTION [dbo].[ToPersianDate](#dt [datetime])
RETURNS [nvarchar](10) WITH EXECUTE AS CALLER
AS
EXTERNAL NAME [PersianSQLFunctions].[UserDefinedFunctions].[ToPersianDate]
GO
second function.
SET ANSI_NULLS OFF
GO
SET QUOTED_IDENTIFIER OFF
GO
CREATE FUNCTION [dbo].[ToPersianDate](#dt [datetime])
RETURNS [nvarchar](10) WITH EXECUTE AS CALLER
AS
EXTERNAL NAME [PersianSQLFunctions].[UserDefinedFunctions].[ToPersianDate]
GO
third function.
CREATE FUNCTION [dbo].[fnToShamsiDate]
(
#d DateTime
)
RETURNS NVARCHAR(10)
AS
BEGIN
-- Declare the return variable here
DECLARE #RV NVARCHAR(10)
IF (#d) IS NULL RETURN NULL;
ELSE SELECT #RV = DBO.ToPersianDate(#D);
-- Return the result of the function
RETURN #RV
END
GO
And also you can find shamsi months from this function
create function [dbo].[fnGetShamsiMonth]
(
#GregorianDate date
)
returns nvarchar(2)
as
begin
declare #ShamsiMonth nvarchar(2), #ShamsiDate nvarchar(10);
set #ShamsiDate = confsys.dbo.fnToShamsiDate(#GregorianDate);
set #ShamsiMonth = SUBSTRING(#ShamsiDate,6,2);
return #ShamsiMonth
end
GO
examples
select confsys.dbo.fnToShamsiDate(getdate())
result is 1397/12/29
get shamsi months
select confsys.dbo.fnGetShamsiMonth(GETDATE());
Function : Full Convert Persian (Shamsi / Jalali ) String to Gregorian (miladi) Datetime in sql server :
> create or ALTER Function [dbo].[Func_ShamsiToMiladi] (#Date
> Varchar(23) ) RETURNS DateTime BEGIN
> -- ==============================================================
> -- SELECT [dbo].[Func_ShamsiToMiladi] ('1356-09-20 05:35:00.000')
> --
> -- Output : '1977-12-11 02:05:00.000'
> -- ==============================================================
> -- BY: Shahrokh Vazifedan Hobabname#Gmail.COM DECLARE #PersianDate Varchar(23) SET #PersianDate = #Date
>
> DECLARE #Year INT = SUBSTRING(#PersianDate, 1, 4)
> DECLARE #Month INT = SUBSTRING(#PersianDate, 6, 2)
> DECLARE #Day INT = SUBSTRING(#PersianDate, 9, 2)
> DECLARE #DiffYear INT = #Year - 1350
> DECLARE #Time varchar(13) = SUBSTRING(#PersianDate, 11, 13)
>
>
> DECLARE #Days INT = #DiffYear * 365.24 +
> CASE WHEN #Month < 7 THEN (#Month - 1) * 31
> ELSE 186 + (#Month - 7) * 30 END + #Day
>
> DECLARE #StartDate DATETIME = '03/21/1971'
> DECLARE #ResultDate DATE = #StartDate + #Days
>
> DECLARE #TempDate varchar(23) = Convert( Nvarchar(10) , #ResultDate ,120) + #Time DECLARE #OffSET_First_half_in_Year
> INT; SET #OffSET_First_half_in_Year = iif( Substring(Convert(
> Nvarchar(50), #TempDate,120) ,6,16) Between '03-20 20:30' and '09-22
> 20:30' , -60 ,0)
> RETURN dateadd(MINUTE, #OffSET_First_half_in_Year + (-1)*datediff(MINUTE, getutcdate(), getdate()),#TempDate ) END
Created By : Shahrokh Vazifedan -Sari # Iran :)
Email: HobabName#Gmail.com
CREATE FUNCTION [dbo].[MITSH] (#MDate DateTime)
RETURNS Varchar(10)
AS
BEGIN
DECLARE #SYear as Integer
DECLARE #SMonth as Integer
DECLARE #my_mah varchar(2)
declare #my_day varchar(2)
DECLARE #SDay as Integer
DECLARE #AllDays as float
DECLARE #ShiftDays as float
DECLARE #OneYear as float
DECLARE #LeftDays as float
DECLARE #YearDay as Integer
DECLARE #Farsi_Date as Varchar(100)
SET #MDate=#MDate-CONVERT(char,#MDate,114)
SET #ShiftDays=466699 +2
SET #OneYear= 365.24199
SET #SYear = 0
SET #SMonth = 0
SET #SDay = 0
SET #AllDays = CAst(#Mdate as Real)
SET #AllDays = #AllDays + #ShiftDays
SET #SYear = (#AllDays / #OneYear) --trunc
SET #LeftDays = #AllDays - #SYear * #OneYear
if (#LeftDays < 0.5)
begin
SET #SYear=#SYear+1
SET #LeftDays = #AllDays - #SYear * #OneYear
end;
SET #YearDay = #LeftDays --trunc
if (#LeftDays - #YearDay) >= 0.5
SET #YearDay=#YearDay+1
if ((#YearDay / 31) > 6 )
begin
SET #SMonth = 6
SET #YearDay=#YearDay-(6 * 31)
SET #SMonth= #SMonth+( #YearDay / 30)
if (#YearDay % 30) <> 0
SET #SMonth=#SMonth+1
SET #YearDay=#YearDay-((#SMonth - 7) * 30)
end
else
begin
SET #SMonth = #YearDay / 31
if (#YearDay % 31) <> 0
SET #SMonth=#SMonth+1
SET #YearDay=#YearDay-((#SMonth - 1) * 31)
end
SET #SDay = #YearDay
SET #SYear=#SYear+1
if #SMonth <10 begin
set #my_mah='0'+str(#SMonth,1)
end else begin
set #my_mah = str(#SMonth,2)
end
if #sday <10 begin
set #my_day='0'+str(#Sday,1)
end else begin
set #my_day = str(#Sday,2)
end
SET #Farsi_Date = CAST (#SYear as VarChar(10)) + '/' + #my_mah + '/' + #my_day
Return #Farsi_Date
END
AN FOR EXEC FUNCTION
SELECT DBO.MITSH(GETDATE())
for example date is 2020-09-25
resualt =>>>> 1399/07/04
Complete Function For Shamsi date for SQL 2008 and 2008 R2 and below versions:
CREATE FUNCTION [dbo].[PersToJul](#iYear int,#iMonth int,#iDay int)
RETURNS bigint
AS
Begin
Declare #PERSIAN_EPOCH as int
Declare #epbase as bigint
Declare #epyear as bigint
Declare #mdays as bigint
Declare #Jofst as Numeric(18,2)
Declare #jdn bigint
Set #PERSIAN_EPOCH=1948321
Set #Jofst=2415020.5
If #iYear>=0
Begin
Set #epbase=#iyear-474
End
Else
Begin
Set #epbase = #iYear - 473
End
set #epyear=474 + (#epbase%2820)
If #iMonth<=7
Begin
Set #mdays=(Convert(bigint,(#iMonth) - 1) * 31)
End
Else
Begin
Set #mdays=(Convert(bigint,(#iMonth) - 1) * 30+6)
End
Set #jdn =Convert(int,#iday) + #mdays+ Cast(((#epyear * 682) - 110) / 2816 as int) + (#epyear - 1) * 365 + Cast(#epbase / 2820 as int) * 1029983 + (#PERSIAN_EPOCH - 1)
RETURN #jdn
End
--------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------
GO
CREATE FUNCTION [dbo].[GrToPers] (#date datetime)
Returns nvarchar(50)
AS
Begin
Declare #depoch as bigint
Declare #cycle as bigint
Declare #cyear as bigint
Declare #ycycle as bigint
Declare #aux1 as bigint
Declare #aux2 as bigint
Declare #yday as bigint
Declare #Jofst as Numeric(18,2)
Declare #jdn bigint
Declare #iYear As Integer
Declare #iMonth As Integer
Declare #iDay As Integer
Set #Jofst=2415020.5
Set #jdn=Round(Cast(#date as int)+ #Jofst,0)
Set #depoch = #jdn - [dbo].[PersToJul](475, 1, 1)
Set #cycle = Cast(#depoch / 1029983 as int)
Set #cyear = #depoch%1029983
If #cyear = 1029982
Begin
Set #ycycle = 2820
End
Else
Begin
Set #aux1 = Cast(#cyear / 366 as int)
Set #aux2 = #cyear%366
Set #ycycle = Cast(((2134 * #aux1) + (2816 * #aux2) + 2815) / 1028522 as int) + #aux1 + 1
End
Set #iYear = #ycycle + (2820 * #cycle) + 474
If #iYear <= 0
Begin
Set #iYear = #iYear - 1
End
Set #yday = (#jdn - [dbo].[PersToJul](#iYear, 1, 1)) + 1
If #yday <= 186
Begin
Set #iMonth = CEILING(Convert(Numeric(18,4),#yday) / 31)
End
Else
Begin
Set #iMonth = CEILING((Convert(Numeric(18,4),#yday) - 6) / 30)
End
Set #iDay = (#jdn - [dbo].[PersToJul](#iYear, #iMonth, 1)) + 1
Return Convert(nvarchar(50),#iDay) + '-' + Convert(nvarchar(50),#iMonth) +'-' + Convert(nvarchar(50),#iYear)
End
--------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------
GO
CREATE FUNCTION [dbo].[JulToGre] (#jdn bigint)
Returns nvarchar(11)
AS
Begin
Declare #Jofst as Numeric(18,2)
Set #Jofst=2415020.5
Return Convert(nvarchar(11),Convert(datetime,(#jdn- #Jofst),113),110)
End
--------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------
GO
CREATE FUNCTION [dbo].[COnvertOToN](#StrMyNum NVARCHAR(2))
RETURNS NVARCHAR(2)
AS
BEGIN
DECLARE #MyNunInStr NVARCHAR(10)
SET #MyNunInStr = #StrMyNum
IF LEN(#MyNunInStr) < 2
BEGIN
SET #MyNunInStr = '0' + #MyNunInStr
END
RETURN #MyNunInStr
END
--------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------
GO
-- Changing Date Format
CREATE FUNCTION [dbo].[RevDateShm](#StrDateShamsi NVARCHAR(10), #Seperator CHAR(1))
RETURNS NVARCHAR(10)
AS
BEGIN
DECLARE #StrDayOfMotn NVARCHAR(10)
DECLARE #StrMothOfYear NVARCHAR(10)
DECLARE #StrYearOfYear NVARCHAR(10)
SET #StrDayOfMotn = dbo.COnvertOToN(REPLACE(SUBSTRING(#StrDateShamsi , 1 , ((SELECT CHARINDEX('-' , #StrDateShamsi , 0)))), '-' , ''))
SET #StrMothOfYear = dbo.COnvertOToN(REPLACE(SUBSTRING(#StrDateShamsi , ((CHARINDEX('-' , #StrDateShamsi , 0) )) , 3) , '-' , ''))
SET #StrYearOfYear = RIGHT(#StrDateShamsi , 4)
return (#StrYearOfYear + #Seperator + #StrMothOfYear + #Seperator + #StrDayOfMotn)
END
--------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------
GO
CREATE FUNCTION [dbo].[ConvertShamsiToMiladiDate](#InputShamsiDateString nvarchar(10))
RETURNS datetime
AS
BEGIN
declare #InputShamsiDateString1 nvarchar(10)
declare #yearm int
declare #monthm int
declare #daym int
set #yearm = CONVERT(int , SUBSTRING(#InputShamsiDateString , 1 , 4))
set #monthm = CONVERT(int , SUBSTRING(#InputShamsiDateString , 6 , 2))
set #daym = CONVERT(int , SUBSTRING(#InputShamsiDateString , 9 , 2))
return (select dbo.[JulToGre](dbo.[PersToJul](#yearm,#monthm ,#daym )))
END
--------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------
GO
-- The Latest And Main Function
CREATE FUNCTION [dbo].[GetShmsiDate](#InputMiladiDate DateTime , #MySeperatorChar char(1))
RETURNS NVARCHAR(10)
AS
BEGIN
return (select dbo.RevDateShm(dbo.GrToPers(#InputMiladiDate), #MySeperatorChar) AS ShamsiDateOfLog)
END
GO
How to use:
SELECT dbo.GetShmsiDate(GETDATE() , N'/') AS ShamsiDate1,
dbo.GetShmsiDate(GETDATE() , N'-') AS ShamsiDate2
Result:
|ShamsiDate1|ShamsiDate2|
|-----------|-----------|
|1400/11/03 | 1400-11-03|
To convert a date to persian, try this code:
DECLARE #DateString NVARCHAR(200)='2022/09/07';
SELECT FORMAT(CAST(#DateString AS DATE),'yyyy/MM/dd','fa');