Select three tables and count - mysql

I have three tables, I need to select email and count the relation between table A and table B, for example:
Table A:
ID | email
1 | test#test
2 | test2#test
3 | test3#test
Table B:
UID | username
11 | James
22 | Gabriel
33 | Jonas
Table C: (Relation between A and B)
ID | email_id | username_id
1 | 1 | 11
2 | 1 | 22
3 | 2 | 33
Expect Result:
Email | Totalrelation
test#test | 2
test2#test | 1
I tried:
select tableA.email,
COUNT(distinct tableC.email_id) AS total from tableA as tableA, tableC as tableC GROUP BY tableC.email_id
But it didnt work, I got total wrong. How can I do it?

Join the tables, group the data and count the number for each group
select a.email, count(c.id) as cnt
from tableA a
left join tableC c on c.email_id = a.id
group by a.email

Related

How do I join multiple entries in one table into a single relationship status in MySQL?

I have a table Follow, which only holds records of which UserID follows which TargetID.
If asked for user A:
If neither A or B are following eachother, they have status of 0 for unrelated, and aren't included in the results.
If user A is following B but not vice versa, B has status 1 for
being followed.
If user B is following A but not vice versa, B has
status 2 for being a follower.
If A is following B, and B following
A, B has status of 3 for being a friend.
How can I, in a single MySQL query, get the relationship status for a given user and all its relationships above status 0?
Example:
Users:
+----+-------+
| id | Name |
+----+-------+
| 1 | Bob |
| 2 | Steve |
| 3 | Scott |
| 4 | Mary |
+----+-------+
Follow:
+----+--------+----------+
| id | UserID | TargetID |
+----+--------+----------+
| 1 | 1 | 2 |
| 2 | 1 | 3 |
| 3 | 2 | 1 |
| 4 | 4 | 1 |
+----+--------+----------+
Expected result for user 1:
+----------+--------+-------+
| TargetID | Status | Name |
+----------+--------+-------+
| 2 | 3 | Steve | (friend)
| 3 | 1 | Scott | (following)
| 4 | 2 | Mary | (follower)
+----------+--------+-------+
You can use subqueries as illustrated below:
-- FOR USER 1
SELECT A.id TargetID,
SUM(IFNULL((SELECT 1 C FROM Follow B WHERE B.UserID=1 AND B.TargetID=A.id),0) +
IFNULL((SELECT 2 C FROM Follow D WHERE A.id=D.UserID AND D.TargetID=1), 0)) Status
, A.name
FROM (SELECT * FROM Users WHERE ID<>1) A
GROUP BY A.id, A.Name
HAVING Status>0; -- for a compact result
-- NOW GLOBALLY
SELECT A.UserID, A.id TargetID,
SUM(IFNULL((SELECT 1 C FROM Follow B WHERE B.UserID=A.UserID AND B.TargetID=A.id),0) +
IFNULL((SELECT 2 C FROM Follow D WHERE A.id=D.UserID AND D.TargetID=A.UserID), 0)) Status
, A.name
FROM (SELECT E.id UserID, F.* FROM Users E JOIN Users F ON E.id<>F.id) A
GROUP BY A.UserID, A.id, A.Name
HAVING Status>0 -- for a compact result
ORDER BY A.UserID;
See DEMO on SQL Fiddle
I have not tried this but try something among the lines of:
Select t.targetid as TargetId,
IF (
(select count(id) from follow where
follow.Userid = f.target.id and follow.target_id = u.id) > 1,
-- mean’s the target is following user 1
(IF (
(select count(id) from follow where
follow.Userid = u.id and follow.target_id = f.targetid) > 1, 3, 2))
-- if user1 is following aswell, then its a friend, else its a follower
, 1)
-- else means its a following
as status,
u.name as Name from follow f
inner Join users u on u.id = f.targetid
where u.id = 1
Inner join to select user 1's relations (if it doesn't exist, they aren't related)
If there is a record, means they are one of 3:

how to perform an outer join in mysql

I have a table A that contains tree columns, id, users ids and vehicle id. And a table B that contains vehicleid, and vehicle name.
Table A
---------------------------
| Id | User_id |Vehicle_id|
---------------------------
| 1 | 1 | 2 |
| 2 | 1 | 3 |
| 3 | 1 | 4 |
| 4 | 2 | 2 |
| 5 | 2 | 3 |
| 6 | 4 | 5 |
---------------------------
Table B
-------------------
| Id |Vehicle_name|
-------------------
| 1 | Car |
| 2 | Bike |
| 3 | Plane |
| 4 | Boat |
| 5 | Rocket |
-------------------
Given a user id, I need to get all vehicle names, that doesn't match with table A. I've tried Outer joins, but I can't manage to do get the info that i need.
For example: Given user id 1, the query should return Car and Rocket.
thanks in advance
This is simple enough using not in or not exists:
select b.*
from b
where not exists (select 1
from a
where a.vehicle_id = b.id and a.user_id = #a_user_id
);
I also thought of using a cross join and was able to get the output in case you are more comfortable with join logic.
SELECT CJOIN.USER_ID, CJOIN.VEHICLE_ID, CJOIN.VEHICLE_NAME
FROM
(SELECT DISTINCT A.USER_ID, B.ID AS VEHICLE_ID, B.VEHICLE_NAME FROM TABLE_A A CROSS JOIN TABLE_B B) CJOIN
LEFT JOIN
TABLE_A D
ON CJOIN.USER_ID = D.USER_ID AND CJOIN.VEHICLE_ID = D.VEHICLE_ID
WHERE D.USER_ID IS NULL AND D.VEHICLE_ID IS NULL;
First, I got all possible combinations of USER_ID x VEHICLE_ID by a cross join and used this table in a left join to pull records for which there is no match.

SQL select distinct where exist row for each id in other table

I have table A and B with a relationship: A n<->1 B relationship.
They are joined over field A.b = B.id, where B.id is unique
I have a parameter which is a bunch of ids of B.
I want to get distinct A.id that have all given B.ids assigned.
Example:
Table B
| id | ...
| 1 |
| 2 |
| 3 |
Table A
| id | b | ...
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
| 2 | 2 |
<-- id=2 is not assigned to b=3 !
| 3 | 1 |
| 3 | 2 |
| 3 | 3 |
Expected result for parameter B.ids="1,2,3": 1, 3 (2 misses the required B.id=3)
How can I do this?
You can do this with aggregation and a having clause:
select id
from tableA a join
tableB b
on a.b = b.id
group by id
having count(distinct b) = (select count(distinct b) from tableB);
Note that this can possibly be simplified with some assumptions. For instance, if you know the b ids are unique, then you don't need the count(distinct) (count() is then sufficient.)
EDIT:
If you want a list of ids that you want to check, you can use:
select id
from tableA a
where find_in_set(a.b, IDLISTHERE) > 0
group by id
having count(distinct b) = (select count(distinct b) from tableB where find_in_set(a.b, IDLISTHERE) > 0);
select id from tableA a join tableB b on a.b = b.id
group by id
having count(distinct b) = (select count(distinct b) from tableB);

Count multiple columns with same WHERE condition

I have a users table with columns: user_id, teacher_id1, teacher_id2, teacher_id3
and
teachers table with id
Each user can have the same id's for teacher_id1, teacher_id2, teacher_id3
I would like to count how many users have same teacher.
User table
+----------------------------------------+
| user_Id teacher_id1 teacher_id2 teacher_id3 |
+----------------------------------------+
| 1 1 1 1 |
| 2 2 1 3 |
| 3 2 3 3 |
| 4 2 2 2 |
+----------------------------------------+
Teacher table
+------+
| id |
+------+
| 1 |
| 2 |
| 3 |
Count for $id1 is: 2
Count for $id2 is: 3
Count for $id3 is: 2
I tried something like this, but it is not correct!
SELECT COUNT(*) FROM users WHERE concat_ws('',teacher_id1 OR teacher_id2
OR teacher_id3) like '{$id}' ";
You have data in three different columns. You need to combine it into one column, to get the distinct counts that you want. For this, you can use union all. Then the count is simply count(distinct):
select teacher_id, COUNT(distinct USER_ID)
from ((select user_id, teacher_id1 as teacher_id
from t
) union all
(select user_id, teacher_id2
from t
) union all
(select user_id, teacher_id3
from t
)
) s
group by teacher_id;
Try this query
select b.id, count(*)
from
tbl1 a
inner join
tbl2 b
on b.id = teacher_id1 or b.id = teacher_id2 or b.id = teacher_id3
group by b.id
SQL FIDDLE:
| ID | COUNT(*) |
-----------------
| 1 | 2 |
| 2 | 3 |
| 3 | 2 |

How to join this data in MySQL?

If I have a table like this:
TABLEA:
id| object | type
1 | greg | person
2 | mary | person
3 | jared | person
4 | kelly | person
5 | melissa | person
6 | william | person
7 | skiing | hobby
8 | biking | hobby
TABLEB:
id | husband | wife
1 | greg | mary
2 | jared | kelly
3 | william | kelly
TABLEC:
id | female | hobby
1 | mary | skiing
2 | kelly | biking
Is there some way I could get a result table of:
TABLED:
id | a | b | link
1 | 1 | 2 | related
2 | 3 | 4 | related
3 | 6 | 4 | related
4 | 1 | 4 | related
5 | 2 | 7 | likes
6 | 4 | 8 | likes
Using only MySQL query(/ies)?
Logic would basically start iterating from TABLE B and all rows of table B.
The third column is related when the table being selected is TableB, and is likes when the table being processed is TABLE B.
Sample logic would be:
Looking at the first row of TABLEB (husband) greg and wife (mary), it looks up from TABLEA to see that greg is at row 1 (id 1) and mary is at row 2 (id 2) and creates a new TableD with the first row being 1 | 2.
Is there a query that can do some join or something that would be able to do this without having to programmatically iterate through all rows of TABLEB, then all rows of TABLEC to produce the desired TABLED?
You will need to JOIN the table tablea two times with the tableb:
one for husband > object and another,
one for wife > object.
So that you can get the two ids of the husband and wife in the same row.
The same with Tablec with tablea. Then use UNION(implicit distinct) or UNION ALL to union the two result sets.
Something like:
SELECT
(#rownum := #rownum + 1) AS id,
sub.*
FROM
(
SELECT
ah.id AS a,
aw.id AS b,
'related' AS link
FROM tableb AS b
INNER JOIN Tablea AS ah ON ah.object = b.husband AND ah.type = 'person'
INNER JOIN Tablea AS aw ON aw.object = b.wife AND aw.type = 'person'
UNION ALL
SELECT
a.id AS a,
a2.id AS b,
'hobby'
FROM tablec AS c
INNER JOIN tablea AS a ON a.object = c.female
INNER JOIN tablea AS a2 ON a2.object = c.hobby AND a2.type = 'hobby'
) AS Sub, (SELECT #rownum := 0) AS t;
SQL Fiddle Demo
This will give you:
| ID | A | B | LINK |
------------------------
| 1 | 1 | 2 | related |
| 2 | 3 | 4 | related |
| 3 | 6 | 4 | related |
| 4 | 2 | 7 | hobby |
| 5 | 4 | 8 | hobby |
Note that:
This query will give you only 5 rows, where the expected result set that you are looking for is 6. It is missing the row 4 | 1 | 4 | related, because there is no entry for those greg(id 1) and keyll (id 4) in the tableb. As you explained in your question.
The new column id is auto incremental id, generated from the result set, it is not selected from the tables.
If you need to create a brand new table from this select, use the following syntax:
CREATE TABLE Tabled
AS
SELECT ...
... -- the same previous select query
and you will have a new table tabled having the same structure of this SELECT.
Like in this updated fiddle.
create table tabled (
id int auto_increment,
a varchar(10),
b varchar(10),
link varchar(10));
insert into tabled (a, b, link)
select a1.id a, a2.id b, 'related' link
from tablea a1
join tableb b on a1.object = b.husband and a1.type = 'person'
join tablea a2 on a2.object = b.wife and a2.type = 'person'
union all
select a1.id a, a2.id b, 'likes' link
from tablea a1
join tablec c on a1.object = c.female and a1.type = 'person'
join tablea a2 on a2.object = c.hobby and a2.type = 'hobby';