Combine 2 tables in SQL including uncommon fields - sql

I have two tables as follows:
table 1 table 2
id name count1 | id name count2
1 x 2 1 x 1
2 y 3 4 y 1
3 z 1 5 z 2
Result expected:
id name count1 count2
1 x 2 1
2 y 3 0
3 z 1 0
4 y 0 1
5 z 0 2
SQL query tried:
SELECT table1.id as id, table1.name as name,
table1.count1 as count1, table2.count2 as count2
FROM table1
LEFT JOIN table2 on table1.id = table2.id and table1.name = table2.name
I feel like this is wrong because I am not getting the expected result. I am unsure of which join to use since I am new. Any help here would be much appreciated.
Thank you very much

You can use UNION ALL within the subquery and then aggregate in the main query as
SELECT id, name, SUM(count1) AS count1, SUM(count2) AS count2
FROM
(
SELECT id, name, count1, 0 AS count2
FROM table1
UNION ALL
SELECT id, name, 0, count2
FROM table2
) q
GROUP BY id, name

You can use a full join:
SELECT COALESCE(t1.id, t2.id) as id, COALESCE(t1.name, t2.name) as name,
COALESCE(t1.count1, 0) as count1, COALESCE(t2.count2, 0) as count2
table1.count1 as count1, table2.count2 as count2
FROM table1 t1 FULL JOIN
table2 t2
ON t1.id = t2.id and t1.name = t2.name;
If your database does not support full join, then you basically have two options. One only uses LEFT JOIN:
SELECT ni.id, ni.name,
COALESCE(t1.count1, 0) as count1, COALESCE(t2.count2, 0) as count2
table1.count1 as count1, table2.count2 as count2
FROM ((SELECT id, name FROM table1
) UNION -- on purpose to remove duplicates
(SELECT id, name FROM table2
)
) ni LEFT JOIN
table1 t1
ON t1.id = ni.id AND ni.name = t1.name
table2 t2
ON ni.id = t2.id and ni.name = t2.name;
The second method is uses UNION ALL, NOT UNION:
SELECT t1.id, t1.name, t1.count1, COALESCE(t2.count2, 0) as count2
FROM table1 t1 LEFT JOIN
table2 t2
ON t1.id = t2.id and t1.name = t2.name
UNION ALL
SELECT t2.id, t2.name, 0, t2.count2
FROM table2 t2 LEFT JOIN
table1 t1
ON t1.id = t2.id and t1.name = t2.name
WHERE t1.id IS NULL;
I have no idea where the "canonical" approach using UNION comes from. I think it is a thought exercise on being close. However, it is a very poor solution for the following reasons:
UNION adds additional overhead for removing duplicates. And that is not part of FULL JOIN functionality.
The lack of WHERE clause on the second query ensures that there are duplicates if there are any matches.
The use of UNION removes duplicates within each subquery. Whether desirable or not, that is not how FULL JOIN works.

Related

SQL Get Count Across 3 Tables

I have a 3 tables, one with these two columns
table1:
id, name
0 foo
1 etc
2 example
table2:
id table1_id
0 1
1 0
2 2
table3:
id table2_id
0 1
1 0
2 0
Which query can I find all 'name's from table1 where ALL ids in table2 have a count of atleast n in table3? i.e if n was 1 it should return foo and etc
EDIT:
Explained poorly, I'm trying to get the name of every record in table1 where ALL corresponding records in table2 (i.e records where the column table1_ID is equal to each id within table1. In my example tables, each ID has one) have a count in table3 of atleast n.
If n was 1, as the table2_id 0 appears twice in records 1 and 2, its 'parent' would be returned. It corresponds to the table 1 record 1, so the name of the record with table1 id: 1 should be returned, which is etc. Example also as it has a count of 1 in the bottom column, however foo does not appear so it shouldnt.
Expected result:
name
foo
etc
You can do this using a subquery in the where clause:
select t1.*
from table1 t1
where (select count(t3.id)
from table2 t2 left join
table3 t3
on t3.table2_id = t2.id
where t2.table1_id = t1.id
group by t2.id
order by count(*) asc -- to get the minimum
limit 1
) >= ? -- value you care about
I suspect that this might have the best performance with appropriate indexes: table2(table1_id, id) and table3(table2_id).
If I have understood the question - if a check on table3.table2_id is greater than 0, the answer would be 'etc' ?
Code below
select t1.name
from
(
select 0 as id, 1 as table2_id
union select 1, 0
union select 2 , 0
) t3
inner join
(
select 0 as id , 1 as table_id
union select 1, 0
union select 2, 2
) t2 on t2.table_id = t3.table2_id
inner join
(
select 0 as id, 'foo' as name
union select 1 , 'etc'
union select 2 , 'example'
) t1 on t1.id = t2.table_id
where t3.table2_id > 0
select table1.name
FROM table1
INNER JOIN table2 ON table1.id=table2.table1_id
INNER JOIN table3 ON table2.id=table3.table2_id
GROUP BY table1.name
HAVING count(*) >= 1
replace the last 1 with whatever n you desire
Here's the sql fiddle if you want to play around with it: http://sqlfiddle.com/#!7/14217/4
Use an INNER join of table1 to table2 and then a LEFT join to table3 and count the corresponding ids of table3.
Then by a 2nd level of aggregation return only the rows of table1 where all the counters are at least 1:
SELECT id, name
FROM (
SELECT t1.id, t1.name, COUNT(t3.id) counter
FROM table1 t1
INNER JOIN table2 t2 ON t2.table1_id = t1.id
LEFT JOIN table3 t3 ON t3.table2_id = t2.id
GROUP BY t1.id, t1.name, t2.id
)
GROUP BY id, name
HAVING MIN(counter) >= 1 -- change to the number that you want
See the demo.
Results:
id
name
0
foo
1
etc

Union two tables with one different column

I have two almost identical tables except one column.
table1
id name amount
1 nm1 15
2 nm2 20
table1
id name amt
1 nm1 15
2 nm2 20
Now I have other joins but I want to avoid have it all twice but rather have more simple sql code.
At the moment I have to do all twice:
select t1.id, t1.name, t1.amount from table1 t1
left outer join.......
union all
select t2.id, t2.name, t2.amt as amount from table2 t2
left outer join.......
I would like to have something like:
select t1.id, t1.name, t1.amount from (select * from table1 union all select * from table2) t1
but this one column "amount" prevents it.
Is there a way how to handle it?
Do the union all before the join:
select t.id, t.name, t.amount, . . .
from (select t1.* from table1 t1 union all
select t2.* from table2 t2
) t left outer join.......

SQL grouping - throwing out old results

Some data would be organized thusly:
ID DATE COUNT1 COUNT2
A 20120101 1 2
A 20120201 2 2
B 20120101 3 0
C 20111201 1 0
C 20120301 2 2
Another table has ID NAME
A MYNAME
.... etc
i want to return a table of
ID NAME COUNT COUNT2
for the most recent available piece of data, i.e. the january count for A is not included
i know I need to use HAVING, INNER JOIN, and GROUP BY but every iteration I can come up with has an error.
If you only want rows with the date equal to the global maximum date, just use a subquery:
select ID,DATE,COUNT1,COUNT2
from table
where DATE=(select max(DATE) from table);
If you want the maximum date per ID, then you can use a self join:
select ID,MAX_DATE,COUNT1,COUNT2
from(
select ID,max(DATE) as MAX_DATE
from table
group by ID
)a
join(
select ID,DATE,COUNT1,COUNT2
from table
)b
on (a.ID=b.ID and a.MAX_DATE=b.DATE);
Not necessarily. This should also work:
select t1.id, t2.name, t1,count1, t1.count2
from table_1 t1 join table_2 t2 on (t1.id = t2.id)
where not exists (
select 1
from table_1 t3
where t1.id = t3.id
and t1.date < t3.date)
order by 1;
You'll need a correlated subquery:
SELECT Id, Name, Count1, Count2
FROM CountsTable AS T1 INNER JOIN NamesTable ON T1.Id=NamesTable.Id
WHERE CountsTable.Date = (
SELECT Max(Date) From CountsTable AS T2 WHERE T1.Id=T2.Id
)

Merge and add values from two tables

Is it possible to craft a query that adds values within two tables:
For example, say you have two tables
id value
-- -----
a 1
c 2
d 3
f 4
g 5
and
id value
-- -----
a 1
b 2
c 3
d 4
e 5
Then when you 'add' the two tables you would get the result where the id's match. So, a=1+1=2, and simply the same result where they don't. So the query would return:
id value
-- -----
a 2
b 2
c 5
d 7
e 5
f 4
g 5
maybe something like
select coalesce(t1.id, t2.id) as id, (coalesce(t1.value, 0) + coalesce(t2.value, 0)) as value
from table1 t1 full outer join table2 t2 on t1.id = t2.id
Use:
SELECT x.id,
SUM(x.value)
FROM (SELECT t.id,
t.value
FROM TABLE_1 t
UNION ALL
SELECT t2.id,
t2.value
FROM TABLE_2 t2) x
GROUP BY x.id
You could do it like this - but the other answers are probably swifter:
SELECT t1.id, t1.value + t2.value AS value
FROM t1 INNER JOIN t2 ON t1.id = t2.id
UNION
SELECT t1.id, t1.value
FROM t1
WHERE t1.id NOT IN (SELECT t2.id FROM t2)
UNION
SELECT t2.id, t2.value
FROM t2
WHERE t2.id NOT IN (SELECT t1.id FROM t1)
SELECT
COALESCE(t1.id, t2.id) AS id,
COALESCE(t1.value, 0) + COALESCE(t2.value, 0) AS value
FROM
t1
FULL OUTER JOIN
t2 ON t1.id = t2.id
OR
SELECT
foo.id,
COALESCE(t1.value, 0) + COALESCE(t2.value, 0) AS value
FROM
(
SELECT t1.id FROM t1
UNION
SELECT t2.id FROM t2
) foo
LEFT JOIN
t1 ON foo.id = t1.id
LEFT JOIN
t2 ON foo.id = t2.id
SELECT ISNULL(T1.Col1.T2.Col1) as ID, (ISNULL(T1.Col2,0) + ISNULL(T2.Col2,0)) as SumCols
FROM T1 OUTER JOIN T2 ON T1.Col1 = T2.Col2
No grouping or anything. It handles the following cases
if an id is in T1 but not in T2 you will get the value in T1 and vice versa. This handles bi-directional inclusion.
If an ID is in both you will get the sum

Need help with Join Problem

I have 2 tables
Table 1
ID Status
1 D
2 F
3 D
Table 2
SID ID Approve
1 1 N
2 1 Y
3 1 Y
4 2 Y
5 3 Y
Result:- Should be
Table 1 (ID, Status)
3, D
2,F
Should not display 1 (As one of child rows have N in the Approve Column)
I need a query to joins 2 tables on ID and finds records that don not have N in their Approve column. Does anyone have any clue how to implement this?
I tried
SELECT * FROM Table1 AS t1
INNER JOIN Table2 AS t2
ON t2.id = t1.id
WHERE t2.Approve != 'N'
Doesn’t work
SELECT *
FROM Table1 t1
WHERE NOT EXISTS(SELECT * FROM Table2 t2 WHERE t2.ID = t1.ID AND t2.Approve = 'N')
More efficient and possibly easier to read is the following:
SELECT * FROM Table1 AS t1
LEFT JOIN Table2 AS t2
ON t2.id = t1.id
group by t1.id HAVING sum(t2.approve='Y') = count(t1.id)
Please Try
SELECT distinct t1.*
FROM Table1 AS t1
INNER JOIN Table2 AS tyes
ON tyes.id = t1.id
AND tyes.approve ='Y'
LEFT OUTER JOIN Table2 as tno
ON tno.id = t1.id
AND tno.approve ='N'
where tno.sid is null
It will select any line which is explicitely approved and never disapproved
Select * from table1 where id not in (select id from table2 where approve = 'N')
ID 1 is still returned since there is also a record in table 2 where approve = 'Y' for ID 1.
If you want to exclude any ID where approve is 'N' for any SID then you'll have to work with subqueries; roughly:
SELECT t1.ID,T1.Status FROM Table1 AS t1 INNER JOIN Table2 AS t2 ON t2.id = t1.id
where t1.id NOT IN (select id from Table2 where approve = 'N' and id = t1.id)
regards,
Stijn