SQL Join - If it doesn't find any, try another parameters - sql

Let's suppose we have the following query:
SELECT *
FROM tableA a
LEFT JOIN tableB b ON b.number = a.number
AND b.name = a.name
AND b.place = a.place
If that LEFT JOIN doesn't find nothing, I need to change the ON parameters to something like:
SELECT *
FROM tableA a
LEFT JOIN tableB b ON b.number = a.number
AND b.person = a.person
That second LEFT JOIN only needs to run if the first one doesn't return nothing. How can I achieve that behaviour?
I have already tried with an OR statement, but doesn't work because the first LEFT JOIN always need to be checked first, and not at the same time that the second.

Perhaps the simplest method is union all:
SELECT a.*, b.*
FROM tableA a JOIN
tableB b
ON b.number = a.number AND
b.name = a.name AND
b.place = a.place
UNION ALL
SELECT a.*, b.*
FROM tableA a JOIN
tableB b
ON b.number = a.number AND
b.person = a.person
WHERE NOT EXISTS (SELECT 1
FROM tableB b
WHERE b.number = a.number AND
b.name = a.name AND
b.place = a.place
);

Related

Conditional join in SQL Server dependent on other table values

I need make a decision which table should be use in join statement depend on values in another table
I tried using CASE and COALESCE but can't achieve any success.
TableA has A and B and C and many other columns
TableB has ID and NAME columns
TableC has ID and NAME columns
My select statement is;
Select A.D, A.E, A.F From TableA A
If A.E = 1 then the following join should be used
left outer join TableB B ON A.B = B.ID
and B.NAME should be returned in the select statement
If A.E = 2 then the following join should be used
left outer join TableC C ON A.B = C.ID
and C.NAME should be returned in the select statement
Just add your conditions to the joins, and then use a case statement to pull the correct field to your result set e.g.
select A.D, A.E, A.F
, case when B.[Name] is not null then B.[Name] else C.[Name] end [Name]
from TableA A
left outer join TableB B ON A.B = B.ID and A.E = 1
left outer join TableC C ON A.B = C.ID and A.E = 2
Join tablea with the union of tableb with an extra column with value 1 and tablec with an extra column with value 2 and apply the conditions in the ON clause:
select
a.D, a.E, a.F, u.NAME
from tablea a
left join (
select *, 1 col from tableb
union all
select *, 2 col from tablec
) u on a.B = u.id and a.E = u.col

return pair of values instead of one value from two queries

I've got two queries that return single result.
They look something like this
// query 1
SELECT A.id FROM tableA A
INNER JOIN tableB B
ON B.id = A.id
WHERE b.status = 'ACTIVE'
// query 2
SELECT C.id FROM tableC C
WHERE c.status = 'ACTIVE'
How to combine them and make return the pair of values instead of one value from different queries? I mean to get something like [A.id, C.id]
Currently I have to use two queries in the applications and I want to combine them into one.
I think like this will do
SELECT (SELECT A.id FROM tableA A
INNER JOIN tableB B
ON B.id = A.id
WHERE b.status = 'ACTIVE'
) as 'query1',
(
SELECT C.id FROM tableC C
WHERE c.status = 'ACTIVE'
) as 'query2'
As your question is not clear, so i assume that you either needids from mentioned queries in one row or in different rows, you can use union all/union (provided that datatypes are compatible or implicitly convertible and duplicates or allowed or not) as below.
Combining Result in different rows.
SELECT A.id
FROM tableA A
INNER JOIN tableB B
ON B.id = A.id
WHERE b.status = 'ACTIVE'
union all
SELECT C.id
FROM tableC C
WHERE c.status = 'ACTIVE'
Combining Result in Single Row.
select max(id1), max(id2)
from(
SELECT A.id as id1, NULL as id2
FROM tableA A
INNER JOIN tableB B
ON B.id = A.id
WHERE b.status = 'ACTIVE'
union all
SELECT NULL, C.id
FROM tableC C
WHERE c.status = 'ACTIVE'
) t;
SAMPLE DEMO
You can run following query which work fine for me:
select t1.id as aid ,t2.id as cid
from (
SELECT A.id
FROM tableA A
INNER JOIN tableB B ON B.id = A.id
WHERE b.status = 'ACTIVE'
) t1
full outer join (
SELECT C.id
FROM tableC C
WHERE c.status = 'ACTIVE'
) t2 on t1.id=t2.id
You can join your second query with your first query as follows, so that you will get two (A.id, C.id) values in one query...
SELECT A.ID,C.ID FROM
(SELECT A.ID FROM table_A A INNER JOIN
table_B B ON A.ID=B.ID WHERE B.STATUS='A')A
INNER JOIN table_c C
ON C.ID=A.ID WHERE C.STATUS='A';

ACCESS SQL query: adding a new field?

I would like to add a field to a existing query that doesn't get affected from 'Where function'
For example,
This is the original code....
SELECT SHELL_Payables.PoolNum,
A.[Code], B.[Program] AS Program, A.PayableAmt, C.ReceivableAmt INTO [New Data]
FROM A INNER JOIN B ON A.ID=B.ID
INNER JOIN C ON A.Num=B.Num
WHERE (((A.AccountingPeriod)<=[AccountingYearMonth]));
I would like to add A.PayableAmt again but this time where clause (accountingperiod <= accountingyearMonth) should not be applied to this field...
Any ideas? It would be much appreciated.
To use union and select into, you would need to write your query something like this:
SELECT *
INTO [New Data]
FROM (
SELECT PoolNum
,A.[Code]
,B.[Program] AS Program
,A.PayableAmt
,C.ReceivableAmt
FROM A
INNER JOIN B ON A.ID = B.ID
INNER JOIN C ON A.Num = B.Num
WHERE A.AccountingPeriod <= AccountingYearMonth
UNION
SELECT PoolNum
,A.[Code]
,B.[Program] AS Program
,A.PayableAmt
,C.ReceivableAmt
FROM A
INNER JOIN B ON A.ID = B.ID
INNER JOIN C ON A.Num = B.Num
)
UPDATE
If you want to add another PayableAmt column to the same row, maybe you can join back to the table A something like this:
SELECT t.PoolNum
,a.[Code]
,a.[Program] AS Program
,t.PayableAmt
,a.PayableAmt AS NewPayableAmt
,C.ReceivableAmt
INTO [New Data]
FROM A
LEFT JOIN
(
SELECT
PoolNum
,A.[Code]
,B.[Program] AS Program
,A.PayableAmt
,C.ReceivableAmt
FROM A
INNER JOIN B ON A.ID = B.ID
INNER JOIN C ON A.Num = B.Num
WHERE A.AccountingPeriod <= AccountingYearMonth
) t
ON t.Code = A.Code --assuming this is unique
INNER JOIN B ON A.ID = B.ID
INNER JOIN C ON A.Num = B.Num

SQL Inner Joins inside Outer Joins; Nesting Alters Results

I'm a bit surprised that these two queries give different results:
First Query:
SELECT a.number, a.name , b.*
FROM Atable a
LEFT OUTER JOIN Btable b
JOIN Ctable c ON c.number = b.number
ON b.number = a.number
ORDER BY a.number;
Second Query:
SELECT a.number, a.name , b.*
FROM Atable a
LEFT OUTER JOIN Btable b ON b.number = a.number
JOIN Ctable c ON c.number = b.number
ORDER BY a.number
My expectation is that both of these would return the results that the first query does. The first query returns every row from TableA; however, unexpectedly, the second row only returns results from TableA if they also exist in TableC.
Why does the join from C to B restrict TableA in the second query but not in the first query?
Thanks!
Your first query, with parens to clarify how it is parsed:
SELECT a.number, a.name , b.*
FROM Atable a LEFT OUTER JOIN
(Btable b JOIN
Ctable c
ON c.number = b.number
) ON b.number = a.number
ORDER BY a.number;
Having two on clauses in a row is confusing, so the parentheses help. This makes it clear that you are keeping all rows from the first table.
The second query is:
SELECT a.number, a.name , b.*
FROM (Atable a LEFT OUTER JOIN
Btable b
ON b.number = a.number
) JOIN
Ctable c
ON c.number = b.number
ORDER BY a.number;
You are inner joining the result of the first join. Hence, only rows that match will go to the result set.
When you are doing multiple joins, I recommend using left join for all the joins. Mixing inner and outer joins can lead to confusion.

Left Join Multiple Tables and Avoid Duplicates

I have two tables with a 1:n relationship to my base table, both of which I want to LEFT JOIN.
-------------------------------
Table A Table B Table C
-------------------------------
|ID|DATA| |ID|DATA| |ID|DATA|
-------------------------------
1 A1 1 B1 1 C1
- - 1 C2
I'm using:
SELECT * FROM TableA a
LEFT JOIN TableB b
ON a.Id = b.Id
LEFT JOIN TableC c
ON a.Id = c.Id
But this is showing duplicates for TableB:
1 A1 B1 C1
1 A1 B1 C2
How can I write this join to ignore the duplicates? Such as:
1 A1 B1 C1
1 A1 null C2
I think you need to do logic to get what you want. You want for any multiple b.ids to eliminate them. You can identify them using row_number() and then use case logic to make subsequent values NULL:
select a.id, a.val,
(case when row_number() over (partition by b.id, b.seqnum order by b.id) = 1 then val
end) as bval
c.val as cval
from TableA a left join
(select b.*, row_number() over (partition by b.id order by b.id) as seqnum
from tableB b
) b
on a.id = b.id left join
tableC c
on a.id = c.id
I don't think you want a full join between B and C, because you will get multiple rows. If B has 2 rows for an id and C has 3, then you will get 6. I suspect that you just want 3. To achieve this, you want to do something like:
select *
from (select b.*, row_number() over (partition by b.id order by b.id) as seqnum
from TableB b
) b
on a.id = b.id full outer join
(select c.*, row_number() over (partition by c.id order by c.id) as seqnum
from TableC c
) c
on b.id = c.id and
b.seqnum = c.seqnum join
TableA a
on a.id = b.id and a.id = c.id
This is enumerating the "B" and "C" lists, and then joining them by position on the list. It uses a full outer join to get the full length of the longer list.
The last join references both tables so TableA can be used as a filter. Extra ids in B and C won't appear in the results.
Do you want to use distinct
SELECT distinct * FROM TableA a
LEFT JOIN TableB b
ON a.Id = b.Id
LEFT JOIN TableC c
ON a.Id = c.Id
Do it as a UNION, i.e.
SELECT TableA.ID, TableB.ID, TableC.Id
FROM TableA a
INNER JOIN TableB b ON a.Id = b.Id
LEFT JOIN TableC c ON a.Id = c.Id
UNION
SELECT TableA.ID, Null, TableC.Id
FROM TableA a
LEFT JOIN TableC c ON a.Id = c.Id
i.e. one SELECT to being back the first row and another to bring back the second row. It's a bit rough because I don't know anything about the data you are trying to read but the principle is sound. You may need to rework it a bit.