SQL Get Count Across 3 Tables - sql

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

Related

SQL Select all first items in a list of rows identified by Ids and filtered by a specific Type in another table

I need to create a table keyed by an ID where the values of one of the columns in the new table are the earliest values entered into the column of another table where the rows share the same ID and have a specific type label.
For example, say I want the Name and first Value entered for each fruit with an entry type A:
These are the tables I have:
TABLE1
Key
ID
Name
1
1
Cherry
2
2
Grape
TABLE2
Key
ID
Value
EntryNum
EntryType
1
1
21
1
A
2
1
32
2
B
3
1
4
3
B
4
1
15
4
A
5
2
3
1
B
6
2
8
2
A
7
2
16
3
B
And this is the result that I want:
TABLE3
ID
Name
EarliestEntry
1
Cherry
21
2
Grape
8
I've attempted the following query but it just returns the same value for all EarliestEntry:
SELECT TABLE1.ID, TABLE2.Name,
(SELECT Value FROM (SELECT ROW_NUMBER() OVER (ORDER BY TABLE2.EntryNum)
as row_num, Value FROM TABLE2
WHERE TABLE2.ID = TABLE1.ID AND TABLE2.EntryType = 'A')
AS sub
WHERE row_num = 1) AS EarliestEntry
INTO TABLE3
FROM TABLE2
INNER JOIN TABLE1 ON TABLE1.ID = TABLE2.ID
GROUP BY TABLE1.ID, TABLE2.Type, TABLE2.EntryNum
I would greatly appreciate help on this. Thank you
If you wanted to use the ROW_NUMBER function then you would need to put that on TABLE1 and add a partition by like so:
WITH rn AS(
SELECT a.Key, ROW_NUMBER() OVER(PARTITION BY a.ID ORDER BY a.EntryNum) AS rn
FROM TABLE2 AS a
)
SELECT b.Name, a.Value AS EarliestValue
FROM TABLE2 AS a
INNER JOIN TABLE1 AS b ON b.ID = a.ID
INNER JOIN rn AS rn ON rn.key = a.key
WHERE rn.rn = 1
In your example you skipped the PARTITION BY clause so you just get a number for all values in TABLE2. Instead of a number per ID in ascending order for Value.
Based on your description of the three tables TABLE1, TABLE2 and TABLE3.
I modified a little bit your script. Thank of Dale K remark, I explain in some words the solution : the field TABLE2.Name shown in the first select was wrong, because [name] belongs to TABLE1, so the right syntax for this is TABLE1.name. And in the GROUP BY clause the field TABLE2.Type might be replaced by TABLE1.name to repect aggregation criteria. So the script becomes :
SELECT DISTINCT table1.id, table1.name,
(SELECT Value FROM (SELECT ROW_NUMBER() OVER (ORDER BY table2.EntryNum)
as row_num, Value FROM table2
WHERE table2.id = table1.id AND table2.EntryType = 'A')
AS sub
WHERE row_num = 1) AS EarliestEntry
INTO table3
FROM table2
INNER JOIN table1 ON table1.id = table2.id
GROUP BY table1.id, table1.name, table2.entrynum;
Here, you can verify the output with fiddle
You are hugely over-complicating this.
Just partition Table2 and take a row-number, then join that to Table1 and filter on only row-number 1
SELECT
t1.Id,
t1.Name,
EarliestEntry = t2.Value
FROM Table1 t1
JOIN (
SELECT *,
rn = ROW_NUMBER() OVER (PARTITION BY t2.ID ORDER BY t2.EntryNum)
FROM Table2 t2
WHERE t2.EntryType = 'A'
) t2 ON t2.ID = t1.ID AND t2.rn = 1;
db<>fiddle

Combine 2 tables in SQL including uncommon fields

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.

Oracle Sql - I have two tables and need to filter table1 with results from table2. I need to return all of table1 if table2 is empty

I'm currently turning a comma-separated string into a number table with the field name of ID. I'm then trying to do an nvl to select all if the generated table is null.
table1.ID = NVL(table2.ID, table1.ID)
I have two tables and need to filter table1 with results from table2. I need to return all of table1 if table2 is empty.
Scenario I
Table1
ID
1
2
3
4
Table2 (Empty)
ID
Return rows 1, 2, 3, 4
Scenario II
Table1
ID
1
2
3
4
Table2
ID
2
3
Return rows 2, 3
You can use filtering in the where clause:
select t1.id
from table1 t1
where not exists (select 1 from table2) or
exists (select 1 from table2 t2 where t2.id = t1.id);
I don't think join is the right way to express this logic.
You can also use UNION
select t1.id
from table1 t1
where not exists (select 1 from table2 where id = t1.id) union all
select t2.id
from table2 t2
where exist (select 1 from table1 where id = t2.id);

Multiple SELECT with NOT IN - inner Select statement returns empty and the whole SELECT returns empty

There are two table TABLE1 and TABLE2 in which there is a common field ID. I wanted to retrieve values from TABLE2 that doesnot match in TABLE1 based on ID value.
select * from TABLE2 where subject = 1 and ID NOT IN (select ID from TABLE1 where subject = 1)
Sample:
TABLE1 ID SUBJECT 1 1
TABLE2 ID SUBJECT 1 1 2 1
The expected result is 2 and it works fine.
But when TABLE1 is empty or the inner select ID from TABLE1 where subject = 1 returns empty, the whole select statement returns empty.
But the expected result is 1, 2
Is there any way to achieve this ?
Use a left join
select t2.*
from table2 t2
left outer join table1 t1 on t1.id = t2.id and t1.subject = 1
where t2.subject = 1
and t1.id is null
See a good explanation of joins
I think you can use not exists also for this work -
select * from TABLE2 where subject = 1 and NOT exists
(select 1 from TABLE1 where subject = 1 and table1.id = table2.id)

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