SQL - Fetch associated data from multiple table - sql

I have 4 tables. The table does not contain any foreign key reference
t1:
| id | name |
+-----+----------------+
| a1 | cheese |
| a2 | butter |
| a3 | milk |
t2:
| id | name | t1_id |
+-----+-------------+--------------+
| b1 | item1 | a1 |
| b2 | item2 | a2 |
| b3 | item3 | a3 |
t3:
| id | name | t2_id |
+-----+-------------+--------------+
| c1 | item4 | b1 |
| c2 | item5 | b2 |
| c3 | item6 | b3 |
t4:
| id | name | t3_id |
+-----+-------------+--------------+
| d1 | item7 | c2 |
| d2 | item8 | c3 |
I need to get all associated data.
When I specify 'a1' I expect something as,
| name | name | name | name |
+----------+-------------+--------------+--------------+
| cheese | item1 |item4 | |
I used the following query,
SELECT a.name, b.name, c.name, d.name FROM t1 AS a
INNER JOIN t2 AS b ON b.id = c.id
INNER JOIN t3 AS c ON c.id = b.id
INNER JOIN t4 AS d ON d.id = c.id;
This fetches no result as INNER JOIN t4 AS d ON d.id = c.id does not have matching value.
How to return the intermediate result? Is this query optimized (or) should I write separate queries to fetch this? How to achieve this?

Do you just want left joins?
SELECT a.name, b.name, c.name, d.name
FROM t1 AS a LEFT JOIN
t2 AS b
ON b.id = c.id LEFT JOIN
t3 AS c
ON c.id = b.id LEFT JOIN
t4 AS d
ON d.id = c.id;

Related

SQL when join table twice the result will display only when both tables are populated

SQL when join table twice the result will display only when both tables are populated.
SELECT D.Date, T1.SUM, T2.SUM FROM DATE AS D
LEFT JOIN T1 AS T1
ON T1.DATE = D.Date
LEFT JOIN T2 AS T2
ON T2.DATE = D.Date
If I have Date Table and then I LEFT JOIN two tables (each of them contains Date/Time and SUM)
I would like to display all the result like this:
BUT the output is:
Tbh I cant reproduce your result on mysql. These are my tables, my query and my result. Is this your real query? It seems like you do a left join for T2 with T2.Date=T1.Date?
Table Test1
+------+------+
| A | B |
+------+------+
| 1 | 5 |
| 2 | 3 |
| 3 | 4 |
+------+------+
Table Test2
+------+------+
| A | B |
+------+------+
| 1 | 5 |
| 3 | 3 |
+------+------+
Table Test3
+------+------+
| A | B |
+------+------+
| 2 | 5 |
| 3 | 6 |
+------+------+
select
t1.A,
t2.B as B1,
t3.B as B2
from Test1 t1
left join Test2 t2 on t2.A=t1.A
left join Test3 t3 on t3.A=t1.A
+------+------+------+
| A | B1 | B2 |
+------+------+------+
| 2 | NULL | 5 |
| 3 | 3 | 6 |
| 1 | 5 | NULL |
+------+------+------+

Unmatched rows with values from both tables

I have two tables T1:
+----+-----+--------------------+----------+
| ID | emp | manager | proj |
+----+-----+--------------------+----------+
| 1 | Sam | Tom | aa |
| 1 | Sam | Tom | bb |
| 1 | Sam | Tom | cc |
| 1 | Sam | Tom | dd |
+----+-----+--------------------+----------+
Table T2:
+--------+---------+--------+-----------+
| Course | Type | proj | Category |
+--------+---------+--------+-----------+
| XYZ | NEW | aa | a |
| DWE | OLD | bb | b |
| RTY | OLD | ii | c |
| UIO | NEW | gg | d |
+--------+---------+--------+-----------+
OUTPUT:
+-----------+-----+----------+--------+---------+---------+----------+
| ID | emp | manager | proj | Course | Type | Category |
+-----------+-----+----------+--------+---------+---------+----------+
| 1 | Sam | Tom | ii | RTY | OLD | c |
| 1 | Sam | Tom | gg | UIO | NEW | d |
+-----------+-----+----------+--------+---------+---------+----------+
I have one common col proj, table 1 has the proj done by the emp, table2 has both proj done and not done by emp with additional fields. I want to get all the unmatched rows from table2 but with the attributes from table 1 as above. Can someone help with a SQL query to do this??
Try this
with a as (
select T2.* from T1 right join T2
on T1.proj=T2.proj
where T1.proj is null) ,
b as (select T1.id, T1.emp, t1.manager from T1 left join T2
on T1.proj=T2.proj
group by T1.id, T1.emp, t1.manager,T2.proj
having T2.proj is null)
select * from a cross join b
select e.ID, e.emp, e.manager, t2.proj, t2.Course, t2.Type as Name, t2.Category
from t2
cross join (select distinct ID, emp, manager from T1) e
where not exists(select * from t1 where t1.prog=t2.proj)
SELECT tbl.ID, tbl.emp, tbl.manager, t2.proj, t2.Course, t2.Type as Name, t2.Category
FROM
(
SELECT t2.ID, t2.emp, t2.manager
FROM Table2 t2
WHERE NOT EXISTS (SELECT 1 FROM Table1 t1 WHERE t1.proj = t2.proj)
) tbl
CROSS APPLY Table1 t1

Select from two slightly similar tables with complex conditions

I have 4 tables in an oracle database with a complex relationship and they do not have useful primary keys.
TableA
+------+------+------+------+------+-----------------+
| ColA | ColX | ColY | ColZ | ColZa| A |
+------+------+------+------+------+-----------------+
| k9 | a1 | c1 | g1 | z1 | 2018-02-19 |
| k9 | a1 | c1 | g3 | z2 | 2018-02-02 |
| k10 | a2 | f3 | g1 | z3 | 2018-02-09 |
| k10 | a | b | c | d | 2018-02-03 |
| k | a | b | c1 | z2 | 2018-02-01 |
| k9 | a1 | c1 | c9 | z5 | 2018-02-04 |
| k9 | a1 | c1 | c2 | z5 | 2018-02-03 |
| k9 | a1 | c1 | g2 | z5 | 2018-02-03 |
+------+------+------+------+------+-----------------+
TableB
+------+------+------+------+------+----------------+
| ColA | ColX | ColY | ColZ | ColZa| B |
+------+------+------+------+------+----------------+
| e | a3 | f | g1 | i | 2018-02-03 |
| e3 | a1 | f1 | g3 | d2 | 2018-02-04 |
| k9 | a1 | c1 | g2 | z5 | 2018-02-08 |
| e4 | a4 | f2 | g2 | i2 | 2018-02-07 |
| e5 | a1 | f1 | g1 | d2 | 2018-02-06 |
| k9 | a1 | c1 | g1 | d2 | 2018-02-22 |
+------+------+------+------+------+----------------+
TableC
+------+------+------+----------------+
| ColA | ColX | ColY | C |
+------+------+------+----------------+
| ab | c2 | c2 | cx |
| k9 | a1 | c1 | cy |
| cd | a2 | c3 | cy |
| ef | c2 | c4 | cz |
| ef | c2 | c2 | cz |
+------+------+------+----------------+
TableD
+------+------+------+----------------+
| ColA | ColX | ColY | D |
+------+------+------+----------------+
| e | a | f | dx |
| e1 | a | a | dy |
| e2 | a1 | a1 | dz |
+------+------+------+----------------+
Some business logic requires me to select and combine data from TableA and TableB
The Problem:
Fetch records ColA, ColX, ColY, ColZ, ColZa, A, B in TableA AND/OR TableB for cases where pseudo key ColA_ColX_ColY have value ColZ = 'g1', with merge on ColA | ColX | ColY | ColZ | ColZa.
I used the word 'pseudo' here because it is not really a key but it's just a means to identify the records of interest in TablesA and TablesB.
To construct a valid key, count(colY) must be 1 for value in colX in TableC and TableD (this is actually the case in all four tables but if you only consider distinct values but I am suppose to use only TableC and TableD since it is more explicit)
The process:
In the result table below, I should get row1 in table TableA because 'a1' has only one count(ColY)=1 in TableC but I ignored row1 in TableB and row3 in TableA because count(ColY) is not equal to 1 in either TableC or TableD
Now that I have a value 'a1' from TableC.ColX which matches my criteria, I select all records in TableA and TableB where ColX = 'a1' and ColY = 'c1' and ColA = 'k9'
My desired result
+------+------+------+------+------+-----------------+----------------+
| ColA | ColX | ColY | ColZ | ColZa| A | B |
+------+------+------+------+------+-----------------+----------------|
| k9 | a1 | c1 | g1 | z1 | 2018-02-19 | [null] |
| k9 | a1 | c1 | g3 | z2 | 2018-02-02 | [null] |
| k9 | a1 | c1 | c9 | z5 | 2018-02-04 | [null] |
| k9 | a1 | c1 | c2 | z5 | 2018-02-03 | [null] |
| k9 | a1 | c1 | g2 | z5 | 2018-02-03 | 2018-02-08 |
| k9 | a1 | c1 | g4 | d2 | [null] | 2018-02-22 |
+------+------+------+------+------+-----------------+----------------+
So, I wrote a query similar to
select a.ColX, a.ColY, a.ColZ, a.ColZa, a.A, b.B from TableA a FULL OUTER JOIN TableB b ON a.ColX=b.ColX AND a.ColY=b.ColY AND a.ColZ=b.ColZ
where (
a.ColX IN
(select ColX from TableA where
ColX IN
(select ColX from TableC group by ColX HAVING count(ColY)=1) and
ColX in
(select distinct ColX from TableB where ColZ = 'g1'and B > trunc(sysdate) - 365)
group by ColX having count(distinct ColY)=1)
OR
b.ColX IN
(select ColX from TableA where
ColX IN
(select ColX from TableC group by ColX HAVING count(ColY)=1) and
ColX in
(select distinct ColX from TableB where ColZ = 'g1' and B > trunc(sysdate) - 365)
group by ColX having count(distinct ColY)=1));
I have no control over the data model here. How do I make my query work?
The data in TableA and TableB are in 100,000 records and data in TableC and TableD are up to a million.
SQL is not my area of expertise and I really hope I am not going too off the mark here.
I didn't understand what your query is supposed to do, but as a pure refactoring exercise I get this:
with whatever as
( select colx
from tablea
where colx in
( select colx
from tablec
group by colx having count(colb) = 1
union all
select colx
from tableb
where colz = 'g1'
and b > trunc(sysdate) - 365 )
group by colx
having count(distinct colza) = 1 )
select a.colx, a.coly, a.colz, a.colza, a.a, b.b
from tablea a
full outer join tableb b
on a.colx = b.colx
and a.coly = b.coly
and a.colz = b.colz
join whatever w
on w.colx in (a.colx, b.colx);

SQL conditional join to multiple tables in one shot

I would like to make conditional join of three tables. Left join table B and if the key is missing then join to table C.
+------+--+------+-------------+--+------+-------------+
| A.id | | B.id | B.OtherFish | | C.id | C.OtherFish |
+------+--+------+-------------+--+------+-------------+
| 1 | | 1 | B1 | | 1 | C1 |
| 2 | | | | | 2 | C2 |
| 3 | | 3 | B3 | | 3 | C3 |
| 4 | | | | | 4 | C4 |
| 5 | | 5 | B5 | | | |
| 6 | | 6 | B6 | | 6 | C6 |
+------+--+------+-------------+--+------+-------------+
There are no matching keys for 2 and 4 in B, and no keys for 5 in C.
Expected results:
+------+-----------+
| A.id | OtherFish |
+------+-----------+
| 1 | B1 |
| 2 | C2 |
| 3 | B3 |
| 4 | C4 |
| 5 | B5 |
| 6 | B6 |
+------+-----------+
The query I use is:
select
A.id
,coalesce(B.id,C.id)
,coalesce(B.OtherFish,C.OtherFish)
from A
left join B
on A.id=B.id
left join C
on A.id=C.id
The drawback of the approach is that I have to use coalesce through all the columns I need from different tables. It is annoying if there are hundred columns in the table. It would be desirable to make a coalesce on the whole aliases like coalesce(B,C).
Is it possible to make it in one shot like:
left join B
on A.id=B.id
left join C
on A.id=(case when B.id is null then C.id end)
so that in C.id I would have all good data without making coalesce through all the columns?
Although I don't see anything wrong with COALESCE , you can try using UNION , something like this:
SELECT a.id,t.OtherFish
FROM A
LEFT JOIN(SELECT b.id,b.otherFish FROM b
UNION ALL
SELECT c.id,c.otherFish FROM C
where c.id NOT EXISTS(SELECT 1 FROM b bb WHERE bb.id = c.id)) t
ON(a.id = t.id)

comparing rows in same table

I am having a result from a query which looks as below
+-------------------+
| id | c1 | c2 | c3 |
+-------------------+
| 1 | x | y | z |
+----+----+----+----+
| 1 | x | y | z1 |
+----+----+----+----+
| 2 | a | b | c |
+----+----+----+----+
| 2 | a1 | b | c1 |
+-------------------+
I need to fetch only records which have values in C1 and c2 different for the same id.
For the above example the result should be
+-------------------+
| id | c1 | c2 | c3 |
+-------------------+
| 2 | a | b | c |
+----+----+----+----+
| 2 | a1 | b | c1 |
+-------------------+
Can you please help with the query.
Joining the table to itself should work. I'm assuming that you meant C1 or C2 being different, given the example result you posted.
SELECT
t1.id,
t1.c1,
t1.c2,
t1.c3
FROM
your_table t1
INNER JOIN your_table t2 ON t1.id = t2.id
WHERE
t1.c1 <> t2.c1 OR
t1.c2 <> t2.c2
SQLFiddle.
This will give you any row which is different of any other. However in the case you have 3 rows, it might not work as you want.
SELECT t1.*
FROM someTable t1, someTable t2
WHERE t1.id = t2.id
AND (t1.c1 != t2.c1 OR t1.c2 != t2.c2)
Edit:
If you want only rows that are different of any other row with the same id the first query won't work in this case:
+-------------------+
| id | c1 | c2 | c3 |
+-------------------+
| 1 | x | y | z |
+----+----+----+----+
| 1 | x | y | z1 |
+----+----+----+----+
| 2 | a | b | c |
+----+----+----+----+
| 2 | a1 | b | c1 |
+----+----+----+----+
| 2 | a1 | b | c3 |
+-------------------+
You would get:
+-------------------+
| id | c1 | c2 | c3 |
+-------------------+
| 2 | a | b | c |
+----+----+----+----+
| 2 | a1 | b | c1 |
+----+----+----+----+
| 2 | a1 | b | c3 |
+-------------------+
Which I think would be wrong. In that case you will need something like:
SELECT t2.*
FROM
(
SELECT id, c1, c2
FROM someTable
GROUP BY id, c1, c2
HAVING COUNT(*) = 1
) t1
JOIN someTable t2 ON t2.id = t1.id
AND t2.c1 = t1.c1
AND t2.c2 = t1.c2
If I understood the question correctly I think the answer of #MattGibson is correct, but if the order matters then order by clause will be required (order by id).
Try this:
SELECT * FROM myTable WHERE ID
IN(SELECT ID FROM myTable GROUP BY ID HAVING COUNT(ID)>1)