JOIN ON clause with CASE statement depending on if field is NULL? - sql

I'm trying to do a simple LEFT JOIN with tables with 2 IDs - basically an ID and Sub-ID. Each row has an ID, but not necessarily a Sub-ID. When a Sub-ID exists, I want to join based on that, if not join on the ID. I'd imagine something like
SELECT ...
FROM tablename a
LEFT JOIN tablename b
ON CASE WHEN SUB_ID IS NOT NULL THEN
a.SUB_ID = b.SUB_ID
ELSE
a.ID = b.ID END
AND
a.otherfield = b.otherfield
But I couldn't get anything like this to work, so instead I had to do 2 queries with a UNION (one that joined on SUB_ID WHERE SUB_ID IS NOT NULL and another that joined on ID WHERE SUB_ID IS NULL.) It worked but I can't imagine there isn't a way to do it. If it helps, my ID and SUB_ID values look like this:
ID SUB_ID
10000 NULL
10001 NULL
10001 10001-3
10001 10001-5
10014 NULL
Any suggestions on how to achieve this without doing a UNION? Thanks in advance!!

We can use COALESCE for this purpose:
SELECT ...
FROM tablename a
LEFT JOIN tablename b
ON COALESCE(a.SUB_ID,a.ID) = COALESCE(b.SUB_ID,b.ID)
COALESCE returns value of first not null parameter from left.
Here is the code at SQL Fiddle

This should work for you.
SELECT ...
FROM tablename a
LEFT JOIN tablename b
ON ((b.SUB_ID IS NOT NULL AND a.SUB_ID = b.SUB_ID) OR
(a.ID = b.ID))
AND a.otherfield = b.otherfield

Interesting.
SELECT ...
FROM tablename a
LEFT JOIN tablename b
ON (
a.SUB_ID = b.SUB_ID
OR (a.SUB_ID IS NULL AND b.SUB_ID IS NULL AND a.ID = b.ID)
)
AND a.otherfield = b.otherfield
That might well work. It's NOT going to be fast, though.
Depending on the state of your data and what you want to achieve, you might want to change the join clause to
ON (
a.SUB_ID = b.SUB_ID
OR (a.SUB_ID IS NULL AND a.ID = b.ID)
OR (b.SUB_ID IS NULL AND a.ID = b.ID)
)
AND a.otherfield = b.otherfield
... instead.

You could JOIN the table twice:
SELECT ...
FROM tablename a
LEFT JOIN tablename b
ON a.SUB_ID = b.SUB_ID
AND a.otherfield = b.otherfield
LEFT JOIN tablename c
ON a.ID = b.ID
AND a.SUB_ID IS NULL
AND a.otherfield = c.otherfield
Then use ISNULL to get the columns, e.g.
ColumnName = ISNULL(b.ColumnName, c.ColumnName)
It depends on your indexes, but I suspect this may get optimised better than having a conditional join clause.

Try something like this:
SELECT *
FROM TABLENAME A
LEFT JOIN TABLENAME B
ON (A.SUB_ID IS NOT NULL AND A.SUB_ID = B.SUB_ID)
OR (A.SUB_ID IS NULL AND A.ID = B.ID)

Related

Cancel join condition based on column value

I am stuck into a situation where I need to perform conditional join. The brief summary could be, I have 2 tables TableA and TableB.
TableA has columns A1,A2,A3,A4,A5,Condition1,Condition2
similarly TableB has columns B1,B2,Condition1,Condition2
And I need to perform join on TableA.Condition1 = TableB.Condition1 and condition join on TableA.Condition2 = TableB.Condition2, condition being TableA.Condition2 should not be null for any record first join.
In other words, if I some any record with TableA.Condition1 = TableB.Condition1 matched and at the same time TableA.Condition2 is not null for any of them then perform second join, otherwise don't perform second join.
Query could be like
SELECT A.* FROM TableA A
INNER JOIN TableB B
ON A.Condition1 = B.Condition1 -- This must be perform
AND WHEN A.Condition2 IS NULL THEN
1 = 1 -- Assuming no join here
ELSE
A.Condition2 = B.Condition2 -- perform join
END
You are only selecting from TableA, so how about using exists instead?
SELECT A.*
FROM TableA A
WHERE (A.Condition2 IS NULL AND
EXISTS (SELECT 1 FROM TableB b WHERE A.Condition1 = B.Condition1)
) AND
(A.Condition2 IS NOT NULL AND
EXISTS (SELECT 1 FROM TableB b WHERE A.Condition1 = B.Condition1 AND A.Condition2 = B.Condition2)
);
Or, if you want a JOIN:
SELECT A.*
FROM TableA A JOIN
TableB B
ON A.Condition1 = B.Condition1 AND
(A.Condition2 IS NOT NULL OR A.Condition2 = B.Condition2);
Try this may help you
SELECT A.*
FROM TableA A
INNER JOIN TableB B ON A.Condition1 = B.Condition1 AND
((A.Condition2 IS NOT NULL AND A.Condition2 = B.Condition2)
OR (A.Condition2 IS NULL) )
hard to understand your question and even harder to understand the purpose
SELECT A.*
-- join table1 and table2 on Condition1
FROM TableA A
JOIN TableB B ON A.Condition1 = B.Condition1
-- if max condition2 is null then there is nothing but nulls
JOIN ( SELECT MAX(Condition2) Condition2 FROM TableA A2 ) A2
-- in that case every row resulting from join1 goes
ON A2.Condition2 IS NULL
-- otherwise use condition2 but replace nulls with some placeholder
-- or maybe you have either all null or no nulls
OR COALESCE (A.Condition2,'null') = COALESCE (B.Condition2,'null')
;

Query is not returning any rows with like '%' operator and left outer join, if right table has no rows

I am using MS SQL SERVER 2008R2. i have two tables A and B as
create table A(
id int primary key, name Varchar(20));
create table B(
id int primary key, user_name Varchar(20));
insert into A values(1,'A1');
insert into A values(2,'A2');
insert into A values(3,'A3');
insert into A values(4,'A4');
insert into A values(5,'A5');
Now my problem is :
select A.*
from A left outer join B on A.id = B.id
where B.user_name like '%';
or
select A.*
from A left outer join B on A.id = B.id
where B.user_name like '%%';
Above written query does not return any records even though left table have 5 entries in it. without any filter on right table it works fine.
select A.* from A left outer join B on A.id = B.id
this query will give you out put like this...
id name id user_name
1 A1 NULL NULL
2 A2 NULL NULL
3 A3 NULL NULL
4 A4 NULL NULL
5 A5 NULL NULL
and you are comparing username using like with null
select A.* from A left outer join B on A.id = B.id where B.user_name like '%%';
hence it will not give you any output
you should try following query
select A.*,b.* from A left outer join B on A.id = B.id where (b.user_name like '%%' or b.user_name is null)
In your scenario...first left join is happening it is finding 5 entries and then on that record set sql sever is applying filter of user_name and as user_name for all rows is null..no records are getting displayed.
you can change your query to
select A.* from A left outer join B on A.id = B.id where ISNULL(B.user_name,'') like '%%';
You are using wild card for comparing null values as well,
Use this,
SELECT a.* FROM a LEFT OUTER JOIN b ON a.id = b.id WHERE b.user_name LIKE '%' OR b.user_name IS NULL;
Since all values in table B are NULL, any wildcard match on NULL values will return NULL.
So the condition where B.user_name like '%'; translates into where NULL like '%'; which evaluates to NULL as NULL cannot be compared with any value.
select A.* from A left outer join B on
A.id = B.id where COALESCE(B.user_name,'') like '%%';
select A.* from A left outer join B on
A.id = B.id where COALESCE(B.user_name,'') like '%';
supporting sql fiddle : http://sqlfiddle.com/#!6/1ca91/8
Note that the COALESCE is ANSI, and therefore supported in Oracle, SQL Server and PostGres and does shortcut evaluation. n
Edit: Based on new information that this same query should work in all SQL Server, PostGres and Oracle. I am changing the SQL query to
use COALESCE instead which is supported in all
Unless you use ISNULL() and check like this where
ISNULL(B.user_name,'') like '%';
select A.* from A left outer join B on
A.id = B.id where ISNULL(B.user_name,'') like '%%';
select A.* from A left outer join B on
A.id = B.id where ISNULL(B.user_name,'') like '%';
See this fiddle
http://sqlfiddle.com/#!6/1ca91/6
Please try this one:
select A.* from A
left outer join (SELECT * FROM B where user_name like '%') X on A.id = X.id;

Join results in 3 tables to my existing query containing 2 tables

Hopefully someone can show me how to complete my sql. I'll start by showing you my existing query:
Select a.ID, a.NAME,
CASE When b.SOURCE = Value1 then 'Rename1'
When b.SOURCE = Value2 then 'Rename2'
When b.SOURCE = etc. ....
END,
b.TARGET, b.COMPARE
FROM Table'a', Table'b'
WHERE b.SOURCE in(Value1, Value2, Value...etc)
AND (a.ID = b.ID_1 or a.ID = b.ID_2)
Now my issue is my client wants to look up 3 different tables (each table contains primary key = a.ID) and return the values from only the column values that are referenced in the Case Statement above.
I assume I would need to do a join but not sure how to go about it. Any suggestions?
Thanks!
Puckhog
Following is the sample query:
Select T1.column,T2.Column,T3.Column
From Table1 T1
Join Table2 T2
on T1.MatchingColumn=T2.MatchingColumn
Join Table3 T3
on T2.MatchingColumn=T3.MatchingColumn
You could do something like this?
SELECT
a.ID, a.NAME, c.SOMETHING
FROM
a
JOIN
b
ON a.ID = b.ID
JOIN
c
ON a.ID = c.ID
WHERE [conditions]

Simulate a left join without using "left join"

I need to simulate the left join effect without using the "left join" key.
I have two tables, A and B, both with id and name columns. I would like to select all the dbids on both tables, where the name in A equals the name in B.
I use this to make a synchronization, so at the beginning B is empty (so I will have couples with id from A with a value and id from B is null). Later I will have a mix of couples with value - value and value - null.
Normally it would be:
SELECT A.id, B.id
FROM A left join B
ON A.name = B.name
The problem is that I can't use the left join and wanted to know if/how it is possible to do the same thing.
you can use this approach, but you must be sure that the inner select only returns one row.
SELECT A.id,
(select B.id from B where A.name = B.name) as B_ID
FROM A
Just reverse the tables and use a right join instead.
SELECT A.id,
B.id
FROM B
RIGHT JOIN A
ON A.name = B.name
I'm not familiar with java/jpa. Using pure SQL, here's one approach:
SELECT A.id AS A_id, B.id AS B_id
FROM A INNER JOIN B
ON A.name = B.name
UNION
SELECT id AS A_id, NULL AS B_id
FROM A
WHERE name NOT IN ( SELECT name FROM B );
In SQL Server, for example, You can use the *= operator to make a left join:
select A.id, B.id
from A, B
where A.name *= B.name
Other databases might have a slightly different syntax, if such an operator exists at all.
This is the old syntax, used before the join keyword was introduced. You should of course use the join keyword instead if possible. The old syntax might not even work in newer versions of the database.
I can only think of two ways that haven't been given so far. My last three ideas have already been given (boohoo) but I put them here for posterity. I DID think of them without cheating. :-p
Calculate whether B has a match, then provide an extra UNIONed row for the B set to supply the NULL when there is no match.
SELECT A.Id, A.Something, B.Id, B.Whatever, B.SomethingElse
FROM
(
SELECT
A.*,
CASE
WHEN EXISTS (SELECT * FROM B WHERE A.Id = B.Id) THEN 1
ELSE 0
END Which
FROM A
) A
INNER JOIN (
SELECT 1 Which, B.* FROM B
UNION ALL SELECT 0, B* FROM B WHERE 1 = 0
) B ON A.Which = B.Which
AND (
A.Which = 0
OR (
A.Which = 1
AND A.Id = b.Id
)
)
A slightly different take on that same query:
SELECT A.Id, B.Id
FROM
(
SELECT
A.*,
CASE
WHEN EXISTS (SELECT * FROM B WHERE A.Id = B.Id) THEN A.Id
ELSE -1 // a value that does not exist in B
END PseudoId
FROM A
) A
INNER JOIN (
SELECT B.Id PseudoId, B.Id FROM B
UNION ALL SELECT -1, NULL
) B ON A.Which = B.Which
AND A.PseudoId = B.PseudoId
Only for SQL Server specifically. I know, it's really a left join, but it doesn't SAY LEFT in there!
SELECT A.Id, B.Id
FROM
A
OUTER APPLY (
SELECT *
FROM B
WHERE A.Id = B.Id
) B
Get the inner join then UNION the outer join:
SELECT A.Id, B.Id
FROM
A
INNER JOIN B ON A.name = B.name
UNION ALL
SELECT A.Id, NULL
FROM A
WHERE NOT EXISTS (
SELECT *
FROM B
WHERE A.Id = B.Id
)
Use RIGHT JOIN. That's not a LEFT JOIN!
SELECT A.Id, B.Id
FROM
B
RIGHT JOIN A ON B.name = A.name
Just select the B value in a subquery expression (let's hope there's only one B per A). Multiple columns from B can be their own expressions (YUCKO!):
SELECT A.Id, (SELECT TOP 1 B.Id FROM B WHERE A.Id = B.Id) Bid
FROM A
Anyone using Oracle may need some FROM DUAL clauses in any SELECTs that have no FROM.
You could use subqueries, something like:
select a.id
, nvl((select b.id from b where b.name = a.name), "") as bId
from a
you can use oracle + operator for left join :-
SELECT A.id, B.id
FROM A , B
ON A.name = B.name (+)
Find link :-
Oracle "(+)" Operator
SELECT A.id, B.id
FROM A full outer join B
ON A.name = B.name
where A.name is not null
I'm not sure if you just can't use a LEFT JOIN or if you're restricted from using any JOINS at all. But as far as I understand your requirements, an INNER JOIN should work:
SELECT A.id, B.id
FROM A
INNER JOIN B ON A.name = B.name
Simulating left join using pure simple sql:
SELECT A.name
FROM A
where (select count(B.name) from B where A.id = B.id)<1;
In left join there are no lines in B referring A so 0 names in B will refer to the lines in A that dont have a match
+ or A.id = B.id in where clause to simulate the inner join

select row from table and substitute a field with one from another column if it exists

I'm trying construct a PostgreSQL query that does the following but so far my efforts have been in vain.
Problem:
There are two tables: A and B. I'd like to select all columns from table A (having columns: id, name, description) and substitute the "A.name" column with the value of the column "B.title" from table B (having columns: id, table_A_id title, langcode) where B.table_A_id is 5 and B.langcode is "nl" (if there are any rows).
My attempts:
SELECT A.name,
case when exists(select title from B where table_A_id = 5 and langcode= 'nl')
then B.title
else A.name
END
FROM A, B
WHERE A.id = 5 and B.table_A_id = 5 and B.langcode = 'nl'
-- second try:
SELECT COALESCE(B.title, A.name) as name
from A, B
where A.id = 5 and B.table_A_id = 5 and exists(select title from B where table_A_id = 5 and langcode= 'nl')
I've tried using a CASE and COALESCE() but failed due to my inexperience with both concepts.
Thanks in advance.
araqnid's is the answer you are looking for, I bet.
But if you want to enforce that no more than one row is returned for each original matching A row, you might prefer to do a subselect instead of a LEFT JOIN. For example:
SELECT A.id, COALESCE(
( SELECT max(B.title) FROM B WHERE
langcode = 'nl' AND B.table_a_id = A.id), A.name ) as name
FROM A
WHERE A.id = 5
I use "max" here to select an arbitrary value, in the event there is more than one. You can use "min" or whatever you consider appropiate in your case.
Perhaps this is more easy to understand than the LEFT JOIN, but (apart from the two being not exactly equivalent) a JOIN will perform better than N subselects (much better is N is large).
Anyway, from a learning point of view, it's good to understand both.
select A.id, coalesce(B.title, A.name)
from TableA AS A
left join (select table_a_id, title from TableB where langcode = 'nl') AS B
on B.table_a_id = A.id
WHERE A.id = 5
Ok, I'm not sure how your tables have to be joined, but something like this should do the job:
SELECT yourcolumnlist,
CASE WHEN A.name IS NULL THEN B.title ELSE A.name END
FROM TableA AS A
INNER JOIN TableB AS B
ON A.id = B.table_A_id
WHERE B.table_A_id = 5
AND B.langcode = 'nl'
Another way to do it would be to use the COALESCE() function:
SELECT yourcolumnlist,
COALESCE(A.name, B.title)
FROM TableA AS A
INNER JOIN TableB AS B
ON A.id = B.table_A_id
WHERE B.table_A_id = 5
AND B.langcode = 'nl'
Try this
select A.id,B.title,A.description from TableA as A inner join tableB as B
on A.id=B.id where B.table_A_id = 5 and B.langcode ='nl'