Пользовательские функции потребляют время в запросах, что теперь? - PullRequest
0 голосов
/ 30 апреля 2011

У меня есть функция 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

1 Ответ

4 голосов
/ 30 апреля 2011

Проблема с вашей функцией зависит от того, сколько раз она повторяется.Вы начинаете свой справочный день 1 января 1980 года. Таким образом, чтобы перейти к текущему состоянию, вам нужно выполнить цикл примерно 30 * 365 (11 000 раз).Я ничего не знаю о календаре Джалали, но, просматривая ваш код, кажется, что каждая дата григорианского календаря имеет ровно одно представление в системе календарей Джалали.Таким образом, вы можете заменить свою функцию (которая выполняет много циклов) простой таблицей поиска.

Чтобы построить таблицу поиска:

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минут, чтобы получить ваши результаты.Если вы решите последовать моему совету, мне было бы интересно узнать, сколько времени займет описанный мною метод.Я абсолютно уверен, что это будет быстрее, чем ваш текущий метод.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...