Como ingressar em uma tabela com uma função com valor de tabela?

53

Eu tenho uma função definida pelo usuário:

create function ut_FooFunc(@fooID bigint, @anotherParam tinyint)
returns @tbl Table (Field1 int, Field2 varchar(100))
as
begin
  -- blah blah
end

Agora eu quero juntar isso em outra tabela, assim:

select f.ID, f.Desc, u.Field1, u.Field2
from Foo f 
join ut_FooFunc(f.ID, 1) u -- doesn't work
where f.SomeCriterion = 1

Em outras palavras, para todos os Fooregistros onde SomeCriterioné 1, quero ver o Foo IDe Desc, juntamente com os valores de Field1e Field2que são retornados ut_FooFuncpara uma entrada de Foo.ID.

Qual é a sintaxe para fazer isso?

Shaul Behr
fonte

Respostas:

84

Você CROSS APPLYnão precisa participar.

A definição de expressões de tabela envolvidas em junções deve ser estável. Ou seja, eles não podem ser correlacionados de modo que a expressão da tabela signifique algo diferente, dependendo do valor de uma linha em outra tabela.

select f.ID, f.Desc, u.Field1, u.Field2
from Foo f 
Cross apply ut_FooFunc(f.ID, 1) u
where f.SomeCriterion = ...
Martin Smith
fonte
0

Eu sei que o tópico é antigo, me fizeram a mesma pergunta, fiz um teste, o resultado da seguinte forma ...

Registros em FacCurrencyRate = 14264 enquanto TestFunction retorna 105 se executado independentemente.

    SELECT F.*, x.CurrencyKey, x.CurrencyName
    FROM ( 
           SELECT CurrencyKey, CurrencyName FROM dbo.TestFunction()
        ) x
    INNER JOIN [dbo].[FactCurrencyRate] F ON x.CurrencyKey = f.CurrencyKey;

O tempo de execução é ...

    (14264 rows affected)
    Table 'FactCurrencyRate'. Scan count 1, logical reads 75, physical reads 1, read-ahead reads 73, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'DimCurrency'. Scan count 1, logical reads 2, physical reads 1, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 31 ms,  elapsed time = 749 ms.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.

Se eu usar a resposta sugerida da seguinte maneira ...

select F.*, x.CurrencyKey, x.CurrencyName from [dbo].[FactCurrencyRate] F
cross apply dbo.TestFunction() x

O tempo de execução e a contagem de resultados são ...

(1497720 rows affected)
Table 'FactCurrencyRate'. Scan count 1, logical reads 75, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 1, logical reads 38110, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'DimCurrency'. Scan count 1, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 2106 ms,  elapsed time = 43242 ms.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.

O que vejo aqui é que a consulta interna traz um conjunto mais correto de resultados e o tempo de execução é muito mais eficiente. Corrija-me com uma abordagem melhor para realizar o mesmo!

user3104116
fonte
11
A aplicação cruzada é aplicada a cada linha externa (linha por linha); é por isso que é muito mais lenta, especialmente em conjuntos de dados maiores. Ao revertê-lo (porque você deseja apenas resultados que correspondam ao TVF, reduz bastante o tempo de execução (sem chamadas que não fazem nada), bem como quantas linhas são retornadas. Mas suas duas instruções não são equivalentes à segunda retorna muito mais linhas que a primeira.Tanto quanto a correção, isso depende dos requisitos da sua empresa
Jonathan Fite 18/04
Sim eu concordo. No entanto, escrevi o código à luz da pergunta inicial, na qual assumi que existe uma relação entre muitas tabelas. E, em segundo lugar, me fizeram uma pergunta em uma entrevista. Graças
user3104116