Время запроса Sql изменяется от 1 секунды до более 1 минуты, если в предложении WHERE используется функция - PullRequest
2 голосов
/ 05 февраля 2010

У меня есть этот запрос в MS SQL, который действует очень странно (по крайней мере, с моей точки зрения).

У меня есть пользовательская функция с именем: dbo.NajblizszaDataWyceny (3, '2010-02-05'), которая представляет собой простую проверку для записи TOP 1 в одной таблице, объединенной с парой других. Сам запрос занимает миллисекунды, так что это не большая проблема, но я все равно показываю функцию.

CREATE FUNCTION [dbo].[NajblizszaDataWyceny] (@idPortfela INT, @dataWaluty DATETIME)
RETURNS DATETIME
AS BEGIN
RETURN (

SELECT TOP 1         [WycenaData]
FROM    [BazaZarzadzanie].[dbo].[Wycena] t1
    LEFT JOIN [BazaZarzadzanie].[dbo].[KlienciPortfeleKonta] t3
    ON t1.[KlienciPortfeleKontaID] = t3.[KlienciPortfeleKontaID]
    LEFT JOIN [BazaZarzadzanie].[dbo].[KlienciPortfele] t4
    ON t3.[PortfelID] = t4.[PortfelID]
WHERE   [WycenaData] <= @dataWaluty  AND [t3].[PortfelID] = @idPortfela
ORDER BY [WycenaData] DESC)
END

Когда я использую эту функцию следующим образом:

DECLARE @dataWyceny DATETIME
SET @dataWyceny = dbo.NajblizszaDataWyceny(3, '2010-02-05') 

SELECT  t1.[KlienciPortfeleKontaID],
    t4.[PortfelIdentyfikator] AS 'UmowaNr',
    t5.[KlienciRachunkiNumer],
    [WycenaData],
    t2.[InISIN] AS 'InstrumentISIN',
    t2.[InNazwa] AS 'InstrumentNazwa',
    [WycenaWartosc]
FROM    [BazaZarzadzanie].[dbo].[Wycena] t1
    LEFT JOIN [BazaZarzadzanie].[dbo].[Instrumenty] t2
    ON t1.[InID] = t2.[InID]
    LEFT JOIN [BazaZarzadzanie].[dbo].[KlienciPortfeleKonta] t3
    ON t1.[KlienciPortfeleKontaID] = t3.[KlienciPortfeleKontaID]
    LEFT JOIN [BazaZarzadzanie].[dbo].[KlienciPortfele] t4
    ON t3.[PortfelID] = t4.[PortfelID]
    LEFT JOIN [BazaZarzadzanie].[dbo].[KlienciRachunki] t5
    ON t3.[KlienciRachunkiID] = t5.[KlienciRachunkiID]
    LEFT JOIN [BazaZarzadzanie].[dbo].[WycenaTyp] t6
    ON t1.[WycenaTyp] = t6.[WycenaTyp]
WHERE   WycenaData = @dataWyceny     AND t3.[PortfelID] = 3
ORDER BY t5.[KlienciRachunkiNumer],
    WycenaData

для запуска требуется 1 секунда. Но когда я помещаю пользовательскую функцию прямо в WHERE, она выглядит следующим образом:

SELECT  t1.[KlienciPortfeleKontaID],
    t4.[PortfelIdentyfikator] AS 'UmowaNr',
    t5.[KlienciRachunkiNumer],
    [WycenaData],
    t2.[InISIN] AS 'InstrumentISIN',
    t2.[InNazwa] AS 'InstrumentNazwa',
    [WycenaWartosc]
FROM    [BazaZarzadzanie].[dbo].[Wycena] t1
    LEFT JOIN [BazaZarzadzanie].[dbo].[Instrumenty] t2
    ON t1.[InID] = t2.[InID]
    LEFT JOIN [BazaZarzadzanie].[dbo].[KlienciPortfeleKonta] t3
    ON t1.[KlienciPortfeleKontaID] = t3.[KlienciPortfeleKontaID]
    LEFT JOIN [BazaZarzadzanie].[dbo].[KlienciPortfele] t4
    ON t3.[PortfelID] = t4.[PortfelID]
    LEFT JOIN [BazaZarzadzanie].[dbo].[KlienciRachunki] t5
    ON t3.[KlienciRachunkiID] = t5.[KlienciRachunkiID]
    LEFT JOIN [BazaZarzadzanie].[dbo].[WycenaTyp] t6
    ON t1.[WycenaTyp] = t6.[WycenaTyp]
WHERE   WycenaData = dbo.NajblizszaDataWyceny(3, '2010-02-05')      AND t3.[PortfelID] = 3
ORDER BY t5.[KlienciRachunkiNumer],
    WycenaData

Требуется 1,5 минуты, чтобы закончить. Кто-нибудь может объяснить, почему это происходит?

Ответы [ 3 ]

7 голосов
/ 05 февраля 2010

Функции не предполагаются чистыми в SQL Server, что означает, что оптимизатор запросов не будет кэшировать результаты функции и повторно использовать ее; функция будет вызываться каждый раз, когда на нее ссылаются. Это справедливо даже для простых функций, которые просто возвращают числа (как мы выяснили в нашей стоимости в проекте, где мы использовали функции для эмуляции констант ...).

Таким образом, в первой версии функция вызывается один раз, когда вы вызываете ее, и результат кэшируется вручную и повторно используется в запросе. Однако во второй версии функция будет вызываться для каждой строки, когда предложение WHERE пытается соответствовать строке. Если у вас много строк, то несколько миллисекунд на строку начинают складываться.

(Обратите также внимание, что ваши запросы семантически отличаются. В первом запросе вы говорите «где все совпадает с результатом функции, которую я оценил в начале», а во втором - « где вещи совпадают с результатом функции, которую я оцениваю в этом конкретном экземпляре во времени, когда я рассматриваю строку ". Поскольку ваша функция использует оператор SELECT, то - в зависимости от уровня изоляции транзакции - она ​​может вернуть разные результаты для разных строк при изменении базовых данных.)

2 голосов
/ 05 февраля 2010

Во втором примере кода функция вызывается для каждой строки в результирующей таблице соединений. Их будет много.

Во-первых, он вызывается только один раз.

0 голосов
/ 05 февраля 2010

Сервер базы данных, по-видимому, недостаточно умен, чтобы решить, что он может оценить функцию только один раз, а затем использовать ее в качестве константы в индексе.

Это более старая версия MS SQL?

Кроме того, вам может потребоваться как-то объявить функцию детерминированной (возвращающей одно и то же значение для одного и того же ввода), если MS-SQL имеет такую ​​опцию.

Обновление: Только что увидел, что ваша функция "простая проверка на ТОП 1 записи в одной таблице, объединенной с парой других". Это означает, что функция не является детерминированной и не зависит от данных базы данных. Оптимизатор не сможет ускорить это.

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