Oracle - Conditional Join eliminating additional joins where unnecessary - sql

Let’s say I have a simplified table structure as follows:
Table A - ID (PK)
Table B - ID (PK), AID = FK to Table A
Table C - ID (PK), BID = FK to Table B
Table D - ID (PK), CID = FK to Table C
Query something like so:
SELECT * FROM TABLE_A TBLA
LEFT JOIN TABLE_B TBLB ON TBLA.ID = TBLB.AID
LEFT JOIN TABLE_C TBLC ON TBLB.ID = TBLC.BID
LEFT JOIN TABLE_D TBLD ON TBLC.ID = TBLD.CID
It’s relatively straight-forward but what I want to do is somewhat a conditional join in that I want all records from TABLE A but want to join TABLE B -> TABLE C -> TABLE D if the first join between TABLE A and TABLE B is satisfied, bearing in mind that I could change TABLE B -> TABLE C -> TABLE D joins to be INNER as there’ll exist in that initial join between TABLE A and TABLE B is satisfied.-
But also I’d need a WHERE condition on TABLE D also.
So essentially want to eliminate the LEFT JOIN’S between TABLE_B, TABLE_C, TABLE_D where join isn’t satisfied between TABLE_A and TABLE_B.
Very simplified data so apologies!
| Table A |
| ID |
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| Table B |
| ID | AID |
| 1 | 5 |
| Table C |
| ID | BID |
| 1 | 1 |
| Table D |
| ID | CID | Value |
| 1 | 1 | ABC |
The reason I want to eliminate the join is that for 4 of the 5 rows in Table A, I’m doing unnecessary joins across three tables to get the value in Table D.

You can use brackets with join, but not sure if that would help you!
SELECT * FROM
TABLE_A TBLA
LEFT JOIN (TABLE_B TBLB
INNER JOIN TABLE_C TBLC ON TBLB.ID = TBLC.BID
INNER JOIN TABLE_D TBLD ON TBLC.ID = TBLD.CID) ON TBLA.ID = TBLB.AID
This way, when you have A matches entries in B, but B is not matched in the chain to C and D, B data is not retrieved, and so for C
You mentioned that you need a where condition on table D? Does that mean that you always have to link to D?! Note that if you have a condition on D, the condition has to be satisfied in all cases. Hence, when no records are retrieved from D, no records will be retreived from the query (even with your initial outer join unless you used OR .. is null )

Related

Delete rows from table using JOIN - SQL Server

I have a table_A -
id | name | is_active |
----+---------+------------+
1 | jon | 1 |
2 | ham | 0 |
3 | gary | null |
I have a table_B -
id | name |
----+---------+
1 | jon |
2 | ham |
I want to remove rows from table B that have is_active value as 0 OR null in table A. So I'm thinking about an INNER JOIN on id column and applying a WHERE clause.
DELETE ROWS from table_B B
INNER JOIN table_A A ON B.id = A.id
WHERE A.is_active = 0 OR A.is_active IS NULL
I don't want any additional columns or changes in table B after the above query. Is this the right way to do it?
Instead of JOIN, use exists:
DELETE FROM table_B
WHERE EXISTS (SELECT 1
FROM table_A A
WHERE A.id = table_B.id AND
(A.is_active = 0 OR A.is_active is null)
);
You need to say which table you want to delete from, use the alias if you have one:
DELETE B
FROM table_B B
INNER JOIN table_A A ON B.id = A.id
WHERE (A.is_active = 0 OR A.is_active IS NULL);

Trying to write an inner join to filter out some conditions

I'm currently struggling with carrying out some joins and hoping someone can shed some light on this.
I have three tables: A,B,C
Table C lists names of individuals
Table A lists the food they like to eat
Table B is the link to show what food in A a person likes from C (Our
system was built without foreign keys! I know, it's a pain!)
What I'm trying to write is a query that will return a list of values from Table C which shows the individuals that don't like a specific food...say PFC
I have the following:
select * from table_c c
inner join table_b b
on c.name = b.bValue
inner join table_a a
on b.aValue = a.number
where a.value not in('PFC')
I'm assuming the joins are working but as table A has multiple values, the two extra rows are being returned. Is it possible to not show this client if one of the joins shows a food I don't want to see?
Table A
|---------------------|------------------|
| Number | Value |
|---------------------|------------------|
| 1 | McDs |
|---------------------|------------------|
| 1 | KFC |
|---------------------|------------------|
| 1 | PFC |
|---------------------|------------------|
Table B
|---------------------|------------------|
| bValue | aValue |
|---------------------|------------------|
| John | 1 |
|---------------------|------------------|
Table C
|---------------------|
| Name |
|---------------------|
| John |
|---------------------|
I'm also using SQL Server 2013 if that makes a difference!
With NOT EXISTS:
select * from table_c c
where not exists (
select 1 from table_b b inner join table_a a
on b.aValue = a.number
where b.bValue = c.name and a.value = 'PFC'
)
One option is to aggregate by name:
SELECT
c.Name
FROM table_c c
INNER JOIN table_b b
ON c.Name = b.bValue
INNER JOIN table_a a
ON b.aValue = a.Number
GROUP BY
c.Name
HAVING
COUNT(CASE WHEN a.Value = 'PFC' THEN 1 END) = 0;
We could also try expressing this using an exists query:
SELECT
c.Name
FROM table_c c
WHERE NOT EXISTS (SELECT 1 FROM table_b b
INNER JOIN table_a a
ON b.aValue = a.Number
WHERE c.Name = b.bValue AND
a.Value = 'PFC');

Inner join table a or b based on existence

I've got these 3 tables:
Table A
---------------------------
|KEY | VALUE_A1 | VALUE_A2|
---------------------------
Table B
------------------------------
|KEY | APPLICATION | VALUE_B1|
------------------------------
Table C
------------------------------
|KEY | APPLICATION | VALUE_C1|
------------------------------
I want to return A.VALUE_A1, A.VALUE_A2 along with B.VALUE_B1 or C.VALUE_C1 based on the existence of the application in B or C. This application is given in de where-clause in de script.
Example:
Table A has 10 records. Table B contains 6 records with application 'APP1'. Table C contains 4 records with application 'APP2'. Both table B and C are through KEY connected to table A. If the application given is 'APP1' then I want to return B.VALUE_B1. If 'APP2' is given I want to return C.VALUE_C1.
Is it possible to INNER JOIN on one of two tables based on a CASE-like condition?
Not INNER JOIN but LEFT JOIN like this
select A.KEY
A.VALUE_A1, A.VALUE_A2,
CASE WHEN B.VALUE_B1 IS NOT NULL THEN B.VALUE_B1 ELSE C.VALUE_C1 END AS RESULT_VALUE
from A
LEFT JOIN B on A.key=B.key
LEFT JOIN C on A.key=C.key
UPDATE: There is an alternative
select A.KEY
A.VALUE_A1, A.VALUE_A2,
sub.VALUE AS RESULT_VALUE
from A
INNER JOIN (SELECT KEY, VALUE_B1 as VALUE
FROM B
UNION ALL
SELECT KEY, VALUE_C1 as VALUE
FROM C) sub on A.KEY=sub.KEY

SQL Join to Get Row with Maximum Value from Right table

I am having problem with sql join (oracle/ms sql)
I have two tables
A
ID | B_ID
---|------
1 | 1
1 | 4
2 | 3
2 | 2
----------
B
B_ID | B_VA| B_VB
-------|--------|-------
1 | 1 | a
2 | 2 | b
3 | 5 | c
4 | 2 | d
-----------------------
From these two tables I need A.ID, B.B_ID, B.B_VA (MAX), B.B_VB (with max B.B_VA)
So result table would be like
ID | B_ID | B_VA| B_VB
-------|--------|--------|-------
1 | 4 | 2 | d
2 | 3 | 5 | c
I tried some joins without success. Can anyone help me with query to get the result I want.
Thank you
Your logic as described doesn't quite correspond to the data. For instance, b_va is numeric, but the column in the output is a string.
Perhaps you want this. The data in a to be aggregated to get the maximum b_id value. Then each column to be joined to get the corresponding b_vb column. That, at least, conforms to your desired output:
select a.id, a.b_id, b1.b_vb as b_va, b2.b_vb
from (select id, max(b_id) as b_id
from a
group by id
) a join
b b1
on a.id = b1.b_id join
b b2
on a.b_id = b2.b_id;
EDIT:
For the corrected data, I think this is what you want:
select a.id, a.b_id, max(b1.b_va) as b_va, b2.b_vb
from (select id, max(b_id) as b_id
from a
group by id
) a join
b b1
on a.id = b1.b_id join
b b2
on a.b_id = b2.b_id
group by a.id, a.b_id, b2.b_vb;
Try this
SELECT X.ID, Y.B_ID, X.B_VA, Y.B_VB
FROM (SELECT A.ID, MAX(B_VA) AS B_VA
FROM A INNER JOIN B ON A.B_ID = B.B_ID
GROUP BY A.ID) AS X INNER JOIN
A AS Z ON X.ID = Z.ID INNER JOIN
B AS Y ON Z.B_ID=Y.B_ID AND X.B_VA=Y.B_VA

Left Outer Join, No result in final query

I'm executing the following query
Select * from A a left outer join B b on (b.id = a.id)
I'm getting one record from A and no records from B. I'm expecting one record in final select query but getting none.
Here is some sample data:
A
v_id, id, date, d_id
1, 1244578, 02-MAR-11, 1827877
B, no data presented:
e_id,id,amount
What am I doing wrong? How can I get it do like this ?
This should work fine:
Select *
from A a
left outer join B b on b."id" = a."id"
See it in action here:
SQL Fiddle Demo
This will give you:
| V_ID | ID | DATE | D_ID | E_ID | AMOUNT |
----------------------------------------------------------
| 1 | 1244578 | 02-MAR-11 | 1827877 | (null) | (null) |
You are getting one record from Table A this is because table A has only one record and Table B have no record.
In left outer join content of first table is show in result join with second table but in your case second table have no records so final result show null values for that records.