¿Cómo unir una tabla con una función con valores de tabla?

53

Tengo una función definida por el usuario:

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

Ahora quiero unir esto en otra mesa, así:

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

En otras palabras, para todos los Fooregistros donde SomeCriteriones 1, quiero ver Foo IDy Desc, junto con los valores de Field1y Field2que se devuelven ut_FooFuncpara una entrada de Foo.ID.

¿Cuál es la sintaxis para hacer esto?

Shaul Behr
fuente

Respuestas:

84

No necesitas CROSS APPLYunirte.

La definición de expresiones de tabla involucradas en uniones debe ser estable. Es decir, no se pueden correlacionar de modo que la expresión de la tabla signifique algo diferente dependiendo del valor de una fila en otra tabla.

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
fuente
0

Sé que el hilo es viejo, me hicieron la misma pregunta, hice una prueba, el resultado es el siguiente ...

Registros en FacCurrencyRate = 14264 mientras TestFunction devuelve 105 si se ejecuta de forma independiente.

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

El tiempo de ejecución es ...

    (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.

Si uso la respuesta sugerida de la siguiente manera ...

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

El tiempo de ejecución y el recuento de resultados es ...

(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.

Lo que veo aquí es que la consulta interna muestra un conjunto de resultados más correcto y el tiempo de ejecución es mucho más eficiente. ¡Corrígeme con un mejor enfoque para lograr lo mismo!

usuario3104116
fuente
1
La aplicación cruzada se aplica a cada fila exterior (así que fila por fila), es por eso que es mucho más lenta, especialmente en conjuntos de datos más grandes. Al revertirlo (porque solo desea resultados que coincidan en el TVF, entonces reduce en gran medida el tiempo de ejecución (sin llamadas que no hacen nada), así como la cantidad de filas que obtiene. Pero sus dos declaraciones no son equivalentes a la segunda devuelve muchas más filas que la primera. En cuanto a la corrección, eso depende de los requisitos de su negocio.
Jonathan Fite
Sí estoy de acuerdo. Sin embargo, escribí el código a la luz de la pregunta inicial en la que supuse que había una o varias relaciones entre las tablas. Y, en segundo lugar, me hicieron una pregunta en una entrevista. Gracias
user3104116