How can I get full results in SQL query using 3 tables, where 1 of them keeps relation of 2 another? - sql

I need help writing a query to display results I want.
"Table 3 - relations" keeps all relations between table 1 and 2.Often, relation between table 1 and 2 will not exist in table 3 so I want to see missing relation in the results for all Table 1 rows - see expected Results below.
I can't modify these tables - I have only SELECT privilege.
Data and expected result below:
Table 1 - a:
a_id, a_name
e.g.:
1 A
2 B
Table 2 - b:
b_id, b_name
e.g.:
1 X
2 Y
Table 3 - relation:
asset1_id (it's always id from Table 1), asset2_id (it's always id from Table 2), relation_type
e.g.:
1 1 covers
1 2 covers
Expected result:
Table1_name, Table2_name, Table3_relation_type (including NULL for b_name and relation_type when such relation does not exist in Table 3 - relation)
e.g.
A X covers
A Y covers
B NULL NULL
I can't get the 3rd expected line with NULLs.

I think that this query will produce those results.
select a.name as a_name,b.name as b_name, r.relation_type from relation r
join a on a.id=r.asset1_id
join b on b.id=r.asset2_id
union
select a.name as a_name,b.name as b_name,r.relation_type from relation r
full outer join a on a.id=r.asset1_id
full outer join b on b.id=r.asset2_id
where a.id is null or b.id is null

With your data sample you could try this one.
It should work both hive or impala.
SELECT t1.name ,t2.name ,r.relation_type
FROM relation r
FULL OUTER JOIN table1 t1 ON(t1.id = r.id1)
FULL OUTER JOIN table2 t2 ON(t2.id = r.id2);
+------+------+---------------+
| name | name | relation_type |
+------+------+---------------+
| A | X | covers |
| A | Y | covers |
| B | NULL | NULL |
+------+------+---------------+

WITH
cte_A AS (
SELECT id as a_id, name as a_name
FROM a
),
cte_C AS (
SELECT c.asset_id1 as a_id, b.name, c.relation
FROM c
LEFT JOIN b ON c.id=b.asset_id2
)
SELECT cte_A.a_name, cte_C.name as c_name, cte_C.relation
FROM cte_A
LEFT JOIN cte_C ON cte_A.a_id=cte_C.a_id

Related

Oracle - Conditional Join eliminating additional joins where unnecessary

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 )

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

Get count of foreign key from multiple tables

I have 3 tables, with Table B & C referencing Table A via Foreign Key. I want to write a query in PostgreSQL to get all ids from A and also their total occurrences from B & C.
a | b | c
-----------------------------------
id | txt | id | a_id | id | a_id
---+---- | ---+----- | ---+------
1 | a | 1 | 1 | 1 | 3
2 | b | 2 | 1 | 2 | 4
3 | c | 3 | 3 | 3 | 4
4 | d | 4 | 4 | 4 | 4
Output desired (just the id from A & total count in B & C) :
id | Count
---+-------
1 | 2 -- twice in B
2 | 0 -- occurs nowhere
3 | 2 -- once in B & once in C
4 | 4 -- once in B & thrice in C
SQL so far SQL Fiddle :
SELECT a_id, COUNT(a_id)
FROM
( SELECT a_id FROM b
UNION ALL
SELECT a_id FROM c
) AS union_table
GROUP BY a_id
The query I wrote fetches from B & C and counts the occurrences. But if the key doesn't occur in B or C, it doesn't show up in the output (e.g. id=2 in output). How can I start my selection from table A & join/union B & C to get the desired output
If the query involves large parts of b and / or c it is more efficient to aggregate first and join later.
I expect these two variants to be considerably faster:
SELECT a.id,
, COALESCE(b.ct, 0) + COALESCE(c.ct, 0) AS bc_ct
FROM a
LEFT JOIN (SELECT a_id, count(*) AS ct FROM b GROUP BY 1) b USING (a_id)
LEFT JOIN (SELECT a_id, count(*) AS ct FROM c GROUP BY 1) c USING (a_id);
You need to account for the possibility that some a_id are not present at all in a and / or b. count() never returns NULL, but that's cold comfort in the face of LEFT JOIN, which leaves you with NULL values for missing rows nonetheless. You must prepare for NULL. Use COALESCE().
Or UNION ALL a_id from both tables, aggregate, then JOIN:
SELECT a.id
, COALESCE(ct.bc_ct, 0) AS bc_ct
FROM a
LEFT JOIN (
SELECT a_id, count(*) AS bc_ct
FROM (
SELECT a_id FROM b
UNION ALL
SELECT a_id FROM c
) bc
GROUP BY 1
) ct USING (a_id);
Probably slower. But still faster than solutions presented so far. And you could do without COALESCE() and still not loose any rows. You might get occasional NULL values for bc_ct, in this case.
Another option:
SELECT
a.id,
(SELECT COUNT(*) FROM b WHERE b.a_id = a.id) +
(SELECT COUNT(*) FROM c WHERE c.a_id = a.id)
FROM
a
Use left join with a subquery:
SELECT a.id, COUNT(x.id)
FROM a
LEFT JOIN (
SELECT id, a_id FROM b
UNION ALL
SELECT id, a_id FROM c
) x ON (a.id = x.a_id)
GROUP BY a.id;

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.