Collecting data from 3 tables into 1 query - mysql

I have three different tables in my MySQL database.
table users: (id, score, name)
table teams: (id, title)
table team_Members: (team_id, user_id)
What I would like to do is to have 1 query that finds every team ID a given user ID is member of, along with the following information:
total number of members in that team
the name of the team
users rank within the team (based on score)
EDIT:
Desired output should look like this;
TITLE (of the group) NUM_MEMBERS RANK
------------------------------------------------
Foo bar team 5 2
Another great group 34 17
.
.
.
The query should be based on the users ID.
Help is greatly appreciated

I think this query should get what you ask for
select t.id, t.title, count(m.user_id) members, (
select count(1)
from users u3
inner join team_Members m3 on u3.id = m3.user_id
and m3.team_id = t.id and u3.score > (
select score from users where name = 'Johan'
)
) + 1 score
from teams t
inner join team_Members m on t.id = m.team_id
where t.id in (
select team_id
from team_Members m2
inner join users u2 on m2.user_id = u2.id
where u2.name = 'Johan'
)
group by t.id, t.title

To collect you just need use JOIN
SELECT
u.*, t.*, tm.*
FROM
users u
JOIN
team_Members tm ON u.id = tm.user_id
JOIN
teams t ON t.id = tm.team_id;
To get total of number of that team use COUNT with some group key
Some like that
SELECT
COUNT(t.id), u.*, t.*, tm.*
FROM
users u
JOIN
team_Members tm ON u.id = tm.user_id
JOIN
teams t ON t.id = tm.team_id GROUP BY t.id;
And to rank just:
SELECT
COUNT(t.id) as number_of_members, u.*, t.*, tm.*
FROM
users u
JOIN
team_Members tm ON u.id = tm.user_id
JOIN
teams t ON t.id = tm.team_id
GROUP BY t.id
ORDER BY u.score;

Related

MySQL JOIN / UNION DISTINCT issue

I am working on a project where users need to have permissions based on which department they are connected to.
So now I am trying to do a query that will show me users that have all necessary permissions based on the users department_id (users that do not have all necessary permissions shall not be displayed in the result).
I have been trying lot of different things (different type of JOINS, sub queries w/where in, exists and union distinct) without luck, this kind of query is next level for me and challenges my logical thinking.
Tables:
users
id
first_name
last_name
department_id
permissions
id
name
departments (relation to groups and categories based on department)
id
name
category_id
group_id
categories
id
name
groups
id
name
departments_permissions (permission requirement)
department_id
permission_id
categories_permissions (permission requirement)
category_id
permission_id
groups_permissions (permission requirement)
group_id
permission_id
users_permissions (users have permissions here)
user_id
permission_id
Department
A department CAN (not required) have many permissions (departments_permissions).
A department CAN (not required) have a category.
A department CAN (not required) have a group.
Category
A category CAN (not required) have many permissions (categories_permissions).
Group
A group CAN (not required) have many permissions (groups_permissions).
Important side note
departments_permissions, categories_permissions and groups_permissions can contain one or more of the same permission_id's, therefore we have to select distinct values.
Example data
https://pastebin.com/BvhAznpY
My latest query attempt:
SELECT
`users`.id,
`users`.first_name,
`users`.last_name,
`users`.department_id
FROM
`users`
INNER JOIN
`departments` ON `users`.department_id = `departments`.id
INNER JOIN
`users_permissions` ON `users`.id = `users_permissions`.user_id
INNER JOIN
(
SELECT permission_id FROM departments_permissions WHERE department_id = departments.id
UNION DISTINCT
SELECT permission_id FROM categories_permissions WHERE category_id = departments.group_id
UNION DISTINCT
SELECT permission_id FROM groups_permissions WHERE group_id = departments.group_id
) AS tblPermissionsRequired ON `users_permissions`.permission_id = `tblPermissionsRequired`.permission_id
GROUP BY
`users_permissions`.user_id;
Build 2 tables (sub-queries) to list
all the permission a user required (i.e. user_permissions)
all the permission a user have (i.e. 3 unions similar to what you have done)
Join them and group by id would find the the number of required and how many of them are given already.
Finally, add a filter clause (using HAVING). Then you got the list of user ID :)
select
up.user_id,
max(u.first_name) as first_name,
max(u.last_name) as last_name,
count(up.permission_id) as permission_required,
count(perm.permission_id) as permission_have
from
users u
join
users_permissions up on u.id = up.user_id
left join
(
SELECT users.id as user_id, permission_id
FROM users
INNER JOIN departments ON users.department_id = departments.id
INNER JOIN departments_permissions on departments_permissions.department_id = departments.id
UNION
SELECT users.id, permission_id
FROM users
INNER JOIN departments ON users.department_id = departments.id
INNER JOIN categories_permissions on categories_permissions.category_id = departments.category_id
UNION
SELECT users.id, permission_id
FROM users
INNER JOIN departments ON users.department_id = departments.id
INNER JOIN groups_permissions on groups_permissions.group_id = groups_permissions.group_id
) perm
on up.user_id = perm.user_id and up.permission_id = perm.permission_id
group by up.user_id
having count(up.permission_id) = count(perm.permission_id)
I think what you need is something like this:
SELECT u.first_name,u.last_name,p.name
FROM users u, user_permissions up, permissions p
WHERE u.id=up.user_id AND
up.permission_id=p.id
UNION
SELECT u.first_name,u.last_name,p.name
FROM users u, departments d, departments_permission dp, permissions p
WHERE u.department_id=d.id
AND d.id=dp.department_id
AND dp.permission_id=p.id
UNION
SELECT u.first_name,u.last_name,p.name
FROM users u, departments d, categories_permission cp, permissions p
WHERE u.department_id=d.id
AND d.category_id=cp.category_id
AND cp.permission_id=p.id
UNION
SELECT u.first_name,u.last_name,p.name
FROM users u, departments d, groups_permission gp, permissions p
WHERE u.department_id=d.id
AND d.group_id=gp.group_id
AND gp.permission_id=p.id
Here's a hint:
Create a VIEW with the query above
Do a SELECT DISTINCT * on the view
This following script should work-
SELECT A.id,A.first_name,A.last_name,A.department_id
FROM
(
SELECT U.*,
A.Dept_id AS Dept_ID,
A.permission_id AS [Dept_Wise_Permissiion_List],
UP.permission_id
FROM users U
INNER JOIN (
SELECT D.id Dept_id,CP.permission_id
FROM departments D INNER JOIN categories_permissions CP ON D.category_id = CP.category_id
UNION ALL
SELECT D.id Dept_id,GP.permission_id
FROM departments D INNER JOIN groups_permissions GP ON D.group_id = GP.group_id
UNION ALL
SELECT D.id Dept_id, DP.permission_id
FROM departments D INNER JOIN departments_permissions DP ON D.id = DP.department_id
) A ON U.department_id =A. Dept_id
LEFT JOIN users_permissions UP
ON U.id = UP.user_id AND A.permission_id = UP.permission_id
)A
GROUP BY A.id,A.first_name,A.last_name,A.department_id
HAVING COUNT(ISNULL(A.permission_id,1)) = COUNT(A.permission_id)

MYSQL Count where active across tables

I have three tables:
person
-----------
person_id, active
person_team
-----------
person_id, team_id, active
team
-----------
team_id, active
I'd like to get the count on teams from each person where active is true in each table.
So far I have:
SELECT t.id, t.title, t.created_timestamp, COUNT(p_t.tag_id) AS count
FROM team t
LEFT JOIN
person_team p_t ON p_t.team_id = t.id AND p_t.active = 1
WHERE
t.active = 1
GROUP BY t.id
ORDER BY t.title
This gets the count where team and person - team are active, but doesn't take into account whether person is active. Should I use a sub query or another type of join?
You need to add the person table in a join, and count a column from that table:
SELECT t.id, t.title, t.created_timestamp, COUNT(p.id) AS count
FROM team t
LEFT JOIN
person_team p_t ON p_t.team_id = t.id AND p_t.active = 1
LEFT JOIN
person p ON p_t.person_id = p.id AND p.active = 1
WHERE
t.active = 1
GROUP BY t.id
ORDER BY t.title
You should use a inner join on sub select for get the columns not in group by
select k.id, t.title, t.created_timestamp, k.count from
( SELECT t.id COUNT(p_t.tag_id) AS count
FROM team t
LEFT JOIN
person_team p_t ON p_t.team_id = t.id AND p_t.active = 1
WHERE
t.active = 1
GROUP BY t.id ) k
inner join team t on t.id = k.id

SQL QUERY giving me desired output, but taking long time to load the output. how can i optimize it?

I have three tables, users(which includes user details), territory_categories(which has different territory names and their ids), user_territory(a junction table between the two)
I want to filter users based on selected territory from the list.
query for that is --
SELECT u.id,account_id_fk,first_name,last_name,email_address,password,mobile_number,gender,user_accuracy,check_in_radius,report_to,role,allow_timeout,
active,last_logged_on,last_known_location_time,last_known_location,u.created_on,u.created_by,
(SELECT COUNT(DISTINCT u2.id) FROM fieldsense.users u2
INNER JOIN user_territory t1 ON u2.id=t1.user_id_fk
INNER JOIN territory_categories c on c.id=t1.teritory_id
**WHERE c.category_name LIKE** "Gujarat" AND(u2.account_id_fk=1 AND u2.role!=0) ) AS usersCount,
IFNULL(a.id,0) attendanceId,IFNULL(a.punch_date,'1111-11-11') punchInDate,IFNULL(a.punch_in,0) punchIntime,IFNULL(a.punch_out_date,'1111-11-11') punchOutDate, IFNULL(a.punch_out,0) punchOutTime
FROM fieldsense.users as u
LEFT OUTER JOIN attendances as a ON u.id=a.user_id_fk AND a.id=(select max(id) from attendances att where att.user_id_fk=u.id)
INNER JOIN user_territory t1 ON u.id=t1.user_id_fk
INNER JOIN territory_categories c on c.id=t1.teritory_id
**WHERE c.category_name LIKE "Gujarat"**
GROUP BY u.id **limit 10**
OUTPUT :
usersCount: 136
attendanceId: 0
punchInDate: 1111-11-11
punchIntime: 0
punchOutDate: 1111-11-11
punchOutTime: 0
10 rows in set (2.06 sec)
And without where clause when it loads it takes almost 1 minute to display the data
OUTPUT without where clause :
usersCount: 144
attendanceId: 0
punchInDate: 1111-11-11
punchIntime: 0
punchOutDate: 1111-11-11
punchOutTime: 0
10 rows in set (54.45 sec)
I am getting the desired output, but query is taking almost 1 minute to load, which i want to optimize. how can i do this ?
Try this out. I just narrowed down your inner join on table user_territory, so it will not join whole user_territory table but will only join distinct user_ids in it. Hope it reduces the performance time,
SELECT u.id,
account_id_fk,
first_name,
last_name,
email_address,
PASSWORD,
mobile_number,
gender,
user_accuracy,
check_in_radius,
report_to,
role,
allow_timeout,
active,
last_logged_on,
last_known_location_time,
last_known_location,
u.created_on,
u.created_by,
(
SELECT
COUNT(u2.id)
FROM
fieldsense.users u2
INNER JOIN (SELECT DISTINCT user_id_fk as id, territory_id FROM user_territory ) t1 ON u2.id = t1.id
INNER JOIN territory_categories c ON c.id = t1.teritory_id
WHERE
c.category_name LIKE "Gujarat"
AND (
u2.account_id_fk = 1
AND u2.role != 0
)
) AS usersCount,
IFNULL(a.id, 0) attendanceId,
IFNULL(a.punch_date, '1111-11-11') punchInDate,
IFNULL(a.punch_in, 0) punchIntime,
IFNULL(
a.punch_out_date,
'1111-11-11'
) punchOutDate,
IFNULL(a.punch_out, 0) punchOutTime
FROM
fieldsense.users AS u
LEFT OUTER JOIN attendances AS a ON u.id = a.user_id_fk
AND a.id = (
SELECT
max(id)
FROM
attendances att
WHERE
att.user_id_fk = u.id
)
INNER JOIN (SELECT DISTINCT user_id_fk as id, territory_id FROM user_territory ) t1 ON u2.id = t1.id
INNER JOIN territory_categories c ON c.id = t1.teritory_id
WHERE
c.category_name LIKE "Gujarat"
GROUP BY
u.id
LIMIT 10

how can i optimize performance with this query?

I have three tables, users, activities and purchases.
Users has many activities and purchases, activities has 4 types.
I need to query users like this:
[
{
user_id: 1,
// from activities
post_count: 2,
updated_count: 3,
print_count: 4,
share_count: 5,
// from purchases
purchase_count: 6
},
...
]
I use this sql:
SELECT u.id, post.post_count, updated.update_count, print.print_count, share.share_count, purchase.purchase_count
FROM users as u
LEFT JOIN (
SELECT user_id, activity_type, count(*) as post_count
FROM activities
WHERE activity_type = 1
GROUP BY user_id
) post
ON u.id = post.user_id
LEFT JOIN (
SELECT user_id, activity_type, count(*) as update_count
FROM activities
WHERE activity_type = 2
GROUP BY user_id
) updated
ON u.id = updated.user_id
LEFT JOIN (
SELECT user_id, activity_type, count(*) as print_count
FROM activities
WHERE activity_type = 3
GROUP BY user_id
) print
ON u.id = print.user_id
LEFT JOIN (
SELECT user_id, activity_type, count(*) as share_count
FROM activities
WHERE activity_type = 4
GROUP BY user_id
) share
ON u.id = share.user_id
LEFT JOIN (
SELECT user_id, count(*) AS purchase_count
FROM purchases
GROUP BY user_id
) purchase
ON u.id = purchase.user_id
how can i optimize performance with this query?
Great thanks to Eugen Rieck
I modified his query to this, then it works.
SELECT
users.id AS user_id,
SUM(IF((activities.activity_type=1),1,0)) AS post_count,
SUM(IF((activities.activity_type=2),1,0)) AS update_count,
SUM(IF((activities.activity_type=3),1,0)) AS print_count,
SUM(IF((activities.activity_type=4),1,0)) AS share_count,
IFNULL(purchase.count,0) AS purchase_count
FROM
users
LEFT JOIN activities ON activities.user_id=users.id
LEFT JOIN (
SELECT user_id, count(*) AS count
FROM purchases
GROUP BY user_id
) purchase
ON users.id = purchase.user_id
GROUP BY users.id
Currently you run the activities table 4 times - this could be folded into one:
SELECT
users.id AS user_id,
SUM(IF(activites.activity_type=1,1,0)) AS post_count,
SUM(IF(activites.activity_type=2,1,0)) AS update_count,
SUM(IF(activites.activity_type=3,1,0)) AS print_count,
SUM(IF(activites.activity_type=4,1,0)) AS share_count,
IFNULL(COUNT(purchases.id),0) AS purchase_count
FROM
users
INNER JOIN activities ON activities.user_id=users.id
LEFT JOIN purchases ON purchases.user_id=users.id
GROUP BY users.id

issue with joins

I have the following query, in which I used JOINs. It says:
unknown column m.bv ..
Could you please take a look and tell me what I'm doing wrong?
$query4 = 'SELECT u.*, SUM(c.ts) AS total_sum1, SUM(m.bv) AS total_sum
FROM users u
LEFT JOIN
(SELECT user_id ,SUM(points) AS ts FROM coupon GROUP BY user_id) c
ON u.user_id=c.user_id
LEFT JOIN
(SELECT user_id ,SUM(points) AS bv FROM matching GROUP BY user_id) r
ON u.user_id=m.user_id
where u.user_id="'.$_SESSION['user_name'].'"
GROUP BY u.user_id';
You are selecting SUM(points) AS bv from the table with the alias r, there is no tables with the alias m. So that it has to be r.bv instead like so:
SELECT
u.*,
SUM(c.ts) AS total_sum1,
SUM(r.bv) AS total_sum
FROM users u
LEFT JOIN
(
SELECT
user_id,
SUM(points) AS ts
FROM coupon
GROUP BY user_id
) c ON u.user_id=c.user_id
LEFT JOIN
(
SELECT
user_id,
SUM(points) AS bv
FROM matching
GROUP BY user_id
) r ON u.user_id = m.user_id
where u.user_id="'.$_SESSION['user_name'].'"
GROUP BY u.user_id
Replace m., with r. Look at second Join
You have aliased the derived table with r and you reference that table (twice) with m. Correct one or the other.
Since you group by user_id in the two subqueries and user_id is (I assume) the primary key of table user, you don't really need the final GROUP BY.
I would write it like this, if it was meant for all (many) users:
SELECT u.*, COALESCE(c.ts, 0) AS total_sum1, COALESCE(m.bv, 0) AS total_sum
FROM users u
LEFT JOIN
(SELECT user_id, SUM(points) AS ts FROM coupon GROUP BY user_id) c
ON u.user_id = c.user_id
LEFT JOIN
(SELECT user_id, SUM(points) AS bv FROM matching GROUP BY user_id) m
ON u.user_id = m.user_id
and like this in your (one user) case:
SELECT u.*, COALESCE(c.ts, 0) AS total_sum1, COALESCE(m.bv, 0) AS total_sum
FROM users u
LEFT JOIN
(SELECT SUM(points) AS ts FROM coupon
WHERE user_id = "'.$_SESSION['user_name'].'") c
ON TRUE
LEFT JOIN
(SELECT SUM(points) AS bv FROM matching
WHERE user_id = "'.$_SESSION['user_name'].'") m
ON TRUE
WHERE u.user_id = "'.$_SESSION['user_name'].'"
The last query can also be simplified to:
SELECT u.*,
COALESCE( (SELECT SUM(points) FROM coupon
WHERE user_id = u.user_id)
, 0) AS total_sum1,
COALESCE( (SELECT SUM(points) FROM matching
WHERE user_id = u.user_id)
, 0) AS total_sum
FROM users u
WHERE u.user_id = "'.$_SESSION['user_name'].'"