Determine table to join based on the condition - sql-server

I am not sure if I've missed to search properly, but I couldn't get the exact question as mine
This is something similar but not exact
https://stackoverflow.com/questions/11533636/determining-which-table-to-join-to
Actually I want to decide with which table to join based on the parameter passed to stored procedure, case when didn't work
Kind of
select * from Table1
left join (case when #Parameter<>NULL Then Table2 else Table3 end) final
on Table1.Col1 = final.Col1
Table2 and Table3 has same structure

I can think of a few of options:
1: IF ... ELSE
IF #Parameter IS NULL
SELECT *
FROM T1
INNER JOIN T2 ON T1.ID = T2.ID
ELSE
SELECT *
FROM T1
INNER JOIN T3 ON T1.ID = T3.ID
2: Dynamic T-SQL
DECLARE #SQL NVARCHAR(MAX);
SELECT #SQL = N'SELECT *
FROM T1
INNER JOIN ' + CASE WHEN #Parameter IS NULL THEN N'T2 t' ELSE N'T3 t' END
+ N' ON T1.ID = t.ID';
EXEC sp_executesql #SQL;
3: UNION ALL and subquery.
SELECT *
FROM T1
INNER JOIN
(
SELECT *
FROM T2
WHERE #Parameter IS NULL
UNION ALL
SELECT *
FROM T3
WHERE #Parameter IS NOT NULL
) t ON T1.ID = t.ID
For this last one you'd have to check the plan the optimiser creates to see if it's OK performance wise.
It seems like you're looking for code reuse, so maybe option 2 is your best one. T-SQL doesn't really lend itself to this sort of polymorphism but you can use workarounds in some cases.
Taking a step back, one question to ask is, if the tables have the same structure, maybe you should just use one table? Then you could use #Parameter to just filter the rows you require instead of trying to create dynamic queries.
It's also worth noting that in situations like this you could handle the code-generation at the application layer with ORMs and the like.

Try this:
select *
from (
select data.*,
case when selection=1 then t1.ExtraField
when selection=2 then t2.ExtraField
when selection=3 then t3.ExtraField
else NULL
end as ExtraField
from data
left join t1 on t1.key = data.key and selection=1
left join t2 on t2.key = data.key and selection=2
left join t3 on t3.key = data.key and selection=3
) T
where ExtraField is not NULL

Done with this
SELECT PA.`module_id` as usertype, PA.module_pk_id, (
CASE PA.`module_id`
WHEN '0'
THEN pea.`name`
ELSE CONCAT(u.`first_name`, " ",u.`last_name`)
END
) as name
FROM `placements_approvers` PA
LEFT JOIN
placements_external_approvers pea
ON
PA.module_pk_id = pea.placement_external_approver_id AND PA.`module_id` = 0
LEFT JOIN
users u
ON
PA.module_pk_id = u.user_id AND PA.`module_id` != 0
WHERE PA.placement_id = '494' AND PA.approver_type = 1

Related

Is it possible to inner join a different table based on a sproc parameter?

I'm in charge of a legacy project and I was looking at simplifying or streamlining a bit.
What I want to do is something like:
declare #n int = 1
select * from table_a a
case when #n = 1 then inner join table_b b on b.x = a.x
else inner join table_c c a.x = c.x
end
The main reason I'm hoping this is possible is because I'm trying to get rid of a lot of repetition in the current sproc. Every time something changes, we have to make sure that each section is updated appropriately.
Currently it looks something like:
if #a = 1 begin
select (long list of columns but not all) from table_A
inner join table_2 a on table_A.a = table_2.a
left join table_B on table_A.c = table_B.c
where (several conditions here)
end
else if #= 2 begin
select (same long list of columns) from table_A
inner join table_3 a on table_A.a = table_3.a
left join table_B on table_A.c = table_B.c
where (same several conditions)
end
else
select (same list again) from table_A
inner join table_4 a on table_A.a = table_4.a
left join table_B on table_A.c = table_B.c
where (same conditions again)
end
In the example, tables 2, 3, and 4 are essentially identical as far as columns are concerned but are used to separate groups.
If it's not possible, I'm thinking I could combine all three of those tables into a single table and add a new column denoting the group and then inner join on that column along with the other one. I just figured that I'd rather not move data around if I didn't have to.
A variation of the following technique can be used, I'm using an outer join with null check.
declare #myVar int
set #myVar = 1
select
t1.id,
t1.nm,
coalesce(t2.nm, t3.nm) valu
from
table1 t1
left outer join
table2 t2 on (t1.id = t2.id and #myVar = 1)
left outer join
table3 t3 on (t1.id = t3.id and #myVar = 2)
where coalesce(t2.nm, t3.nm) is not null
The basic made-up schema in use:
select *
into table1
from
(select 1 id, 'a' nm union
select 2, 'b' union
select 3, 'c' union
select 7, 'j') a
select *
into table2
from
(select 1 id, 'a1' nm union
select 2, 'b1' union
select 3, 'c1' union
select 4, 'd1' ) a
select *
into table3
from
(select 1 id, 'a2' nm union
select 2, 'b2' union
select 3, 'c2' union
select 4, 'd2' union
select 5, 'e2' ) a
Does this work for you?
You can use an CROSS APPLY(...UNION ALL...) to effectively implement a conditional join to your middle table.
select (long list of columns but not all)
from table_A
cross apply (
select t2.c from table_2 t2 where t2.a = table_A.a and #a = 1
union all
select t3.c from table_3 t3 where t3.a = table_A.a and #a = 2
union all
select t4.c from table_4 t4 where t4.a = table_A.a and #a not in (1,2)
) X
left join table_B on X.c = table_B.c
where (several conditions here)
This should even run quite efficiently, assuming that you have indexes on table_2.a, table_3.a, table_4.a, and table_B.c.

SQL Server Update data if not exists from another table

I need to update values in a table from another table and if they don't exist it must be inserted. So I saw this example in here
UPDATE Table1 SET (...) WHERE Column1='SomeValue'
IF ##ROWCOUNT=0
INSERT INTO Table1 VALUES (...)
Now I would like to do this but instead of a where clause with a value i need to check if columns match on another table.
Something like
UPDATE t1
SET
t1.status = t2.status
FROM
table1 t1, table2 t2
WHERE t1.reference = t2.reference and t1.section = t2.section
I assume this is very bad in terms of performance and i would like to hear some other suggestions.
EDIT
Also please, if you can, i would like to hear why the approach you give is better than using a merge or a join for example. Much appreciated.
RESULT
Here is the approach i went for after checking how bad merge performance is compared to an update then insert and trying to test everyones approach this is what i decided for, basically uses a bit of everyone's answer
UPDATE C
SET
C.ConsumoWeek01 = M.ConsumoWeek01
FROM
Consumos AS C
INNER JOIN #tempTable M
ON C.Referencia = M.Referencia
AND C.UAP = M.UAP
INSERT INTO
Consumos
SELECT *
FROM #tempTable M
WHERE NOT EXISTS
(
SELECT 1 FROM Consumos C
WHERE C.Referencia = M.Referencia
AND C.UAP = M.UAP
)
Try this
UPDATE t1
SET
t1.status = t2.status
FROM
table1 t1
WHERE NOT EXISTS (SELECT 1 FROM table2 t2
WHERE t1.reference = t2.reference
and t1.section = t2.section )
use not exists
UPDATE t1
SET
t1.status = t2.status
FROM
table1 t1 where not exists( select 1 from table2 t2 where t1.=ref=t2.ref and t1.section=t2.secction)
Use merge statement:
MERGE INTO Table1 T1
USING
(
SELECT * FROM Table2
) T2
ON
(
T1.reference = T2.reference AND
T1.section = T2.section
)
WHEN MATCHED THEN
--UPDATE STATEMENT
WHEN NOT MATCHED THEN
--INSERT STATEMENT
Your method is fine just use standard explicit join syntax instead of old style with comma separate :
update t1
set . . .
from table t1 inner join
table t2
on t1.reference = t2.reference and t1.section = t2.section;

SQL: Isnull Function

I'm trying to update an table (se code below).
Where, when a match I want to extract the UserID, if there's no match I want to it to be given the value 6000.
However, the script doesn't do what I would expect it to do.
If U.UserID doesn't exist it doesn't write any value (6000), not if it's null.
How can I go around the problem?
/* Ensure Person (UserID)*/
UPDATE #List
SET #List.UserID = ISNULL(U.UserID, '6000')
FROM #List
INNER JOIN table1 t1 ON #List.ID = t1.ID
INNER JOIN User U ON U.FirstName + ' ' + U.Surname = t1.PersonFullname
According to me problem with joining the table with INNER JOIN i.e this will bring only matching records, Now if you want to updated all the records from the First table not only matching as well as other records where UserID is null then solution to this problem is Use of LEFT JOIN
UPDATE List
SET List.UserID = ISNULL(U.UserID, '6000')
FROM #iList List
LEFT JOIN table1 t1 ON List.ID = t1.ID
LEFT JOIN User U ON U.FirstName + ' ' + U.Surname = t1.PersonFullname

SQL - Apply only one set of joins at a time

I have three joins in my query. My requirement is to select records if either first two joins get satisfied or the third join gets satisfied
select * from security.requestview r
-- either below two joins satisfy
left join security.RequestDelegateView rd on r.id = rd.RequestId
join (select top 1 PersonnelNumber from SECURITY.MyRolesView) m on (m.PersonnelNumber=r.RequestorId or m.PersonnelNumber=r.InitiatorId or m.PersonnelNumber=rd.DelegatePersonnelNumber)
-- or this join
join (select substring(ltrim(DDSUCode),0,3) as Division from security.staffview where PersonnelNumber = (select top 1 PersonnelNumber from SECURITY.MyRolesView)) s
on r.OrganizationUnitRefId like s.Division+'%'
But ofcourse it will try to satisfy all the joins together.
Is there any way I can put some condition where it will select record if either first two joins satisfy or the last join alone satisfies?
Update
I tried putting them as where conditions but then the query is running forever
To answer this question:
Is there any way I can put some condition where it will select record
if either first two joins satisfy or the last join alone satisfies?
No, I know of no way to do this in a single query.
One way you can write this so that the third join only gets executed if the first one doesn't return records is in a multi-query series that populates a temp table or table variable:
INSERT INTO #tmp
SELECT (the first join);
IF (SELECT COUNT(*) FROM #tmp) = 0
INSERT INTO #tmp
SELECT (the second join);
SELECT * FROM #tmp;
WITH FirstJoin AS (
select * from security.requestview r
left join security.RequestDelegateView rd on r.id = rd.RequestId
join (select top 1 PersonnelNumber from SECURITY.MyRolesView) m on (m.PersonnelNumber=r.RequestorId or m.PersonnelNumber=r.InitiatorId or m.PersonnelNumber=rd.DelegatePersonnelNumber)
), SecondJoin AS (
select * from security.requestview r
join (select substring(ltrim(DDSUCode),0,3) as Division from security.staffview where PersonnelNumber = (select top 1 PersonnelNumber from SECURITY.MyRolesView)) s
on r.OrganizationUnitRefId like s.Division+'%'
), BothJoins AS (
SELECT * FROM FirstJoin
UNION ALL
SELECT * FROM SecondJoin
)
SELECT DISTINCT * FROM BothJoins
To get this to work you need to Change the "Select *" inside the CTE's so that every returned column is named. (Since I don't know whats in your tables I couldn't do it).
Please note that FirstJoin and SecondJoin needs to return the same columns.
select *
from table1
left join table2
on table1.id = table2.t1
left join table3
on table1.id = table3.t1
left join table4
on table1.id = table4.t1
where table2.t1 is not null
or table3.t1 is not null
or table4.t1 is not null

SQL - Using a join on a column with multiple values in cells

I have two tables I am trying to join. One table has a column with IDs in it, and I am trying to do a left join to a different table that has the same IDs in it, although the second table could contain more than one ID per cell. For example, if my first table has an ID value of 123, and the second table has an ID value of 123;724;823, is there any way to get it to join the two rows?
You tried in query designer? Is very easy to make joins there.
SELECT column_names
FROM table-name1 LEFT JOIN table-name2
ON ID_column-name1 = ID_Column-name2
WHERE condition X,Y,Z
Hope will help you.
select *
from
(
select '123' as id
union select '124'
) as t1
left join
(
select '123;001;002' as id
union select '001;123;002'
union select '001;002;123'
) as t2 on
t2.id = t1.id
or t2.id like t1.id + ';%'
or t2.id like '%;' + t1.id + ';%'
or t2.id like '%;' + t1.id
Using the multiple like operators is probably the fastest way, but if you have a string splitter function like this one DelimitedSplit8K, you can split the values out into a table and join to it.
SELECT *
FROM table1 t1
LEFT JOIN (
SELECT *
FROM table2 t2
OUTER APPLY (
SELECT *
FROM dbo.[DelimitedSplit8K] (t2.id,';') -- splits the values in multi id column
) t
) t ON t.Item = t1.id -- t.Item is the value generated from the DelimitedSplit8K TVF

Resources