我有一个将Gregorian datetime转换为Jalali datetime的sql函数。 我有一张桌子
Id StartDate FinishDate AlarmDate
当我执行一个简单的查询,例如这个
select Id,dbo.Jalali(StartDate) , dbo.Jalali(FinishDate),dbo.Jalali(AlarmDate) from MyTable
此查询将浪费超过2分钟的时间。该表只有2000条记录。
解决方案是什么?
功能
create FUNCTION dbo.MiladiToShamsi
(@dd datetime)
RETURNS char(10)
AS
BEGIN
DECLARE @mahs as char(2)
DECLARE @rozs as char(2)
DECLARE @diff As int
DECLARE @i As int
DECLARE @leap As int
DECLARE @roz AS int
DECLARE @mah As int
DECLARE @sal As int
SELECT @roz = 11
SELECT @mah = 10
SELECT @sal = 1358
SELECT @diff = DateDiff("d", cast('1980/01/01' as datetime), @dd) -- leap year
SELECT @i = 1
while @i <= @diff
BEGIN
SELECT @roz = @roz + 1
If @mah = 12 And ((@sal+1) - ((@sal+1)/4)*4) <> 0
If @roz > 29 BEGIN
SELECT @roz = 1
SELECT @mah = @mah + 1
End
If @mah > 12 BEGIN
SELECT @sal = @sal + 1
SELECT @mah = 1
End
If @mah > 6
If @roz > 30 BEGIN
SELECT @roz = 1
SELECT @mah = @mah + 1
End
if @mah <= 6
If @roz > 31 BEGIN
SELECT @roz = 1
SELECT @mah = @mah + 1
End
SELECT @i = @i + 1
END
if @mah < 10
SELECT @mahs = '0' + LTRIM(RTRIM(str(@mah)))
else
SELECT @mahs = LTRIM(RTRIM(str(@mah)))
if @roz < 10
SELECT @rozs = '0' + LTRIM(RTRIM(str(@roz)))
else
SELECT @rozs = LTRIM(RTRIM(str(@roz)))
RETURN LTRIM(RTRIM(str(@sal))) + '/' + LTRIM(RTRIM(@mahs)) + '/' + LTRIM(RTRIM(@rozs))
END
答案 0 :(得分:4)
您的功能问题取决于它循环的次数。您将于1980年1月1日开始参考日。因此,要获得最新信息,您需要循环大约30 * 365(11,000次)。我对Jalali日历一无所知,但通过查看代码,似乎每个公历日期在Jalali日历系统中只有一个表示。因此,您可以使用简单的查找表替换您的函数(执行大量循环)。
构建查找表:
CREATE TABLE [dbo].[Calendar](
[Gregorian] [datetime] NOT NULL,
[Jalali] [char](10) NOT NULL,
CONSTRAINT [PK_Calendar] PRIMARY KEY CLUSTERED
(
[Gregorian] ASC,
[Jalali] ASC
) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [idx_Calendar_Jalali_Gregorian] ON [dbo].[Calendar]
(
[Jalali] ASC,
[Gregorian] ASC
) ON [PRIMARY]
GO
使用值填充查找表:
Declare @dd datetime
DECLARE @mahs as char(2)
DECLARE @rozs as char(2)
DECLARE @diff As int
DECLARE @i As int
DECLARE @leap As int
DECLARE @roz AS int
DECLARE @mah As int
DECLARE @sal As int
SELECT @roz = 11
SELECT @mah = 10
SELECT @sal = 1358
SELECT @diff = DateDiff("d", cast('1980/01/01' as datetime), @dd) -- leap year
SELECT @i = 1
Set @dd = '19800101'
while @dd <= '22000101'
BEGIN
SELECT @roz = @roz + 1
If @mah = 12 And ((@sal+1) - ((@sal+1)/4)*4) <> 0
If @roz > 29 BEGIN
SELECT @roz = 1
SELECT @mah = @mah + 1
End
If @mah > 12 BEGIN
SELECT @sal = @sal + 1
SELECT @mah = 1
End
If @mah > 6
If @roz > 30 BEGIN
SELECT @roz = 1
SELECT @mah = @mah + 1
End
if @mah <= 6
If @roz > 31 BEGIN
SELECT @roz = 1
SELECT @mah = @mah + 1
End
if @mah < 10
SELECT @mahs = '0' + LTRIM(RTRIM(str(@mah)))
else
SELECT @mahs = LTRIM(RTRIM(str(@mah)))
if @roz < 10
SELECT @rozs = '0' + LTRIM(RTRIM(str(@roz)))
else
SELECT @rozs = LTRIM(RTRIM(str(@roz)))
Insert Into Calendar(Gregorian, Jalali)
Select @dd, LTRIM(RTRIM(str(@sal))) + '/' + LTRIM(RTRIM(@mahs)) + '/' + LTRIM(RTRIM(@rozs))
SELECT @dd = DATEADD(day, 1, @dd)
END
现在您可以将功能简化为:
CREATE FUNCTION dbo.MiladiToShamsi
(@dd datetime)
RETURNS char(10)
AS
BEGIN
Return (Select Jalali
From dbo.Calendar
Where Gregorian = DateAdd(Day, DateDiff(Day, 0, @dd), 0)
)
END
现在,当您运行查询时,它应该表现得更好。但是,当您具有执行像此访问权限的用户定义函数时,性能可能仍会受到影响,因为SQL Server将为每个函数调用访问一次表。相反,最好不要使用该功能。现在有一个查找表,你可以简单地加入它(3次)来获得所有的转换,如下所示:
select Id,
StartDate.Jalali As StartDate,
FinishDate.Jalali As FinishDate,
AlarmDate.Jalali As AlarmDate
From MyTable
Inner Join Calendar As StartDate
On MyTable.StartDate = StartDate.Gregorian
Inner Join Calendar As FinishDate
On MyTable.FinishDate = FinishDate.Greogorian
Inner Join Calendar As AlarmDate
On MyTable.AlarmDate = AlarmDate.Gregorian
你说,在原帖中,你花了2分多钟才得到你的结果。如果您决定在这里遵循我的建议,我很想知道我描述的方法需要多长时间。我绝对相信它会比你现在的方法更快。