Meu primeiro pensamento aqui é -- qual é o problema de desempenho? Claro que você tem um loop (uma vez por linha para aplicar onde) dentro de um loop que executa uma consulta. Mas você está recebendo planos de execução ruins? Seus conjuntos de resultados são enormes? Mas vamos ao genérico. Como uma vez resolve este problema? SQL realmente não faz memoização (como o ilustre @Martin_Smith aponta). Então, o que um garoto pode fazer?
Opção 1 - Novo design
Crie um design totalmente novo. Neste caso específico @Aaron_Bertrand destaca que uma tabela de calendário pode atender às suas necessidades. Muito certo. Isso realmente não ajuda em situações que não sejam de calendário, mas, como geralmente acontece no SQL, você precisa pensar um pouco diferente.
Opção 2 - Chamar menos a UDF
Restrinja o conjunto de itens que chamam essa função. Isso me lembra muito como fazer paginação/contagem de linhas . Gere um pequeno conjunto de resultados que tenha os valores distintos necessários e então chame seu UDF para que ele seja chamado apenas algumas vezes. Isso pode ou não ser uma opção, mas pode funcionar em muitos cenários.
Opção 3 - UDF dinâmico
Eu provavelmente vou ser vaiado para fora da sala por esta sugestão, mas aqui vai. O que torna essa UDF lenta é a instrução select dentro do loop. Se sua tabela de feriados realmente muda com pouca frequência, você pode colocar um gatilho na mesa. O gatilho gravaria e atualizaria o UDF. A nova UDF poderia forçar todas as decisões do feriado. Seria um pouco como canibalismo com SQL escrevendo SQL? Claro. Mas isso eliminaria a subconsulta e aceleraria a UDF. Que comecem as provocações.
Opção 4 - Memorize!
Embora o SQL não possa memorizar diretamente, temos o SQL CLR. Converta o UDF em um udf SQL CLR. No CLR você pode usar variáveis estáticas. Você pode facilmente pegar a tabela Holidays em algum intervalo regular e armazená-las em uma tabela de hash. Em seguida, basta reescrever seu loop no CLR. Você pode até ir mais longe e memorizar a resposta inteira se essa for a lógica apropriada.
Atualização:
Opção 1 - Eu estava realmente tentando focar no geral aqui, não na função de exemplo que você usou acima. No entanto, o design atual de sua UDF permite várias chamadas para a tabela Holiday se você encontrar algumas seguidas. Usar algum tipo de tabela no estilo de calendário que contém uma lista de 'dias ruins' e o 'próximo dia útil' correspondente permitirá que você remova a possibilidade de vários acessos e consultas.
Opção 3 - Embora o domínio seja desconhecido com antecedência, você pode muito bem modificar sua tabela de feriados. Para um determinado dia de feriado, conteria o próximo dia útil correspondente. A partir desses dados, você pode cuspir uma UDF com uma instrução case longa (quando '5/5/2012' depois '5/14/2012' ou algo semelhante) na parte inferior. Essa estratégia pode não funcionar para todos os tipos de problemas, mas pode funcionar bem para alguns tipos de problemas.
Opção 4 - Há implicações para todas as tecnologias. O CLR precisa ser implantado, a configuração do SQL Server modificada e o SQL CLR é limitado à estrutura 3.5. Pessoalmente, achei esses ajustes bastante fáceis, mas sua situação pode ser diferente (digamos, um DBA recalcitrante ou restrições sobre modificações nos servidores de produção).
O uso de variáveis estáticas requer que os assemblies sejam concedeu CONFIANÇA TOTAL . Você terá que certificar-se de obter o seu bloqueio correto.
Há algumas evidências que em muito alto níveis de transação O CLR não funciona tão bem quanto o SQL direto. No seu cenário, no entanto, essa observação pode não ser aplicável porque não há uma correlação direta de SQL para o que você está tentando fazer (memoize).