Alguns exemplos para mostrar, apenas no caso:
Tabela embutida avaliada
CREATE FUNCTION MyNS.GetUnshippedOrders()
RETURNS TABLE
AS
RETURN SELECT a.SaleId, a.CustomerID, b.Qty
FROM Sales.Sales a INNER JOIN Sales.SaleDetail b
ON a.SaleId = b.SaleId
INNER JOIN Production.Product c ON b.ProductID = c.ProductID
WHERE a.ShipDate IS NULL
GO
Tabela com várias instruções avaliada
CREATE FUNCTION MyNS.GetLastShipped(@CustomerID INT)
RETURNS @CustomerOrder TABLE
(SaleOrderID INT NOT NULL,
CustomerID INT NOT NULL,
OrderDate DATETIME NOT NULL,
OrderQty INT NOT NULL)
AS
BEGIN
DECLARE @MaxDate DATETIME
SELECT @MaxDate = MAX(OrderDate)
FROM Sales.SalesOrderHeader
WHERE CustomerID = @CustomerID
INSERT @CustomerOrder
SELECT a.SalesOrderID, a.CustomerID, a.OrderDate, b.OrderQty
FROM Sales.SalesOrderHeader a INNER JOIN Sales.SalesOrderHeader b
ON a.SalesOrderID = b.SalesOrderID
INNER JOIN Production.Product c ON b.ProductID = c.ProductID
WHERE a.OrderDate = @MaxDate
AND a.CustomerID = @CustomerID
RETURN
END
GO
Existe uma vantagem em usar um tipo (instrução em linha ou multi) sobre o outro? Existem certos cenários em que um é melhor que o outro ou as diferenças são puramente sintáticas? Sei que as duas consultas de exemplo estão fazendo coisas diferentes, mas existe uma razão para escrevê-las dessa maneira?
Lendo sobre eles e as vantagens / diferenças não foram realmente explicadas.
Respostas:
Ao pesquisar o comentário de Matt, revi minha declaração original. Ele está correto, haverá uma diferença no desempenho entre uma função com valor de tabela em linha (ITVF) e uma função com valor de tabela com várias instruções (MSTVF), mesmo que ambos simplesmente executem uma instrução SELECT. O SQL Server tratará um ITVF como um
VIEW
na medida em que calculará um plano de execução usando as estatísticas mais recentes nas tabelas em questão. Um MSTVF é equivalente a colocar todo o conteúdo da sua instrução SELECT em uma variável de tabela e depois ingressar nela. Portanto, o compilador não pode usar nenhuma estatística de tabela nas tabelas no MSTVF. Sendo assim, todas as coisas iguais (o que raramente são), o ITVF terá um desempenho melhor que o MSTVF. Nos meus testes, a diferença de desempenho no tempo de conclusão era insignificante, no entanto, do ponto de vista estatístico, era perceptível.No seu caso, as duas funções não são funcionalmente equivalentes. A função MSTV faz uma consulta extra toda vez que é chamada e, mais importante, filtra a identificação do cliente. Em uma consulta grande, o otimizador não poderia tirar proveito de outros tipos de junções, pois precisaria chamar a função para cada customerId passado. No entanto, se você reescreveu sua função MSTV da seguinte maneira:
Em uma consulta, o otimizador poderia chamar essa função uma vez e criar um melhor plano de execução, mas ainda assim não seria melhor que um ITVS ou a equivalente, não parametrizado
VIEW
.Os ITVFs devem ter preferência sobre os MSTVFs, quando possível, porque os tipos de dados, anulabilidade e intercalação das colunas na tabela, enquanto você declara essas propriedades em uma função com valor de tabela com várias instruções e, principalmente, obterá melhores planos de execução do ITVF. Na minha experiência, não encontrei muitas circunstâncias em que uma ITVF era uma opção melhor que uma VIEW, mas a milhagem pode variar.
Graças a Matt.
Adição
Desde que vi isso surgir recentemente, aqui está uma excelente análise feita por Wayne Sheffield, comparando a diferença de desempenho entre as funções de valor de tabela em linha e as funções de múltiplas declarações.
Sua postagem original no blog.
Copiar no SQL Server Central
fonte
Internamente, o SQL Server trata uma função com valor de tabela embutida da mesma maneira que faria em uma exibição e trata uma função com valor de tabela com várias instruções semelhante à maneira como trataria um procedimento armazenado.
Quando uma função com valor de tabela embutida é usada como parte de uma consulta externa, o processador de consultas expande a definição de UDF e gera um plano de execução que acessa os objetos subjacentes, usando os índices nesses objetos.
Para uma função com valor de tabela com várias instruções, um plano de execução é criado para a própria função e armazenado no cache do plano de execução (depois que a função é executada pela primeira vez). Se funções com valor de tabela com várias instruções são usadas como parte de consultas maiores, o otimizador não sabe o que a função retorna e, portanto, faz algumas suposições padrão - na verdade, assume que a função retornará uma única linha e que os retornos de a função será acessada usando uma varredura de tabela em uma tabela com uma única linha.
Onde as funções com valor de tabela de várias instruções podem ter um desempenho ruim é quando elas retornam um grande número de linhas e são unidas em consultas externas. Os problemas de desempenho devem-se principalmente ao fato de o otimizador produzir um plano assumindo que uma única linha seja retornada, o que não será necessariamente o plano mais apropriado.
Como regra geral, descobrimos que, sempre que possível, funções com valor de tabela em linha devem ser usadas preferencialmente a funções com várias instruções (quando o UDF será usado como parte de uma consulta externa) devido a esses possíveis problemas de desempenho.
fonte
Há outra diferença. Uma função embutida com valor de tabela pode ser inserida, atualizada e excluída de - assim como uma exibição. Restrições semelhantes se aplicam - não é possível atualizar funções usando agregados, não é possível atualizar colunas calculadas e assim por diante.
fonte
Acho que seus exemplos respondem muito bem à pergunta. A primeira função pode ser executada como uma única seleção e é um bom motivo para usar o estilo embutido. O segundo provavelmente poderia ser feito como uma única instrução (usando uma subconsulta para obter a data máxima), mas alguns codificadores podem achar mais fácil ler ou mais natural fazê-lo em várias instruções, como você fez. Algumas funções simplesmente não podem ser executadas em uma instrução e, portanto, requerem a versão de múltiplas instruções.
Eu sugiro usar o mais simples (em linha) sempre que possível, e usar várias instruções quando necessário (obviamente) ou quando a preferência / legibilidade pessoal fizer com que a digitação extra seja necessária.
fonte
veja Comparando funções com valor de tabela em linha e com várias instruções, você pode encontrar boas descrições e referências de desempenho
fonte
Não testei isso, mas uma função de instrução múltipla armazena em cache o conjunto de resultados. Pode haver casos em que há muita coisa acontecendo para o otimizador alinhar a função. Por exemplo, suponha que você tenha uma função que retorne um resultado de bancos de dados diferentes, dependendo do que você passa como "Número da Empresa". Normalmente, você poderia criar uma visualização com um sindicato e depois filtrar por número da empresa, mas descobri que algumas vezes o servidor sql retira o sindicato inteiro e não é inteligente o suficiente para chamar o seleto. Uma função de tabela pode ter lógica para escolher a fonte.
fonte
Outro caso para usar uma função de linha múltipla seria evitar que o servidor sql pressione a cláusula where.
Por exemplo, eu tenho uma tabela com nomes de tabela e alguns nomes de tabela são formatados como C05_2019 e C12_2018 e todas as tabelas formatadas dessa maneira têm o mesmo esquema. Eu queria mesclar todos esses dados em uma tabela e analisar 05 e 12 em uma coluna CompNo e 2018,2019 em uma coluna de ano. No entanto, existem outras tabelas como ACA_StupidTable que não consigo extrair CompNo e CompYr e receberiam um erro de conversão se tentasse. Portanto, minha consulta foi dividida em duas partes, uma consulta interna que retornou apenas tabelas formatadas como 'C_______' e, em seguida, a consulta externa fez uma sub-string e uma conversão int. ie Cast (Substring (2, 2) como int) como CompNo. Tudo parece bom, exceto que o servidor sql decidiu colocar minha função Cast antes que os resultados fossem filtrados e, portanto, recebo um erro de conversão. Uma função de tabela de múltiplas instruções pode impedir que isso aconteça,
fonte
Talvez de uma maneira muito condensada. ITVF (TVF em linha): mais se você é uma pessoa DB, é tipo de exibição parametrizada, faça um único SELECT st
MTVF (TVF de múltiplas instruções): desenvolvedor, cria e carrega uma variável de tabela.
fonte
se você for fazer uma consulta, poderá ingressar na função Valor da Tabela Inline, como:
incorrerá pouco em cima e funcionará bem.
se você tentar usar a tabela de instruções múltiplas avaliada em uma consulta semelhante, terá problemas de desempenho:
porque você executará a função 1 vez para cada linha retornada, à medida que o conjunto de resultados for maior, ele será executado cada vez mais devagar.
fonte