Reputation: 647
Not sure if this can be done, but here is what I am trying to do.
I have two tables: Table 1 is called Task and it contains all of the possible Task Names Table 2 is called Task_subset and it contains only a subset of the Task Names included in Table 1
I have a variable called @TaskControl, that is passed in as a parameter, it either is equal to Table1 or Table2
Based on the value of the @TaskControl variable I want to join one of my Task Tables
For example:
If @TaskControl = 'Table1':
Select * From Orders O Join Task T on T.id = O.id
If @TaskControl = 'Table2):
Select * From Orders O Join Task_subset T on T.id = O.id
How would I do this, Sql Server 08
Upvotes: 1
Views: 3970
Reputation: 74345
Try the following. It should avoid the stored procedure plan getting bound based on the value of the parameter passed during the first execution of the stored procedure (See SQL Server Parameter Sniffing for details):
create proc dbo.foo
@TaskControl varchar(32)
as
declare @selection varchar(32)
set @selection = @TaskControl
select *
from dbo.Orders t
join dbo.Task t1 on t1.id = t.id
where @selection = 'Table1'
UNION ALL
select *
from dbo.Orders t
join dbo.Task_subset t1 on t1.id = t.id
where @selection = 'Table2'
return 0
go
The stored procedure shouldn't get recompiled for each invocation, either, as @Martin suggested might happen, but the parameter value 1st passed in should not influence the execution plan the gets bound. But if performance is an issue, run a sql trace with the profiler and see if the cached execution plan is reused or if a recompile is triggered.
One thing, though: you will need to ensure, though, that each individual select
in the UNION
returns the exact same columns. Each select
in a UNION
must have the same number of columns and each column must have a common type (or default conversion to the common type). The 1st select
defines the number, types and names of the columns in the result set.
Upvotes: 0
Reputation: 453786
If they are UNION
compatible you could give this a shot. From a quick test this end it only appears to access the relevant table.
I do agree more with JNK's and Remus's answers however. This does have a recompilation cost for every invocation and not much benefit.
;WITH T AS
(
SELECT 'Table1' AS TaskControl, id
FROM Task
UNION ALL
SELECT 'Table2' AS TaskControl, id
FROM Task_subset
)
SELECT *
FROM T
JOIN Orders O on T.id = O.id
WHERE TaskControl = @TaskControl
OPTION (RECOMPILE)
Upvotes: 1
Reputation: 294407
Doing it exactly as you do it right now is the best way. Having one single statement that attempts to somehow dynamically join one of two statements is the last thing you want. T-SQL is a language for data access, not for DRY code-reuse programming. If you attempt to have a single statement then the optimizer has to come up with a plan that always work, no matter the value of @TaskControl
, and so the plan will always have to join both tables.
A more lengthy discussion on this topic is Dynamic Search Conditions in T-SQL (your dynamic join falls into the same topic as dynamic search).
Upvotes: 2
Reputation: 65197
Don't overcomplicate it. Put it into a stored proc like so:
CREATE PROCEDURE dbo.MyProcedure(@TaskControl varchar(20))
AS
If @TaskControl = 'Table1'
Select * From Orders O Join Task T on T.id = O.id
ELSE If @TaskControl = 'Table2'
Select * From Orders O Join Task_subset T on T.id = O.id
ELSE SELECT 'Invalid Parameter'
Or just straight TSQL with no proc:
If @TaskControl = 'Table1'
Select * From Orders O Join Task T on T.id = O.id
ELSE If @TaskControl = 'Table2'
Select * From Orders O Join Task_subset T on T.id = O.id
Upvotes: 3
Reputation: 47402
I don't know how good performance would be, and this would not scale well as you add on additional optional tables, but this should work in the situation that you present.
SELECT
O.some_column,
COALESCE(T.some_task_column, TS.some_task_subset_column)
FROM
Orders O
LEFT OUTER JOIN Tasks T ON
@task_control = 'Tasks' AND
T.id = O.id
LEFT OUTER JOIN Task_Subsets TS ON
@task_control = 'Task Subsets' AND
TS.id = O.id
Upvotes: 0