I have 4 tables as shown below
I basically want to get how many users from table1 are in tables 2, 3 and 4. Similarly for table2 I want to get how many users are present in table 1, 3 and 4. and same for tables 3 and 4
Basically all the possible combinations. The final result I want is something as below
One of the way I am trying to solve is by doing a left-join of table1 with other tables to followed by count to get first row of my output. But doing it for all the possible combinations is not optimized. I was looking for any other alternative that is possible
My code for the same
SELECT
COUNT(DISTINCT A.id) table1,
COUNT(DISTINCT B.id) table2,
COUNT(DISTINCT C.id) table3,
COUNT(DISTINCT D.id) table4
FROM table1 A
LEFT JOIN table2 B
ON A.id = B.id
LEFT JOIN table3 C
ON A.id = C.id
LEFT JOIN table4 D
ON A.id = D.id
db-fiddle (This fiddle is for mysql, I am looking for a generic SQL based approach than any db specific approach)
I would recommend:
with t as (
select 'table1' as which, id from table1 union all
select 'table2' as which, id from table2 union all
select 'table3' as which, id from table3 union all
select 'table4' as which, id from table4
)
select ta.which,
sum(case when tb.which = 'table1' then 1 else 0 end) as cnt_table1,
sum(case when tb.which = 'table2' then 1 else 0 end) as cnt_table2,
sum(case when tb.which = 'table3' then 1 else 0 end) as cnt_table3,
sum(case when tb.which = 'table4' then 1 else 0 end) as cnt_table4
from t ta left join
t tb
on ta.id = tb.id
group by ta.which;
Note: This assumes that id is unique in each of the tables. That is a reasonable assumption given the name of the column and the sample data. However, if there are duplicates, you can change the union all in the CTE to union.
This structure also readily generalizes to additional tables.
Use UNION ALL
DEMO
select 'table1' as col1,count(table1.id),count(table2.id),count(table3.id),count(table4.id)
from table1
left join table2 on table1.id=table2.id
left join table3 on table1.id=table3.id
left join table4 on table1.id=table4.id
union all
select 'table2' ,count(table1.id),count(table2.id),count(table3.id),count(table4.id)
from table2
left join table1 on table2.id=table1.id
left join table3 on table2.id=table3.id
left join table4 on table2.id=table4.id
union all
select 'table3' ,count(table1.id),count(table2.id),count(table3.id),count(table4.id)
from table3
left join table1 on table3.id=table1.id
left join table2 on table3.id=table2.id
left join table4 on table3.id=table4.id
union all
select 'table4' ,count(table1.id),count(table2.id),count(table3.id),count(table4.id)
from table4
left join table1 on table4.id=table1.id
left join table2 on table4.id=table2.id
left join table3 on table4.id=table3.id
OUTPUT:
col1 tbl1 tbl2 tbl3 tbl4
table1 8 3 2 2
table2 3 6 1 0
table3 2 1 5 0
table4 2 0 0 4
Related
I want to select the data from any of the four tables. Data can be available on any one out of four. Four table also will have data. two table also will have data. one table also will have data. please correct me below.
select top 100 t1.*
from
Table1 t1
left JOIN Table2 t2 on t1.EventId = t2.EventId
LEFT JOIN Table3 t3 ON t1.EventId = t3.EventId
LEFT JOIN Table4 t4 ON t1.EventId = t4.EventId
WHERE
t1.EventId = 12345 AND
t1.EditType = 'D' and
t2.EditType = 'D'and
t3.EditType = 'D' and
t4.EditType = 'D'
Putting the conditions in the WHERE clause turns outer joins into inner joins. Put the conditions in the ON clause
Select *
from Table1 t1
left JOIN Table2 t2
on t1.EventId = t2.EventId
and t2.EditType = 'D'
left JOIN Table3 t3
ON t1.EventId = t3.EventId
and t3.EditType = 'D'
left JOIN Table4 t4
ON t1.EventId = t4.EventId
and t4.EditType = 'D'
where t1.EventId = 12345
and t1.EditType = 'D'
If your tables have the same structure, you might be better doing a union all in a view or CTE (common table expression), then selecting from it instead of doing a left join - that way your information is appearing in separate records:
WITH FullData AS (
SELECT *, 1 AS TableSource FROM Table1
UNION ALL
SELECT *, 2 AS TableSource FROM Table2
UNION ALL
SELECT *, 3 AS TableSource FROM Table3
UNION ALL
SELECT *, 4 AS TableSource FROM Table4 )
SELECT * FROM FullData WHERE EventID = 12345 And EventType = 'D'
As per my example, you can also add in a source identifier to tell which table the information is being pulled from - as long as the structure remains the same throughout, the union all will work fine.
Is there any alternate way to fetch the following data without using union?
select A.name,A.age,B.Address,C.phoneNo from table1 A,Table2 B,Table3 C where a.pkId = b.FkId and b.pkId = c.FkId
union
select A.name,A.age,B.Address,C.phoneNo from table4 A,Table5 B,Table3 C where a.pkId = b.FkId and b.pkId = c.FkId
I am using this in Hibernate and unfortunately hibernate doesnt support Union. I was just wondering if there is any other way to achieve it else ill have to write it in a procedure and save the data in temp table and fire a sql to read data from that temp table
There is an alternative for union, but it is not pretty:
select distinct coalesce(x1.name, x2.name) as name,
coalesce(x1.age, x2.age) as age,
coalesce(x1.Address, x2.Address) as age,
coalesce(x1.phoneNo, x2.phoneNo) as age,
from (select A.name, A.age, B.Address, C.phoneNo
from table1 A join
Table2 B
on a.pkId = b.FkId join
Table3 C
on b.pkId = c.FkId
) x1 full outer join
(select A.name, A.age, B.Address, C.phoneNo
from table4 A join
Table5 B
on a.pkId = b.FkId join
Table3 C
on b.pkId = c.FkId
) x2
on 1 = 0; -- always false
I can't imagine why you would want to express a union like this. I would highly recommend, though, that you start using proper, explicit join syntax.
is this working ?
SELECT
CASE DISTINCT_FLG WHEN 1 THEN nameA ELSE nameB END name,
CASE DISTINCT_FLG WHEN 1 THEN ageA ELSE ageB END age,
CASE DISTINCT_FLG WHEN 1 THEN AddressA ELSE AddressB END Address,
CASE DISTINCT_FLG WHEN 1 THEN phoneNoA ELSE phoneNoB END phoneNo
FROM (
SELECT
T1.name AS nameA, T1.age AS ageA, T2.Address AS AddressA, T3.phoneNo AS phoneNoA,
T4.name AS nameB, T4.age AS ageB, T5.Address AS AddressB, T3.phoneNo AS phoneNoB,
ROW_NUMBER() OVER(PARTITION BY T1.name, T1.age, T2.Address, T4.name, T4.age, T5.Address, T3.phoneNo ORDER BY NULL) AS DISTINCT_FLG
FROM
table1 T1,
table2 T2,
table4 T4,
table5 T5,
table3 T3
WHERE
T1.pkId = T2.FkId AND
T4.pkId = T5.FkId AND
(
T2.pkId = T3.FkId OR
T5.pkId = T3.FkId
)
) WHERE DISTINCT_FLG IN (1, 2)
Those two UNION parts have Table3 C in common, so we can join the rest to it. To emulate UNION records from the source table can be replicated through unconditional cross join of an auxiliary table with the required number or rows:
Select Distinct
CASE R.r WHEN 1 THEN A1.name ELSE A2.name END As name ,
CASE R.r WHEN 1 THEN A1.age ELSE A2.age END As age ,
CASE R.r WHEN 1 THEN B1.Address ELSE B2.Address END As Address,
C.phoneNo
From Table3 C, --< Start at common Table3
(Select Rownum r From USER_TABLES Where Rownum < 3) R --< Two rows to replicate Table3 C
-- Any table with more than one row will do
-- USER_TABLES should have enough rows in this particular case
Left Join Table2 B1 On R.r = 1 AND B1.pkId = C.FkId --< Left Join branch one
Left Join table1 A1 On R.r = 1 AND A1.pkId = B1.FkId
Left Join Table5 B2 On R.r = 2 AND B2.pkId = C.FkId --< Left Join branch two
Left Join table4 A2 On R.r = 2 AND A2.pkId = B2.FkId
Where (R.r = 1 AND A1.pkId Is NOT NULL) --/ Make sure we have values
OR (R.r = 2 AND A2.pkId Is NOT NULL) --\ for the branch
But really, consider a view.
I have two table a and b
table a
ID
a
b
c
table b
ID Value
a 1
b 2
default 0
So I want to join two tables on ID when exactly matching, otherwise use default value
The desired results
ID Value
a 1
b 2
c 0
Use a LEFT OUTER JOIN for that purpose like
select t1.ID, COALESCE(t2.Value, 0) as Value
from tablea t1
left join tableb t2 on t1.ID = t2.ID;
Try this:
SELECT a.ID, b.Value
FROM a
INNER JOIN b
ON a.ID = b.ID
UNION ALL
SELECT a.ID, b.Value
FROM a
CROSS JOIN b
WHERE
a.id <> b.id
This will provide you with all matching IDs, then the UNION of the CROSS JOIN query should provide the default value for non-matching IDs.
Try this:
SELECT t1.ID,
COALESCE(t2.Value, (SELECT Value FROM tableb t3 WHERE ID ='default')) AS Value
FROM tablea t1
LEFT JOIN tableb t2 on t1.ID = t2.ID;
I want the use 2 SELECT statements and an INNER JOIN from different queries BUT also want to show the two different results, from different tables in the same query. Like this..
SELECT column1 FROM earth e1 that is null
+
SELECT chair5 FROM space s1 that is not null
INNER JOIN space s1 ON e1.car = s1.truck
ORDER BY e1.column,s1.chair5
How do I show the results of two different queries while using an INNER JOIN?
Assume the table T1 contains values 'A','B','C' and table T2 contains values 'A','B','D'
You may query
select 'T1' as source, col from t1
union all
select 'T2' as source, col from t2
union all
select 'join T1,T2' as source, t1.col from t1 inner join t2
on t1.col= t2.col
order by 1,2
;
getting
SOURCE COL
---------- -----
T1 A
T1 B
T1 C
T2 A
T2 B
T2 D
join T1,T2 A
join T1,T2 B
The first column identifies the source: single query or a join
Alternatively, which I'll prefer you may get the same information (more compressed) using FULL OUTER JOIN
with fj as (
select t1.col t1_col, t2.col t2_col
from t1 full outer join t2
on t1.col= t2.col
)
select
case when t1_col is not null and t2_col is not null then 'both'
when t1_col is not null then 'T1 only'
when t2_col is not null then 'T2 only' end as source,
nvl(t1_col, t2_col) col
from fj
order by 1,2
.
SOURCE COL
------- ----------
T1 only C
T2 only D
both A
both B
This is simple SQL JOIN question and my solution works while trying with sample data but when i do the same with huge data, it fails.
I have two table
tbl1
a b
0 10
1 2
4 5
2 2
Another table tbl2
a c
1 22
2 18
10 9
98 8
Now i want final table like this
a b c
0 10
1 2 22
2 2 18
4 5
10 9
98 8
What i did is:
1) temptbl = select a from tbl1 UNION select a from tbl2;
2) valueA = temptbl left join tbl1 on a
3) valueB = temptbl left join tbl2 on a
4) inner join valueA and ValueB on a
My solution works on small data when i try it locally, but while running it on server, left join produces some random data (steps 1 works, but after step 2, it does not work). Can somebody help me on this? AM i doing wrong? Are there any other solution
Please note, value in column a is unique in both the table.
You can do this in pieces:
-- Pick out records whose "a" values are in T1
SELECT T1.a, T1. b, T2.c
FROM T1
LEFT OUTER JOIN T2
ON T1.a=T2.a
UNION
-- Add records whose "a" values are NOT in T1
SELECT T2.a, NULL 'b', T2.c
FROM T2
WHERE NOT EXISTS (SELECT 1 FROM T1 WHERE T1.a = T2.a)
You probably want something like this:
SELECT coalesce(tbl1.a, tbl2.a) as a, /* one of them will be non-null */
tbl1.b,
tbl2.c
FROM tbl1 FULL OUTER JOIN tbl2
ON tbl1.a = tbl2.a
If your database doesn't support the FULL JOIN, you can UNION together a LEFT JOIN for each of the tables. A LEFT JOIN with only tbl1 on the "left side" will not yield records where only tbl2 has an a value.
Edit: Per the OP's request here's the equivalent as a UNION -- since apparently this database doesn't support #DVK's good suggestion of an anti-join:
SELECT a,
b,
NULL as c
FROM tbl1
UNION
SELECT a,
NULL as b,
c
FROM tbl2
Try something like
SELECT all.a, b, c
FROM (SELECT DISTINCT a FROM tbl1 UNION SELECT a from tbl 2) all
LEFT OUTER JOIN tbl1 on tbl1.a = all.a
LEFT OUTER JOIN tbl2 on tbl2.a = all.a