Sql query with 2 join and count numbers and group by - mysql

I want to use an join to list the car colors count, car type, and users name.
I have 3 table
Table 1 Useres
id|username|fullname
1 | test0 | xy xy
2 | test1 | yx yx
Table 2 Car Type
id|car_type|user_id
1 | Ford | 1
2 | BMW | 2
3 | Ford | 1
4 | Skoda | 1
5 | BMW | 2
Table 3 Car Color
id| Color |user_id|car_id
1 | Red | 1 |1
2 | Blue | 2 |2
3 | Red | 2 |5
4 | Red | 1 |3
5 | Red | 1 |4
6 | Green | 1 |4
One car has 2 color
The result should be:
countType | CountColor | UserName
3 | 4 | test0
2 | 2 | test1
I tryed this:
SELECT
test as BlogsPost,
test2 as CommenstPost,
u.name
FROM users u
LEFT JOIN (
select COUNT(blogs.user_id) as test FROM blogs GROUP by blogs.user_id) blogs
on blogs.user_id=u.id
LEFT JOIN (
select COUNT(comments.user_id) as test2 FROM comments GROUP by comments.user_id) comments
on comments.user_id=u.id
GROUP by users.id

If I understand your question correctly with reference to your actual code section what you want is a list of users with how many blogs they have and how many comments they have. Now if you were wanting to count one matching table you could just do this:
SELECT
U.NAME
,COUNT(1) AS BLOG_COUNT
FROM USERS U
LEFT JOIN BLOGS B
ON B.USER_ID = U.ID
GROUP BY U.NAME
But since you are wanting to count two tables you have to do it slightly differently. There's a few ways of doing it but the way I like is like this:
SELECT
U.NAME
,B.BB_COUNT AS BLOG_COUNT
,C.CC_COUNT AS COMMENT_COUNT
FROM USERS U
LEFT JOIN
(
SELECT
BB.USER_ID
,COUNT(1) AS BB_COUNT
FROM BLOGS BB
GROUP BY BB.USER_ID
) B
ON B.USER_ID = U.ID
LEFT JOIN
(
SELECT
CC.USER_ID
,COUNT(1) AS CC_COUNT
FROM COMMENTS CC
GROUP BY CC.USER_ID
) C
ON C.USER_ID = U.ID
That may or may not be the most efficient way but in my experience it works pretty well and it's simple to understand. It all depends a lot on the number of rows in the tables and indexes etc. Usually the idea is to narrow down rows returned as fast as possible. In this case you'll have two sub queries but they'll end up with only as many rows as you have users basically.
Another thing to note, this will return all users, period. That may not be what you want. You might want only a subset of users. If so this inner select may not be the most efficient because you're doing calculations on users that may not be in the final result, wasting time. However I may be getting off topic.

I agree with the comment that states the table design is not really well constructed yet for you to achieve the counts you want you will need to do subqueries like this:
SELECT
(SELECT count(1) from CarType where user_id=username) as countType,
(SELECT count(1) from CarColor where user_id=username) as countColor,
username from (
SELECT username from Users
) a
As a suggestion for design:
Table Users
Table Cars
Table Colors
Then you have a Relationship table where you have user_id, car_id, color_id
This would be the proper table design for this structure

Related

Writing complex MySQL Join query

This may be simple for some, but I cannot work it out.
I have 3 tables:
Teams, Users, Tags
Teams Users Tags
------------------- ------------------ -----------------------
userID | teamUserID userID | username userID | name | value
------------------- ------------------ -----------------------
1 | 2 1 | dan 2 | myTag | 1
1 | 3 2 | bob 2 | aTag | 2
1 | 4 3 | jon 3 | bTag | 1
4 | rob 4 | cTag | 5
Each team can have a number of users in it, and each user can own a number of tags.
I need a query which will provide a list of users in any given team, with a total number of tags they have.
So when I request results from team 1 (dan's team) it should return this:
-----------------------------------
userID | username | tagTotalValue
-----------------------------------
2 | bob | 3
3 | jon | 1
4 | rob | 5
I have this query so far, but it just gives me one record with an overall total for the whole team, rather than a list of all the users in the team separately with their totals.
SELECT username, SUM(value) tagTotalValue
FROM users u LEFT JOIN tags t
ON u.userID = t.userID
WHERE u.userID IN (SELECT teamUserID FROM teams WHERE userID = 1)
Help!
If anyone can explain a good way of working out how to build these queries, I would be very grateful to learn. Do I just need to do a mySQL course, or is there a simple method I can employ?
I need a query which will provide a list of users in any given team,
with a total number of tags they have.
This seems to have little to do with the query you have written. You should start by joining the three tables together and then aggregating. The query looks something like this:
SELECT t.teamId, u.userId, u.username, count(ta.userId) as numTags
FROM teams t JOIN
users u
ON t.teamUserID = u.UserId LEFT JOIN
tags ta
ON u.userID = ta.userID
WHERE t.teamId = #teamId -- this can be removed
GROUP BY t.teamId, u.userId, u.username;
This query makes the leap that teams has a column that identifies the team -- say teamId.

Mysql finding results not present in jointable

I've got a mysql question that I haven't been able to figure out. Its a little bit different than the other questions I've found here on SO.
I've got three tables
users
____________
ID name
1 John
2 Mike
3 Susie
tasks
___________
ID Name
1 Brush Teeth
2 Shower
3 Check Email
users_tasks
_____________________
ID user_id task_id
1 1 1
2 1 2
3 1 3
4 2 1
5 2 2
6 3 1
Im trying to find out what users haven't completed what tasks yet. I would like the result set to look like this:
user_id task_id
__________________
2 3
3 2
3 3
The closest I have come is this query, which only gives me users that haven't completed at least one of the tasks, but doesn't give me the task.
select * FROM users
right JOIN users_tasks on users.id = users_tasks.user_id
right JOIN tasks on users_tasks.task_id = tasks.id
where tasks.id is null
I cant figure out how to return duplicate users and tasks based on what is missing form the join table.
Thanks in advance
An easy solution is just to require that the entry is not in your completed tasks table:
select * from users, tasks
where not exists (
select * from users_tasks
where users.id = users_tasks.user_id and tasks.id = users_tasks.task_id
);
Result:
+------+-------+------+-------------+
| id | name | id | name |
+------+-------+------+-------------+
| 3 | susie | 2 | Shower |
| 2 | mike | 3 | Check Email |
| 3 | susie | 3 | Check Email |
+------+-------+------+-------------+
One way to do this is to create a set representing the cartesian product of the users and tasks tables. This is what would be the result if every user had done every task. Then do a left join with the actual users_tasks table and filter for null to get the missing items:
select sub.*
from (
select u.id user_id, t.id task_id
from users u, tasks t
) sub
left join users_tasks ut on sub.user_id = ut.user_id and sub.task_id = ut.task_id
where ut.ID is null;

MySQL selective GROUP BY, using the maximal value

I have the following (simplified) three tables:
user_reservations:
id | user_id |
1 | 3 |
1 | 3 |
user_kar:
id | user_id | szak_id |
1 | 3 | 1 |
2 | 3 | 2 |
szak:
id | name |
1 | A |
2 | B |
Now I would like to count the reservations of the user by the 'szak' name, but I want to have every user counted only for one szak. In this case, user_id has 2 'szak', and if I write a query something like:
SELECT sz.name, COUNT(*) FROM user_reservations r
LEFT JOIN user_kar k ON k.user_id = r.user_id
LEFT JOIN szak s ON r.szak_id = r.id
It will return two rows:
A | 2 |
B | 2 |
However I want to every reservation counted to only one szak (lets say the highest id only). I tried MAX(k.id) with HAVING, but seems uneffective.
I would like to know if there is a supported method for that in MySQL, or should I first pick all the user ID-s on the backend site first, check their maximum kar.user_id, and then count only with those, removing them from the id list, when the given szak is counted, and then build the data back together on the backend side?
Thanks for the help - I was googling around for like 2 hours, but so far, I found no solution, so maybe you could help me.
Something like this?
SELECT sz.name,
Count(*)
FROM (SELECT r.user_id,
Ifnull(Max(k.szak_id), -1) AS max_szak_id
FROM user_reservations r
LEFT OUTER JOIN user_kar k
ON k.user_id = r.user_id
GROUP BY r.user_id) t
LEFT OUTER JOIN szak sz
ON sz.id = t.max_szak_id
GROUP BY sz.name;

Doing a LEFT JOIN with a default value if nothing matches

Firstly, I'm not even sure if LEFT JOIN is the best way of doing what I want to do, so will try best to explain.
I have 2 tables - one containing a list of sites, and another containing a list of roles. A user may just have one role, but they may have as many roles as there are sites.
Table 'sites'
site_id | site_name | site_domain
---------------------------------------
1 | Site A | sitea.com
2 | Site B | siteb.com
Table 'roles'
site_id | user_id | role_id
---------------------------------
1 | 1 | 1
1 | 2 | 1
2 | 2 | 2
As in my example above, User 1 has just one role on just one site, but User 2 has two roles on two different sites.
What I'd like to do, and it's probably just a Sunday night not thinking straight kinda thing, is to get a list of sites out, along with the role for each individual user - if no role exists for that site for a user, then return NULL (or 0).
Two examples of how the output should result:
User 1
site_id | site_name | site_domain | role_id
----------------------------------------------
1 | Site A | sitea.com | 1
2 | Site B | siteb.com | NULL
User 2
site_id | site_name | site_domain | role_id
----------------------------------------------
1 | Site A | sitea.com | 1
2 | Site B | siteb.com | 2
The query I have at the moment is below, however, the 2nd site does not appear since the user doesn't have a role defined (ie. like above, but for User 1 the 2nd Site B will not list). I know I'm just missing something but cannot think of it tonight :o(
SELECT site_id, site_name, site_domain, role_id
FROM `sites`
LEFT JOIN `roles`
ON sites.site_id=roles.site_id
WHERE roles.user_id='1'
What you need to do is start with a table that combines all sites and users. Then join in the information in the roles table:
select s.site_id, s.site_name, s.site_domain, u.user_id, r.role_id
from (select distinct user_id from roles) u cross join
sites s left outer join
roles r
on s.site_id = r.site_id and
u.user_id = r.user_id
If you have a users table, then you can replace the first subquery with that table.
For the sites where a single user is involved, you can just put a constant in the subquery:
select s.site_id, s.site_name, s.site_domain, u.user_id, r.role_id
from (select 1 as user_id) u cross join
sites s left outer join
roles r
on s.site_id = r.site_id and
u.user_id = r.user_id
Or a where clause: where u.user_id = 1.

MySQL: Limit Results related to FROM with Joins that have multiple subelements

I habe a main table that i select from and a table with subelements that i select from in a join. Example:
person skill person_to_skill
id | name id | skill id | p_id | s_id
------------ ------------ ----------------
1 | jim 1 | sewing 1 | 1 | 2
2 | peter 2 | cooking 2 | 2 | 1
3 | susan 3 | singing 3 | 2 | 3
4 | kevin 4 | 3 | 1
5 | 3 | 2
6 | 4 | 3
So now we see, sim has only one skill, peter has two and so forth.
Now if i select from person, koin skill and then also join person_to_skill, but i only want two persons. How do i manage to do so without grouping and thereby not getting all the skills?
Shortly: I want to select two persons from "person" with all their skills.
I tried just using LIMIT but that limits the result rows, not the persons.
If i use GROUP BY i only get one skill per person.
Is this possible without a subselect?
Any ideas anyone?
My Approach so far, changed to work with the example, looks like this:
SELECT p.id,p.name,s.skill
FROM person AS p
LEFT JOIN person_to_skill psk ON (psk.p_id = p.id)
LEFT JOIN skill s ON (s.id = psk.s_id)
ORDER BY p.name
LIMIT 0,2
Limit number of persons at very beginning in subquery then join to them other tables as you've already done:
SELECT p.id,p.name,s.skill
FROM (select * from person ORDER BY name LIMIT 0,2) AS p
LEFT JOIN person_to_skill psk ON (psk.p_id = p.id)
LEFT JOIN skill s ON (s.id = psk.s_id)
Added after comment:
If you really can't use subqueries you can do it using two queries. Firstly select users ids:
select id from person ORDER BY name LIMIT 0,2
and then use those ids in next query:
SELECT p.id,p.name,s.skill
FROM person p
LEFT JOIN person_to_skill psk ON (psk.p_id = p.id)
LEFT JOIN skill s ON (s.id = psk.s_id)
WHERE p.id IN (ids from previous query)
You can do something like
SELECT p.id, p.name, group_concat(s.skill separator ',')
and then group by person and limit the number of rows.