Моя система выполняет довольно тяжелую обработку, и я атаковал производительность, чтобы дать мне возможность выполнять больше тестовых прогонов в более короткие сроки.
У меня есть довольно много случаев, когда нужно вызывать UDF, скажем, 5 миллионов строк (и я в значительной степени думал, что не было никакого способа обойти это).
Что ж, оказывается, есть способ обойти это, и это дает огромные улучшения производительности, когда пользовательские функции вызываются через набор различных параметров, несколько меньших, чем общий набор строк.
Рассмотрим UDF, который принимает набор входных данных и возвращает результат, основанный на сложной логике, но для набора входных данных в 5-метровых строках, скажем, есть только 100 000 различных входных данных, и поэтому он будет производить только 100 000 различных наборов результатов (Мои конкретные случаи варьируются от процентных ставок до сложных назначений кода, но все они дискретны - фундаментальный момент с этой техникой заключается в том, что вы можете просто определить, сработает ли уловка, запустив SELECT DISTINCT
).
Я обнаружил, что делая что-то вроде этого:
INSERT INTO PreCalcs
SELECT param1
,param2
,dbo.udf_result(param1, param2) AS result
FROM (
SELECT DISTINCT param1, param2 FROM big_table
)
Когда PreCalcs соответствующим образом проиндексирован, комбинация этого с:
SELECT big_table.param1
,big_table.param2
,PreCalcs.result
FROM big_table
INNER JOIN PreCalcs
ON PreCalcs.param1 = big_table.param1
AND PreCalcs.param2 = big_table.param2
Вы получаете ОГРОМНОЕ повышение производительности. Очевидно, то, что что-то является детерминированным, не означает, что SQL Server кэширует прошлые вызовы и повторно использует их, как можно подумать.
Единственное, на что вам нужно обратить внимание, это когда NULL разрешены, тогда вам нужно тщательно исправить свои объединения:
SELECT big_table.param1
,big_table.param2
,PreCalcs.result
FROM big_table
INNER JOIN PreCalcs
ON (
PreCalcs.param1 = big_table.param1
OR COALESCE(PreCalcs.param1, big_table.param1) IS NULL
)
AND (
PreCalcs.param2 = big_table.param2
OR COALESCE(PreCalcs.param2, big_table.param2) IS NULL
)
Надеюсь, это поможет, и любые подобные трюки с UDF или рефакторинг запросов для повышения производительности приветствуются.
Полагаю, вопрос в том, почему ручное кэширование так необходимо - разве это не значит, что сервер знает, что функция является детерминированной? И если это имеет такое большое значение, и если UDF такие дорогие, почему оптимизатор просто не делает это в плане выполнения?