Count Memberships of User in Groups - mysql

I've a tree structure of an association which is divided in divisions, subdivisions etc. on every level users may have memberships to certain roles.
I want to count the memberships on every "structure type" (association, division, subdivision) as defined in the table
The Table structure looks like:
table intern_structures
Contains the hierarchy (nested set, but that does not matter here)
id | intern_structure_type_id | name | parent_id | lft | rgt
1 1 My Company USA 0 1 6
2 2 Texas 1 2 5
3 3 El Paso 2 3 4
table intern_structure_types
Contains Description to the types like "association", "division", "subdivision"
id | name
1 Association
2 Division
3 Subdivision
table memberships
Contains the memberships
id | user_id | intern_structure_id | role_id
1 1 1 1
2 1 2 2
3 2 3 1
3 2 3 3
....
table roles
Contains role descriptions
id | name
1 Admin
2 Moderator
3 Clerk
I want a grouped list like:
structure_type_name | role_name | count of memberships
Association Admin 1
Association Moderator 10
Association Clerk 0 << !! I miss the zero rows!
Division Admin 7
Divison Moderator 43
Division Clerk 31
Subdivision Admin 234
Subdivision Moderator 942
Subdivision Clerk 456
What I achieved so far is this query:
SELECT
is_types.name,
roles.name,
COUNT(memberships.id)
FROM
roles,
intern_structure_types AS is_types
LEFT JOIN intern_structures AS is_elements ON is_elements.intern_structure_type_id = is_types.id
LEFT JOIN memberships ON memberships.intern_structure_id = is_elements.id
WHERE
roles.id = memberships.role_id
GROUP BY
is_types.id, roles.id
It works fine except that it doesn't list all roles because some roles don't have any memberships yet but I want them listed as well just with 0 as membership count.
I'd be very thankful for any help!

I'm assuming the counts you showed in the OP are contrived. To get the results you want, you should create a derived table of the types and roles in use and then left join that entire query to a cross join of the roles and types.
Select is_types.name
, roles.name
, Count(Z.is_type_name)
From roles
Cross Join intern_structure_types As is_types
Left Join (
Select is_types.name As is_type_name
, roles.name As role_name
From intern_structures As is_elements
Join intern_structure_types As is_types
On is_types.id = is_elements.intern_structure_type_id
Join memberships
On memberships.intern_structure_id = is_elements.id
Join roles
On roles.id = memberships.role_id
) As Z
On Z.is_type_name = is_types.name
And Z.role_name = roles.name
Group By is_types.name, roles.name

Related

SQL Select parent as column name and child as value

I am creating a database to store music.
There are different categories that each have sub categories. Example:
id name parentID
1 instrumentation null
2 solo_instrument null
3 Concert Band 1
4 Brass Band 1
5 Fanfare Band 1
6 Clarinet 2
7 Saxophone 2
8 Trumpet 2
On the other hand I have a table that stores the musicID that is linked to a categoryID
id categoryID musicID
1 4 1
2 8 1
3 3 2
4 6 2
I need the following result from a query:
musicID instrumentation solo_instrument
1 Brass Band Trumpet
2 Concert Band Clarinet
I have been told to use a tree structure as in the future it is likely that other categories are added and this should be able to support that. However, I am not able to figure out how to write a query to get the result above.
I kind of get the result I want when selecting first the instrumentation, second the solo_instrument, but this is all hardcoded and does not allow for music tracks to only have one parentID as I select them individually.
Is this even possible or should I overhaul my database structure? I'd like to see your recommendations.
You should be able to tackle this using conditional aggregation.
Query :
SELECT
mu.musicID,
MAX(CASE WHEN cp.name = 'instrumentation' THEN ca.name END) instrumentation,
MAX(CASE WHEN cp.name = 'solo_instrument' THEN ca.name END) solo_instrument
FROM
musics mu
INNER JOIN categories ca ON ca.id = mu.categoryID
INNER JOIN categories cp ON cp.id = ca.parentID
GROUP by mu.musicID
The INNER JOINs pull up the corresponding category, and then goes up one level to find the parent category. If new root categories are created, you would just need to add more MAX() columns to the query.
In this DB Fiddle demo with your sample data, the query returns :
| musicID | instrumentation | solo_instrument |
| ------- | --------------- | --------------- |
| 1 | Brass Band | Trumpet |
| 2 | Concert Band | Clarinet |
First you group by musicid in table_music and the join twice to table_categories:
select t.musicid, c1.name instrumentation, c2.name solo_instrument
from (
select musicid, min(categoryid) instrumentationid, max(categoryid) solo_instrumentid
from table_music
group by musicid
) t inner join table_categories c1
on c1.id = t.instrumentationid
inner join table_categories c2
on c2.id = t.solo_instrumentid
order by t.musicid

How to Join 3 MYSQL Tables with multiple conditions and even if no data in 3rd table

My New Project is something like a blog roll in which users can follow other users.
While listing all Posts, There will be a button to follow/unfollow under each post.
Tables used are users, posts and followers. which are as follows:
USER Table
id firstName lastName
.....................................
1 arun prasanth
2 ann antony
3 sruthy abc
6 John abc
POSTS Table
id user_id postname url
.....................................................
1 2 post1 www.url1.com
2 2 post2 www.url2.com
3 6 post3 www.url3.com
4 3 post4 www.url4.com
Followers Table
id user_id following_users_id date
..............................................
1 2 1 2018-01-25
2 2 3 2018-01-25
3 6 3 2018-01-25
4 3 6 2018-01-25
to list all posts my query was like:
SELECT * FROM posts LEFT JOIN users on posts.user_id = users.user_id;
and it worked fine as both table have corresponding rows and the result was by listing both table columns with all data.
Now My issue is while joining third table, ie followers table. problem with followers table is, it will have only one row which corresponds to current logged in users user_id and followers.user_id.
i tried all variants like LEFT RIGHT INNER and OUTER JOIN's. but no success.
the result i want is:
user.id -> xxx
user.firstName -> xxx
user.lastName -> xxx
posts.id -> xxx
posts.user_id -> xxx
posts.postname -> xxx
posts.url -> xxx
followers.id -> xxx
followers.user_id -> **Logged IN Users ID**
followers.following_users_id -> **posts.user_id**
followers.date -> xxx
the followers.id, followers.user_id, followers.following_users_id, followers.date should be printed only if followers.user_id === Logged IN Users ID AND followers.following_users_id === posts.user_id is true. else those columns should be blank.
Is there any way i can retrieve data as mentioned above of should i use something like a foreach loop?

Put value in custom defined column in sql query

I would like to create a sql query which create a column that doesn't exists in the db tables and gets fills on whether a row exists or not exists in a specific table.
For example:
I have 3 tables:
Users (For users list) - UID, UName
Locations (List of all available locations) - LID, LName
UsersLocations (All the locations the users have checked into) - UserID, LocationID
I need a sql query that from a user id get me a table of all the locations with a column that says whether the user has been in this location or not.
Example for Users table
UID | UName
1 John
4 Amy
5 Dann
Example for Locations table:
LID | LName
1 London
2 Barcelona
3 Paris
4 New York
Example for UsersLocations table:
UserID | LocationID
5 1
5 2
Example for output (for userid = 5):
User ID | Location | Was Here
5 London true
5 Barcelona true
5 Paris false
5 New York false
The output needs to include all the Locations from the locations table.
Also the UsersLocations table only contains the userID of the users that checked into that location.
Hmmm. One method is a correlated subquery:
select u.userid, l.location,
(case when exists (select 1 from userLocations where ul.userid = u.userid and ul.lid = l.locationid)
then 'true'
else 'false'
end) as WasHere
from location l cross join
(select 5 as userid) u;
The only part that is database-specific is the subquery for u.

Mysql join query with where and like

I have three related tables users, groups and group_members. The users table contains users information and groups table contains the group name and group owner id (ie, users_id). Table group_members contains the members of each groups (user_id and group_id). I tried to load the users details of each group but the result is not correct. My tables are,
users
id firstname lastname email
1 anu mm anu#gmail.com
2 manu mohan manu#gmail.com
3 cinu ab cinu#gmail.com
4 vinu mj vinu#gmail.com
5 vijesh cc vijesh#gmail.com
6 admin admin admin#gmail.com
groups
id user_id name
1 1 group1
2 2 group2
3 2 group3
4 3 group4
group_members
id user_id group_id
1 1 1
2 2 2
3 2 3
4 3 4
5 2 3
7 1 2
8 1 3
9 3 1
But when I tried the bellow query,
select users.firstname,users.lastname,users.email,groups.name as groupname from users inner join groups on groups.user_id = users.id inner join group_members on group_members.group_id = groups.id where groups.id=3 AND users.firstname LIKE 'cin%' OR users.lastname LIKE 'cin%' OR users.email LIKE 'cin%'
getting the result (which is not expected)
firstname lastname email groupname
cinu ab cinu#gmail.com group4
In this case I used the where condition WHERE groups.id =3 but the result takes the group4 (groups.id is 4). Please suggest me a solution.
your have to use () arround your or statements:
where groups.id=3 AND (users.firstname LIKE 'cin%' OR users.lastname LIKE 'cin%' OR users.email LIKE 'cin%')
Because AND has a higher priority you query looks like:
where (groups.id=3 AND users.firstname LIKE 'cin%') OR users.lastname LIKE 'cin%' OR users.email LIKE 'cin%'
And that is not what you want.

MySQL IN operator of result from another column

I have 2 tables:
users(uid, name, titles)
titles(uid, name)
users:
uid | name | titles
1 David 2,4
2 John 5
3 Jane 4
titles:
uid | name
2 Owner
4 CEO
5 Manager
The question is how do I select something like this:
SELECT u.* FROM users as u
JOIN titles as t
ON t.uid IN (u.titles)
WHERE t.uid=2
Notice the IN(u.titles)? It's only taking the first title uid in u.titles field. That means when I change condition to WHERE t.uid=4, it shows no records.
Any idea?
SELECT u.*
FROM users as u
JOIN titles as t ON find_in_set(t.uid, u.titles) > 0
WHERE t.uid=2
If you want that each user can have multiple titles I would recommend a reference table which references the users to the titles.