Как объединить таблицу с табличной функцией?

53

У меня есть пользовательская функция:

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

Теперь я хочу присоединиться к этому на другом столе, вот так:

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

Другими словами, для всех Fooзаписей, где SomeCriterion1, я хочу видеть Foo IDи Desc, а также значения Field1и Field2, которые возвращаются ut_FooFuncдля ввода Foo.ID.

Какой синтаксис для этого?

Шауль Бер
источник

Ответы:

84

Вам CROSS APPLYне нужно присоединяться.

Определение табличных выражений, участвующих в соединениях, должно быть стабильным. Т.е. они не могут быть соотнесены так, что табличное выражение означает что-то другое, зависящее от значения строки в другой таблице.

select f.ID, f.Desc, u.Field1, u.Field2
from Foo f 
Cross apply ut_FooFunc(f.ID, 1) u
where f.SomeCriterion = ...
Мартин Смит
источник
0

Я знаю, что ветка старая, мне задавали тот же вопрос, я сделал тест, результат следующий ...

Записи в FacCurrencyRate = 14264, в то время как TestFunction возвращает 105, если выполняется независимо.

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

Время выполнения ...

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

Если я использую предложенный ответ следующим образом ...

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

Время выполнения и количество результатов ...

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

Здесь я вижу, что внутренний запрос дает более правильный набор результатов, а время выполнения намного эффективнее. Поправь меня с лучшим подходом, чтобы добиться того же!

user3104116
источник
1
Перекрестное применение применяется к каждому внешнему ряду (поэтому строка за строкой), поэтому оно намного медленнее, особенно для больших наборов данных. Отменив его (поскольку вам нужны только результаты, имеющие совпадение в TVF, вы значительно сократите время выполнения (без вызовов, которые ничего не делают), а также количество возвращаемых строк. Но ваши два оператора не эквивалентны второму возвращает гораздо больше строк, чем первое. Насколько правильность зависит от ваших бизнес-требований
Джонатан Файт
Да, я согласен. Тем не менее, я написал код в свете первоначального вопроса, в котором я предположил, что между таблицами существует отношение один ко многим. И, во-вторых, мне задали вопрос в интервью. Спасибо
user3104116