SQL query to use different tables conditionally - sql

I have a stored procedure which uses different tables for a join based on an input parameter. Currently I have to write the SQL query twice (with only the table name difference). Is it possible to combine them so I do not have to repeat SQL query logic twice?
Current code:
CREATE PROCEDURE SampleProc
#Condition BIT
AS
IF #Condition = 0
BEGIN
SELECT *
FROM TableA1 A /* Use TableA1 instead of TableA2 */
INNER JOIN TableB B ON A.Id = B.Id /* The rest of the query remains the same */
/* Inner Join some more complex join logic */
END
ELSE
BEGIN
SELECT *
FROM TableA2 A /* Use TableA2 instead of TableA1 */
INNER JOIN TableB B ON A.Id = B.Id /* The rest of the query remains the same */
/* Inner Join some more complex join logic */
END
END
One of the possible ways is to store TableA1 / TableA2 data to a temp table first and use the temp table to join inside a complex query. Is there any better way?

If the two tables have the same structure (as implied by the temp table comment), you can do:
select . . .
from ((select a.* from tablea1 a where #condition = 0
) union all
(select a.* from tablea2 a where #condition <> 0
)
) a inner join
b
Another alternative is dynamic SQL, but that can be tricky to maintain -- because the code looks like a string.
Sometimes, you can do this with a left join as well:
select b.*, coalesce(a1.col, a2.col) as col
from b left join
tablea1 a1
on a1.id = b.id and #condition = 0 left join
tablea2 a2
on a2.id = b.id and #condition <> 0
where a1.id is not null or a2.id is not null
. . .
Although this should have good performance, it has the downside that all references to a columns need to use coalesce().

if TableA1 and TableA2 have same columns, try this
SELECT
*
From
( select
*
from
TableA1
where
#Condition = 0
union all
select
*
from
TableA2
where
#Condition != 0) as A
INNER JOIN
TableB B
On
A.Id =B.Id

Related

SQL conditional joins

I have a table-valued function with joins where I want to choose which join I use depending on a local variable like:
DECLARE #type int;
Then do some logic with #type and set it to 1.
SELECT ...
FROM table t
inner join ... a on a.id = t.id and #type = 1 -- Only trigger this join if #type is 1
inner join ... b on b.id = t.id and #type = 2 -- Only trigger this join if #type is 2
So my question is: how can I choose which join to trigger depending on the value of #type (if even possible).
The reason I want to do this is that the SELECT statement is massive, and I don't want repetitive code in the script.
Use left join instead:
SELECT ...
FROM table t LEFT JOIN
a
ON a.id = t.id AND #type = 1 LEFT JOIN
b
ON b.id = t.id AND #type = 2 ;
You might need WHERE #type IN (1, 2) if you want an empty result set for other values.
You will need COALESCE() in the SELECT to combine the columns:
COALESCE(a.col1, b.col1) as col1
This should be quite efficient. However, you might want to simply use UNION ALL:
SELECT ...
FROM table t JOIN
a
ON a.id = t.id
WHERE #type = 1
UNION ALL
SELECT ...
FROM table t JOIN
b
ON b.id = t.id
WHERE #type = 2 ;
You could union your two tables within a subquery. For any similar columns (i.e. would be in the same column in the outer select) you can place them above each other, for columns unique to each source you'd need to pad the other side of the union with NULL, e.g.
SELECT t.id,
a.SimilarCol,
a.UniqueToA,
a.UniqueToB
FROM Table AS t
INNER JOIN
( SELECT a.id,
a.SimilarCol, -- Column you would want to consider the same in each table
a.UniqueToA, -- Column Unique to this table
UniqueToB = NULL -- Column Unique to the other table
FROM SomeTable AS a
WHERE #Type = 1
UNION ALL
SELECT b.id,
b.SimilarCol,
UniqueToA = NULL,
b.UniqueToB
FROM SomeOtherTable AS b
WHERE #type = 2
) AS a
ON a.id = t.id;
Example on db<>Fiddle

Conditionally joining from multiple tables

Does SQL allow some form of conditional "table choosing" within the Join statement? ie. selecting a different table to join based on a predefined variable/condition.
define var = 1
select *
from tbl
join (case when &var=1 then tblA when &var=2 then tblB else tblC end) a on tbl.id = a.id
The error I get when attempting this method is ORA-00905: missing keyword.
No. Neither SQL nor Oracle allow this, unless you use dynamic SQL.
Assuming the tables have the same columns, you could write this logic as:
select *
from tbl join
(select a.* from tblA where &var = 1 union all
select b.* from tblB where &var = 2 union all
select c.* from tblC where &var not in (1, 2)
) abc
on tbl.id = abc.id;
You still need to specify all joins ahead of time and they would have to be left outer joins, but you can rewrite your statement like this. This way will work regardless of the number of fields in each of the tables (requirement for the union), and if they are named differently then you can access the appropriate field by name.
DECLARE #var int
SET #var=1
select tbl.*, tblA.ShippingName, tblB.BillingName, tblC.OtherName
from tbl
left outer join tblA on tbl.id = tblA.id and #var = 1
left outer join tblB on tbl.id = tblB.id and #var = 2
left outer join tblC on tbl.id = tblC.id and #var = 3

Inner join 2 tables but return all if 1 table empty

I have 2 tables say A and B, and I want to do a join on them.
Table A will always have records in it.
When table B has rows in it, I want the query to turn all the rows in which table A and table B matches. (i.e. behave like inner join)
However, if table B is empty, I'd like to return everything from table A.
Is this possible to do in 1 query?
Thanks.
Yes, for results like this, use LEFT JOIN.
Basically what INNER JOIN does is it only returns row where it has atleast one match on the other table. The LEFT JOIN, on the other hand, returns all records on the left hand side table whether it has not match on the other table.
To further gain more knowledge about joins, kindly visit the link below:
Visual Representation of SQL Joins
I came across the same question and, as it was never answered, I post a solution given to this problem somewhere else in case it helps someone in the future.
See the source.
select *
from TableA as a
left join TableB as b
on b.A_Id = a.A_Id
where
b.A_Id is not null or
not exists (select top 1 A_Id from TableB)
Here is another one, but you need to add one "null" row to table B if it's empty
-- In case B is empty
Insert into TableB (col1,col2) values (null,null)
select *
from TableA as a inner join TableB as b
on
b.A_Id = a.A_Id
or b.A_Id is null
I would use an if-else block to solve it like below:
if (select count(*) from tableB) > 0
begin
Select * from TableA a Inner Join TableB b on a.ID = b.A_ID
end
else
begin
Select * from TableA
end
Try This
SELECT t1.* FROM table1 AS t1 INNER JOIN table2 AS t2 ON t1.something = t2.someotherthing UNION SELECT * FROM table1 WHERE something = somethingelse;
This is solution:
CREATE TABLE MyData(Id INT, Something VARCHAR(10), OwnerId INT);
CREATE TABLE OwnerFilter(OwnerId INT);
SELECT *
FROM
(SELECT NULL AS Gr) AS Dummy
LEFT JOIN OwnerFilter F ON (1 = 1)
JOIN MyData D ON (F.OwnerId IS NULL OR D.OwnerId = F.OwnerId);
Link to sqlfiddle: http://sqlfiddle.com/#!6/0f9d9/7
I did the following:
DECLARE #TableB TABLE (id INT)
-- INSERT INTO #TableB
-- VALUES (some ids to filter by)
SELECT TOP 10 *
FROM [TableA] A
LEFT JOIN #TableB B
ON A.ID = B.id
WHERE B.id IS NOT NULL
OR iif(exists(SELECT *
FROM TableB), 1, 0) = 0
Now:
If TableB is empty (leave the commented lines commented) you'll get the top 10.
If TableB has some ids in it, you'll only join by those.
I do not know how efficient this is. Comments are welcome.
Maybe use a CTE
;WITH ctetable(
Select * from TableA
)
IF(EXISTS(SELECT 1 FROM TableB))
BEGIN
Select * from ctetable
Inner join TableB
END
ELSE
BEGIN
Select * from ctetable
END
or dynamic SQL
DECLARE #Query NVARCHAR(max);
SET #QUERY = 'Select * FROM TableA';
IF(EXISTS(SELECT 1 FROM TableB))
BEGIN
SET #QUERY = CONCAT(#QUERY,' INNER JOIN TableB');
END
EXEC sp_executesql #Query

Using Join based on condition

Can anyone please explain me how can we use join on the basis of condition.
Lets say i am filtering data on the basis of a condition now my concern is if a particular BIT type parameters value is 1 then the data set include one more join else return same as earlier.
Here is three tables A,B,C
now i want to make a proc which has the #bool bit parameter
if #bool=0
then
select A.* from A
inner join B on B.id=A.id
and if #bool=1
then
select A.* from A
INNER JOIN B on B.id=A.id
inner join C on C.id=A.id
Thanks In Advance.
What you have will work (certainly in a SPROC in MS SQL Server anyway) with minor mods.
if #bool=0 then
select A.* from A
inner join B on B.id=A.id
else if #bool=1 then -- Or just else if #boll is limited to [0,1]
select A.* from A
INNER JOIN B on B.id=A.id
inner join C on C.id=A.id
However, the caveat is that SQL parameter sniffing will cache a plan for the first path it goes down, which won't necessarily be optimal for other paths through your code.
Also, if you do take this 'multiple alternative query' approach to your procs, it is generally a good idea to ensure that the column names and types returned are identitical in all cases (Your query is fine because it is A.*).
Edit
Assuming that you are using SQL Server, an alternative is to use dynamic sql:
DECLARE #sql NVARCHAR(MAX)
SET #sql = N'select A.* from A
inner join B on B.id=A.id'
IF #bool = 1
SET #sql = #sql + N' inner join C on C.id=A.id'
sp_executesql #sql
If you need to add filters etc, have a look at this post: Add WHERE clauses to SQL dynamically / programmatically
select A.* from A
inner join B on B.id = A.id
left outer join C on C.id = A.id and #bool = 1
where (#bool = 1 and C.id is not null) or #bool = 0
The #bool = 1 "activates" the left outer join, so to speak, and turns it, in effect, into an inner join by applying it in the WHERE clause, too. If #bool = 0 then the left outer join returns nothing from C and removes the WHERE restriction.
Try the following query
SELECT A.*
FROM A
INNER JOIN B on B.id=A.id
INNER JOIN C on C.id=A.id and #bool=1
You do it using a union:
SELECT A.*
FROM A
INNER JOIN B on B.id=A.id
WHERE bool = 0
UNION ALL
SELECT A.*
FROM A
INNER JOIN B on B.id=A.id
INNER JOIN C on C.id=A.id
WHERE bool = 1
I'm assuming that bool is stored in table A or B.

Is it possible to use IF or CASE in sql FROM statement

I have a long stored procedure and I would like to make a slight modification to the procedure without having to create a new one(for maintenance purposes).
Is it possible to use a IF or CASE in the FROM statement of the select statement to join other tables?
Like this:
from tableA a
join tableB b a.indexed = c.indexed
IF #Param='Y'
BEGIN
join tableC c a.indexed = c.indexed
END
It didn't seem to work for me. But I am wondering if this is even possible and/or if this even makes sense to do.
Thanks.
No, it is not possible. You can only accomplish this through the use of dynamic SQL.
The Curse and Blessings of Dynamic SQL
An Intro to Dynamic SQL
I would not advise using Dynamic SQL, there are most likely better ways to perform this operation but you would have to provide more info.
You can achieve something like it if you have a left outer join
Consider
declare #param bit = 1
select a.*, b.*, c.* from a
inner join b on a.id = b.a_id
left outer join c on b.id = c.b_id and #param = 1
This will return all columns from a, b, c.
Now try with
declare #param bit = 0
This will return all columns from a and b, and nulls for columns of c.
It won't work if both joins are inner.
No this is not possible. Your best bet would probably be to select from both tables and only include the data your care about. If you provide an example of what you are trying to do I can provide a better answer.
Attempt at an example:
SELECT t1.id, COALESCE(t2.name, t3.name)
FROM Table1 as t1
LEFT JOIN Table2 as t2
ON t1.id = t2.id
LEFT JOIN Table2 as t3
ON t1.id = t3.id
While what you proposed is not possible, you can play with your where conditions:
from tableA a
inner join tableB b ON a.indexed = c.indexed
left join tableC c ON a.indexed = c.indexed AND 1 = CASE #Param WHEN 'Y' THEN 1 ELSE 0 END
More performant would be to just doing a big
IF #Param='Y' THEN
from tableA a
inner join tableB b ON a.indexed = c.indexed
ELSE
from tableA a
inner join tableB b ON a.indexed = c.indexed
left join tableC c ON a.indexed = c.indexed
You haven't revealed you SELECT clause. The essence of what you want is as follows:
SELECT indexed
FROM tableA
INTERSECT
SELECT indexed
FROM tableB
INTERSECT
SELECT indexed
FROM tableC
WHERE #Param = 'Y'
Then use this table expression as dictated by your SELECT clause e.g. say you only want to project tableA:
WITH T
AS
(
SELECT indexed
FROM tableA
INTERSECT
SELECT indexed
FROM tableB
INTERSECT
SELECT indexed
FROM tableC
WHERE #Param = 'Y'
)
SELECT *
FROM tableA
WHERE indexed IN ( SELECT indexed FROM T );