SQL count rows in third table - mysql

A row in Table A can be linked to many rows in Table B. A row in Table B will be linked to either one or zero rows in Table C.
For a given row in Table A, I would like to count the (indirectly) linked rows in Table C.
I would like to return this for each row in Table A. The below doesn't give any errors, but doesn't give the correct value.
SELECT
*,
(
SELECT count(*)
FROM TABLE_A
INNER JOIN TABLE_B ON TABLE_A.id = TABLE_B.foreignKeyA
INNER JOIN TABLE_C ON TABLE_B.id = TABLE_C.foreignKeyB
) as cCount
FROM TABLE_A
Sample data:
TABLE_A
id
1
2
TABLE_B
id foreignKeyA
1 1
2 1
3 2
4 2
TABLE_C
id foreignKeyB
1 3
2 4
Should return (for the rows of Table A):
id cCount
1 0
2 2

I would suggest a correlated subquery:
SELECT a.*,
(SELECT count(*)
FROM TABLE_B b JOIN
TABLE_C c
ON c.foreignKeyB = b.id
WHERE b.foreignKeyA = a.id
) as aCount
FROM TABLE_A a;
Obviously, if you want the count for each row in TABLE_C, then you would adjust the table names and conditions.
You can do this with JOINs, but you need outer joins and an overall aggregation.

> with table_count as (
Select count(*) as cnt, TableB_FK
From TableB B
JOIN TableA A on A.FK = B.FK
Group By B.FK
)
select C.*, nvl(t.cnt,0)
from TableC C
left join table_count T on T.tableB_fk = C.FK
Table_count has aggregate count for foreign Keys.
Left Join table C with table count and replace null with 0

Change the INNER joins to LEFT joins for the case there are not any linked rows to TABLE_C, in which case you want 0 as result:
SELECT TABLE_A.id, count(TABLE_C.id) cCount
FROM TABLE_A
LEFT JOIN TABLE_B ON TABLE_B.foreignKeyA = TABLE_A.id
LEFT JOIN TABLE_C ON TABLE_C.foreignKeyB = TABLE_B.id
GROUP BY TABLE_A.id
See the demo.
Results:
| id | cCount |
| --- | ------ |
| 1 | 0 |
| 2 | 2 |

Related

How to left join when only matched one record from right table

I want to know How to left join when only matched one record from right table.
For example,
tableA
id
value
1
34
2
42
3
60
tableB
id
value
tableA_id
1
20
1
2
31
1
3
50
2
I want to get result like below using left outer join.
tableA_id
tableA_value
tableB_value
1
34
null
2
42
50
3
60
null
tableB_value of first row is null, because tableA.id = tableB.tableA_id matches multiple records.
how to solve it ?
Thanks.
You can make use of COUNT() as an analytic function to keep track of how many times a tableA_id occurs in the A table:
SELECT a.id AS tableA_id, a.value AS tableA_value, b.value AS tableB_value
FROM tableA a
LEFT JOIN
(
SELECT *, COUNT(*) OVER (PARTITION BY tableA_id) cnt
FROM tableB
) b
ON a.id = b.tableA_id AND b.cnt = 1
ORDER BY a.id;
Demo
You can do this using aggregation on the b table as well:
SELECT a.id AS tableA_id, a.value AS tableA_value, b.value AS tableB_value
FROM tableA a LEFT JOIN
(SELECT tableA_id, MAX(value) as value
FROM tableB
GROUP BY tableA_id
HAVING COUNT(*) = 1
) b
ON a.id = b.tableA_id
ORDER BY a.id;
This works because if there is only one row in B for a given id, then MAX() returns the value on that row.

Joining two tables with aggregrate functions

I have three tables I need to combine. My first table(Foreign table) has the id's for literally all objects. Table A has people upvote id's from Foreign Table and Table B has people downvote id's from Foreign Table.
I want to combine these 3 tables into 1 big table with counts in them. See the result table. Count table A refers to the amount of times the specific id is present in table A. How do I combine these to the final table?
Foreign Table
id
---
1
2
3
...
Table A
id | foreign_id
-----------------------
1 3
2 3
3 1
Table B
id | foreign_id
-----------------------
1 3
2 1
3 2
Result Table
id | count table A | count table B
------------------------------------
1 1 1
2 0 1
3 2 1
Probably the fastest method is a a correlated subquery:
select f.*,
(select count(*) from a where a.foreign_id = f.id) as a_cnt,
(select count(*) from b where b.foreign_id = f.id) as b_cnt
from foreign f;
This avoids the GROUP BY on the outer query. As a bonus it can use an index on a(foreign_id) and b(foreign_id) which also benefit performance.
for manage properly the 0 result you could use sum and check for null
select f.id
, sum( when a.foreign_id is null then 0 else 1 end ) count_a
, sum( when b.foreign_id is null then 0 else 1 end ) count_b
from Foreign f
LEFT JOIN TableA a f.id = a.foreign_id
LEFT JOIN TableA a f.id = b.foreign_id
GROUP BY f.id
You can join, aggregate by the primary key of the foreign table and count distinct values of the primary keys of table_a and table_b:
select
f.id,
count(distinct a.id) count_table_a,
count(distinct b.id) count_table_b
from foreign_table f
left join table_a a on a.foreign_id = f.id
left join table_b b on b.foreign_id = f.id
group by f.id
count() returns 0 where there is no match, so there is no need for special processing unlike when suming.
Demo on DB Fiddle:
id | count_table_a | count_table_b
-: | ------------: | ------------:
1 | 1 | 1
2 | 0 | 1
3 | 2 | 1
I guess below would work
Select f.id, count(a.*),count(b.*) from
foreign f
Left Join
tableA a on f.id=a.foreignid
Join
TableB b on f.id=b.foreignid
Group by f.id
Or
Select id, * from foreign f left join
(
Select foreignid, count(*) from tableA a
group
by foreignid join
Select foreignid, count(*) from tableB b
group
by foreignid on
a.foreignid<b.foreignid) t on
f.id=t.foreignid;

Two right joins

I am trying to select all records in TABLEC and its equivalent value in TABLEA or TABLEB using right join. I am using MYSQL 5.5.47.
--Table data as follows
TABLEA TABLEB TABLEC
ID FNAME ID MNAME ID LNAME
0 ANOOP 0 N 0 SINGH
1 BIMA 2 SITA 3 RAJ
4 CIMI 4 B 5 KUMAR
6 RAVI 5 A 6 D
--Using below query and trying to select all records in TABLEC and its equivalent value in TABLEA or TABLEB
SELECT A.FNAME, B.MNAME, C.LNAME
FROM TABLEA AS A
RIGHT JOIN TABLEB AS B ON A.ID = B.ID
RIGHT JOIN TABLEC AS C ON C.ID = B.ID
--I am getting the following result
ANOOP N SINGH
NULL NULL RAJ
NULL A KUMAR
***NULL*** NULL D
The highlighted value doesn’t show the value as 'RAVI' instead it shows NULL in MYSQL 5.5.47. I tried to modify the '=' condition in second join related to C & A but still no luck. What am I doing wrong here? How do I get the value 'RAVI' in place of NULL? Any suggestion would be highly helpful.
DROP TABLE IF EXISTS table_a;
DROP TABLE IF EXISTS table_b;
DROP TABLE IF EXISTS table_c;
CREATE TABLE table_a
(id INT NOT NULL PRIMARY KEY
,fname VARCHAR(12) NULL
);
INSERT INTO table_a VALUES
(0,'ANOOP'),
(1,'BIMA'),
(4,'CIMI'),
(6,'RAVI');
CREATE TABLE table_b
(id INT NOT NULL PRIMARY KEY
,mname VARCHAR(12) NULL
);
INSERT INTO table_b VALUES
(0,'N'),
(2,'SITA'),
(4,'B'),
(5,'A');
CREATE TABLE table_c
(id INT NOT NULL PRIMARY KEY
,lname VARCHAR(12) NULL
);
INSERT INTO table_c VALUES
(0,'SINGH'),
(3,'RAJ'),
(5,'KUMAR'),
(6,'D');
SELECT a.fname
, b.mname
, c.lname
FROM table_c c
LEFT
JOIN table_a a
ON a.id = c.id
LEFT
JOIN table_b b
ON b.id = c.id;
+-------+-------+-------+
| fname | mname | lname |
+-------+-------+-------+
| ANOOP | N | SINGH |
| NULL | NULL | RAJ |
| NULL | A | KUMAR |
| RAVI | NULL | D |
+-------+-------+-------+
4 rows in set (0.02 sec)
You are trying to select all records in TABLEC and its equivalent value in TABLEA or TABLEB using right join. So Table A and B is joined to Table c records. So we need to use Left join(you will get all records of Table C and common records of Table A and B). More info please ref this link
SELECT
ifnull(A.FNAME,""),
ifnull(B.MNAME,""),
ifnull(C.LNAME,"")
FROM
TABLEA AS A
LEFT JOIN
TABLEB AS B
ON
A.ID = B.ID
LEFT JOIN
TABLEC AS C
ON
C.ID = B.ID
The problem is you don't have a table that contains all the ids. So you have to make one. Then you can join from that.
Get all the ids with this query
SELECT ID FROM TABLEA
UNION
SELECT ID FROM TABLEB
UNION
SELECT ID FROM TABLEC
Now we can use this query/table of ids to join the others
SELECT A.FNAME, B.MNAME, C.LNAME
FROM (
SELECT ID FROM TABLEA
UNION
SELECT ID FROM TABLEB
UNION
SELECT ID FROM TABLEC
) I
LEFT JOIN TABLEA A ON I.ID = A.ID
LEFT JOIN TABLEB B ON I.ID = B.ID
LEFT JOIN TABLEC C ON I.ID = C.ID
Of course if you had another table (TABLEID) that had a list of all IDs you could use that instead of the sub-query above. It might be your model has such a table, but we won't know unless you tell us.
As you said "select all records in TABLEC and its equivalent value in TABLEA or TABLEB", so you need to join the C with A and C with B. So your need to update your query as :
SELECT A.FNAME, B.MNAME, C.LNAME
FROM TABLEC AS C RIGHT JOIN TABLEB AS B
ON B.ID = C.ID
RIGHT JOIN TABLEA AS A
ON C.ID = A.ID
If you want all the record that exist in A, B and C. The NULL value will shown for the record which doesn't have the value,
SELECT A.FNAME, B.MNAME, C.LNAME
FROM (
TABLEA AS A
LEFT JOIN TABLEB AS B ON B.ID = A.ID
)
RIGHT JOIN TABLEC AS C ON ( C.ID = B.ID
OR B.ID = NULL
OR A.ID = C.ID )
WHERE 1
Use this works perfectly
SELECT A.FNAME, B.MNAME, C.LNAME
FROM TABLEC AS C
LEFT JOIN TABLEA AS A ON (A.ID = C.ID)
LEFT JOIN TABLEB AS B ON (B.ID = C.ID)

Counting all childs and sub-childs of each node in a hierarchy

I have this hierarchy of tables :
Table A
|
| Table B
|
| Table C
|
| Table D
For a given row in Table A, let's say the row with ID=1, I want to get this output :
ID | childB | ChildC | childD
-------------------------------
1 | x | x | x
Where childB is the number of children in Table B, ChildC are the children in Table C of the children found in Table B..., etc.
I want to get this output by one sql query. Now I can get only the counting of the children in Table B using this query :
SELECT a.ID, (SELECT
COUNT(b.parentID)
FROM TableB AS b
WHERE b.parentID= a.ID)
AS childB
FROM TableA a
WHERE a.ID =1
if you want it for a specific ID (as you mentioned for example ID=1) you can left join them all on idparent and id and use count(distinct):
select a.ID,
count(distinct b.id) childB,
count(distinct c.id) childC,
count(distinct d.id) childD
from tableA a
left join tableB b on b.parentID = a.ID
left join tableC c on c.parentID = b.ID
left join tableD d on d.parentID = c.ID
where a.ID=1
group by a.ID;
here is a fiddle DEMO.

Count rows that have the same set of related records

This is probably pretty simple but I can't figure this out. Here are the tables I'm working with.
table_a
id other_data
-------------
1 blah
2 foo
3 bar
table_b
ref_a ref_c
-------------
1 1
1 2
2 3
3 3
table_c
id name
----------
1 TestA
2 TestB
3 TestC
What I'm trying to get is something like this where I'm counting the number of rows (table_a) that have the same set of children (table_b). I also want to be able to get related data from another table (The name from table_c).
TestA,TestB 1
TestC 2
I know it probably uses Group By and GROUP_CONCAT but I can't get this to work.
I tried this but it doesn't work.
SELECT GROUP_CONCAT(DISTINCT table_c.name separator ', ') as 'combo_text', COUNT(DISTINCT table_a.id)
FROM table_a
INNER JOIN table_b
on table_a.id = table_b.ref_a
INNER JOIN table_c
on table_c.id = table_b.ref_c
GROUP BY table_b.ref_a
SELECT a.id, count(a.id) as count, GROUP_CONCAT(name) as names
FROM table_a a
JOIN table_b b ON (a.id = b.ref_a)
JOIN table_c c ON (b.ref_c = c.id)
GROUP BY a.id
sqlFiddle demo
based on your result you want something like this
SELECT names, count(count) as count FROM
(SELECT a.id, count(a.id) as count, GROUP_CONCAT(name) as names
FROM table_a a
JOIN table_b b ON (a.id = b.ref_a)
JOIN table_c c ON (b.ref_c = c.id)
GROUP BY a.id
)T1
GROUP BY names
sqlFiddle demo