2011-04-30 27 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分鐘得到你的結果。如果你決定遵循我的建議,我會很好奇,知道我描述的方法需要多長時間。我絕對相信它會比現在的方法更快。

相關問題