2011-04-30 28 views
0

我有一个将公历日期时间转换为Jalali日期时间的sql函数。 我有一个表作为用户定义的函数在查询中是耗时的,现在是什么?

Id  StartDate  FinishDate  AlarmDate 

当我EXCUTE一个简单的查询,如本

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 
+1

请提供函数定义。 – 2011-04-30 11:53:50

+0

我已经听说过关于clr的功能。是否有解决方案?他们是否更快? – Saleh 2011-04-30 11:59:18

+1

是的,他们可以更快地进行字符串操作。由于这个函数没有做任何数据访问,所以你应该使用'WITH SCHEMABINDING'选项,我也不确定这个函数到底在做什么,但是看看如何去掉程序代码 – 2011-04-30 12:06:00

回答

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分钟得到你的结果。如果你决定遵循我的建议,我会很好奇,知道我描述的方法需要多长时间。我绝对相信它会比现在的方法更快。

相关问题