SQL LEFT JOIN with conditional CASE statements - sql

Hopefully this is a quickie
SELECT *
FROM T
left JOIN J ON
CASE
WHEN condition1 THEN 1 --prefer this option even if CASE2 has a value
WHEN condition2 THEN 2
ELSE 0
END = 1 (edit: but if 1 does not satisfy, then join on 2)
Both cases return results, but I want THEN 1 to supersede THEN 2 and be the lookup priority
Can I have SQL do something like join on max(CASE)?
Basically I am trying to duplicate a nested INDEX/MATCH from Excel
edit: what i am hearing is that the Case should stop at the first returned TRUE, but it doesn't behave like that when i test
SELECT *
FROM T
left JOIN J ON
CASE
WHEN condition1 THEN 1 --prefer this option even if CASE2 has a value
WHEN condition2 THEN 1
ELSE 0
END = 1
it seems to prefer the 2nd THEN 1 sometimes, but not always... is there anything i am missing that would cause this behavior?

It doesn't matter which of the conditions causes the rows to match in a join. There are legitimate reasons to use a case expression in a join but I think you just want to or your conditions and then use the case expression to output a ranked reason for the match.
SELECT *, CASE WHEN <condition1> THEN 1 WHEN <condition2> THEN 2 END as match_code
FROM T LEFT OUTER JOIN J ON <condition1> or <condition2>
I don't know what to picture regarding the "nested INDEX/MATCH" from Excel. If I'm on the wrong track above then perhaps you're looking for a nested case expression?
Now if your conditions will have matches across different rows and you only want to keep one then...
WITH matches AS (
SELECT *, CASE WHEN <condition1> THEN 1 WHEN <condition2> THEN 2 END AS match_code
FROM T LEFT OUTER JOIN J ON <condition1> OR <condition2>
), ranked as (
SELECT *, MIN(match_code) OVER (PARTITION BY ???) AS keeper
FROM matches
)
SELECT ...
FROM ranked
WHERE match_code = keeper

Well, you can always have several conditions in your CASE Statements:
SELECT *
FROM T
left JOIN J ON
CASE
WHEN condition1 THEN 1 --prefer this option even if CASE2 has a value
WHEN condition2 And !condition1 THEN 2
ELSE 0
END = 1
--UPDATED--
If both of your conditions are required to match, but condition1 is optional then you can try this statement too:
SELECT *
FROM T
left JOIN J ON
CASE
WHEN condition1 And condition2 THEN 1 --Both conditions match
WHEN condition2 THEN 2 -- condition1 has no match
ELSE 0
END = 1

You can use With statement to this in 2 steps:
With first_join as
(SELECT *
FROM T
left JOIN J ON condition1)
select * from first_join
join J On case when Name_of_2nd_condition is null
then condition2
ELSE null end

You can use the CROSS APPLY /OUTER APPLY operator : https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/
SELECT *
FROM T
OUTER APPLY (SELECT TOP 1 *
FROM J
WHERE condition1 OR condition2
ORDER BY order) J

Related

SQL Syntax for CASE command with multiple WHEN value

TL/DR
Is it possible to use "IN" syntax after "WHEN" if the condition is at CASE level ?
My scenario :
I am writing a SQL CASE statement with multiple WHEN value validation.
The CASE condition is complex (and long) so i don't want to repeat it at WHEN level.
This works :
CASE
WHEN ( SELECT VALUE FROM Tab1 INNER JOIN Tab2 ON Tab1 ....very long statement) IN ('A','B','C') THEN 1
WHEN ( same very long statement as above) IN ('D','E','F') THEN 2
WHEN ( same very long statement as above) IN ... etc
END
I would like to make it more readable as this, but syntax below fails
CASE ( SELECT VALUE FROM Tab1 INNER JOIN Tab2 ON Tab1 ....very long statement)
WHEN IN ('A','B','C') THEN 1 -- fails syntax error
WHEN 'D' OR 'E' OR 'F' THEN 2 -- also fails syntax error
END
Of course i am trying to avoid listing all values with same outcome in different when
Syntax below works but very long list of values
CASE ( SELECT VALUE FROM Tab1 INNER JOIN Tab2 ON Tab1 ....very long statement)
WHEN 'A' THEN 1
WHEN 'B' THEN 1
WHEN 'C' THEN 1
WHEN 'D' THEN 2
WHEN 'E' THEN 2
WHEN 'F' THEN 2
....
END
What can SQL do for me there ?
Formulate the long query as you did, and CROSS JOIN the main query with it:
SELECT
base_query.other
, base_query.columns
, base_query.otherquery
, CASE
WHEN xcross.result IN ('A','B','C') THEN 1
WHEN xcross.result IN ('D','E','F') THEN 2
WHEN xcross.result IN ('G','H','I') THEN 3
ELSE NULL
END
FROM other_table ot
JOIN yet_other_table you on ot.join_col = yot.join_col
CROSS JOIN (
SELECT val AS result FROM Tab1 INNER JOIN Tab2 ON Tab1 ....very long statement
) AS xcross

IN/EXISTS predicate sub-queries can only be used in Filter/Join and a few commands

I am trying to write a query in azure databricks and I am getting the following error
"IN/EXISTS predicate sub-queries can only be used in Filter/Join and a few commands"
This is the code I am using.
SELECT id,
(CASE WHEN id in (SELECT id from aTable) THEN 1 ELSE 0 END) as a,
(CASE WHEN id in (SELECT id from bTable) THEN 1 ELSE 0 END) as b,
(CASE WHEN id in (SELECT id from cTable) THEN 1 ELSE 0 END) as c
FROM table
I read that sql doesn't let you do this because the case statements are evaluated row by row, and it wants to prevent you from doing a SELECT statement for each row evaluation. If that is the case, is there an alternative or workaround to accomplish this? Thanks
Databricks does not support subqueries using IN or EXISTS in CASE statements. As an alternative, consider outer joining each view to master table:
Query could be like then structure below:
select .....
case when a.id is not null then a
when b.id is not null then b
end as id
from Table_t t LEFT JOIN (select id from aTable ) a ON t.id=a.id LEFT JOIN(
select id from bTable) b ON t.id=b.id
..................
I tried to reproduce similar scenario and got same error:
Regardless of whether it is contained in a CASE WHEN, the IN operator utilising a subquery only functions in filters, not projections. If you explicitly supply values in the IN clause as opposed to using a subquery, it works just great.
To work around this, I tried left join to tables and then check for a null in the case statement.
This query might work
%sql
SELECT t.Id,
(CASE WHEN at.Id is not null THEN 1 ELSE 0 END) as a,
(CASE WHEN bt.Id is not null THEN 1 ELSE 0 END) as b,
(CASE WHEN ct.Id is not null THEN 1 ELSE 0 END) as c
FROM table t
LEFT JOIN aTable at ON t.Id = at.Id
LEFT JOIN bTable bt ON t.Id = bt.Id
LEFT JOIN cTable ct ON t.Id = ct.Id
Sample data:
Output:

PROC SQL - Case when with multiple tables

I need to create a dataset (TABLE3) in order to check if some variables from other two tables are equals. If so, the code must return 0, otherwise 1. But, as I'm a new user of SAS and SQL I'm struggling to figure out how to do that.
I'm trying something like that but it's not working.
PROC SQL;
CREATE TABLE TABLE3 AS
SELECT A.*, B.*
CASE WHEN B.VARIABLE1 = A.VARIABLE2 THEN 0 ELSE 1 END AS VARIABLE_1_2,
CASE WHEN B.VARIABLE3 = A.VARIABLE4 THEN 0 ELSE 1 END AS VARIABLE_3_4
FROM TABLE1 AS A
LEFT JOIN TABLE2 AS B;
P.S.: Variables 1, 2, 3 and 4 are all character variables.
In addition to the tables relation you must add after the join, You miss a coma "," just before the first case.
For a JOIN condition to work, there must a ON clause
PROC SQL;
CREATE TABLE TABLE3 AS
SELECT A.*, B.*,
CASE WHEN B.VARIABLE1 = A.VARIABLE2 THEN 0 ELSE 1 END AS VARIABLE_1_2,
CASE WHEN B.VARIABLE3 = A.VARIABLE4 THEN 0 ELSE 1 END AS VARIABLE_3_4
FROM TABLE1 AS A LEFT JOIN TABLE2 AS B
**ON TABLE1.SOME_COLUMN_NAME = TABLE2.SOME_COLUMN_NAME**
;

Can I do case when the last 3 string equal aaa then do...?

I have a string of data and I'd like to see if I can do a case when statement if it endings in certain texts then return value from different temporary table. Is it possible to do this?
i.e. case when prodwarehouse ending in '001' then pull the on hand qty from table #a else 0 end.
case when prodwarehouse ending in '009' then pull from on hand qty from table #b else 0 end.
Table #a and #b has those values : productwarehouse - on hand qty
Don't know much about how many tables are involved or the relationsships but you can use case statements with a sub select.
select
case when prodwarehouse like '%001' then (select onHandQty from tableA a where yt.id = a.id)
when prodwarehouse like '%009' then (select onHandQty from tableB b where yt.id = b.id)
else 0
end as 'Qty'
from yourTable yt
OR you could just join the tables so that you wouldn't need to do a sub select.
select
case when a.prodwarehouse like '%001' then b.qty
when a.prodwarehouse like '%009' then c.qty
else 0
end as 'Qty'
from tablea a
join table b on...
join table c on...

SQL Conditional filter with different values

I have found lots of posts on coniditonal filtering in the where clause, but they all seem to be based off of using the same value, such as:
WHERE (o.OrderID = #orderid OR #orderid IS NULL)
I need to do something slightly different, I need to remove a filter and its value completely base on another value, so something like:
select *
from tableA
where 1 = 1
case when a = 1 then
and b in (select b from tableB)
else
-- do nothing
end
I know that the above is not allowed, and I am just writing as an example of what I am trying to do. does Anyone have any idea of a good way to do this? I know i could use if statements and duplicate the query, but it is a large one, and i am trying to avoid that.
Thanks
SELECT *
FROM tableA
WHERE a <> 1
OR (a = 1 AND EXISTS(SELECT b from TableB WHERE tableA.b = TableB.b))
You could also write this as:
SELECT tableA.*
FROM tableA
LEFT JOIN tableB
ON tableA.b = tableB.b
WHERE tableA.a <> 1
OR (tableA.a = 1 AND tableB.b IS NOT NULL)
"Correcting" your WHERE clause:
select *
from tableA
where 'T' = case
when a = 1 then case
when b in (select b from tableB) then 'T'
end
else 'T'
end;