2012-04-30 2 views
2

Рассмотрим простое соединение:T-SQL: Условно присоединения с помощью параметра

Select TableB.* 
From TableA 
Inner Join TableB 
    On TableB.ID = TableA.ID 

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

Select TableD.* 
From TableA 
Inner Join 
    [If @useTableC = 1 Then Join to TableC Else Join to TableB] As TableD 

Оба TableB и TableC имеют одинаковые столбцы.

Как создать такой вид соединения. Имейте в виду, что этот пример на самом деле представляет собой небольшую часть гораздо большего запроса, поэтому вы не можете просто использовать инструкции If ... Else.

Большое спасибо!

ответ

6

Обычно я предпочел бы переписать в более чем одном заявлении, но если вам это действительно нужно:

SELECT td.* 
FROM TableA ta 
JOIN (
    SELECT tc.* FROM TableC Where @useTableC = 1 
    UNION ALL 
    SELECT tb.* FROM TableB Where @useTableC = 0 
) td ON (/* JOIN CONDITION MISSING */) 
+0

Когда этот UNION выполнен и @useTableC = 1, SQL Server все еще проходит и обрабатывает ВСЕ записи в TableB? Если это так, это будет ужасная трата времени обработки. – AndroidDev

+1

Нет штрафа за IO, @AndroidDev: когда '@useTableC = 1', SQL Server не читает ни одной строки TableB. –

+0

Зачем ему не читать строки из TableB? Я не понимаю, как это возможно. Записи в TableB будут исключены только после их восстановления и THEN, где применяется предложение Where. – AndroidDev

1

К сожалению, T-SQL не имеет синтаксис, чтобы поддержать это. Я немного поработал с этим вопросом и не смог найти решение, пока не нашел, что эта статья помогла мне (используя соединения Left Outer), и это может вам помочь. Он расположен here.

+0

Ваше решение было бы здорово, если бы у меня было несколько условных таблиц. Но я мог бы иметь десятки таблиц, поэтому использование соединений Left Outer было бы серьезной проблемой производительности. – AndroidDev

0

Ответы Гиррари Лимы превосходны, но в качестве альтернативы вы можете создать свой запрос с помощью динамического SQL. Это позволит вам использовать ряд утверждений if для добавления или отсутствия таких объединений, как вы хотите, используя столько операторов if, сколько необходимо. В качестве грубой и шероховатой, например:

declare @sql varchar(8000) 

set @sql = 'Select TableD.* From TableA ' 

if @usertableC = 1 
then select @sql = @sql + ' innner join tableC' 

if @usertableC != 1 
then select @sql = @sql + ' innner join tableB 

exec @sql 

Существует хорошее обсуждение динамического SQL в "the curse and blessing of dynamic sql"

+2

Динамический sql часто лучше работает с планами запросов, чем описанный выше подход объединения, который может хранить план «A» при первом запуске proc и всегда использовать план «A», даже если он должен использовать «B» –

+0

Привет, @ BrianWhite, вы получили здесь действительную точку, но повторного использования неэффективного плана запроса можно избежать, используя подсказку запроса 'RECOMPILE' или параметр« WITH RECOMPILE »хранимой процедуры. (http://technet.microsoft.com/en-us/library/ms181714.aspx) –

+0

@GerardoLima Вы совершенно правы, но оптимизатор пытается повторно использовать планы запросов по какой-либо причине. Я бы предложил только включить подсказку запроса, если у вас действительно есть основания подозревать, что план кэширования будет неэффективным (редко, но это происходит) или для определенных целей тестирования производительности. Я не хотел бы так легко использовать подсказки. – TimothyAWiseman

0

Nice решение, Геральдо! Эффект не влияет на использование объединения. Я не могу сказать вам, почему (возможно, кто-то может заполнить пробелы здесь), но план выполнения показывает 0 затрат ввода-вывода для постоянного сканирования.

Однако это зависит от того, как схемы для TableB и TableC идентичны. Для того, чтобы сделать это с помощью нескольких операторов, поддерживая разнородные схемы в TableB и TableC, я бы рассмотреть следующий подход:

DECLARE @sql NVARCHAR(MAX) 
SET @sql = 'SELECT td.* FROM TableA ta INNER JOIN Table' + 
    CASE WHEN @useTableC = 1 THEN 'C' ELSE 'B' END + ' td ON ta.ID = td.ID' 
EXEC sp_executesql @sql 

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

+0

Привет, @Peter, вы подняли действительную точку здесь: решение, которое я предложил, неверно, если таблица схемы несовместимы. Решение, тем не менее, может быть проще: только выбранные столбцы должны быть совместимы, следовательно, любая несовместимость между «TableC» и «TableB» может быть решена в предложении SELECT. –

Смежные вопросы