So in the database, there's a table named roles_users. This holds all the roles that the users have. Here, there's two columns: user_id, role_id.
A normal user, with no extra roles has 1 row in this table. This row has role_id 1.
A admin user, has 2 rows in this table. One with role_id 1, and one row with role_id 2
Like this:
user_id role_id
88 1
88 2
99 1 // Only one row with that user_id, so he's a user
Now im trying to count how many users/admin/sellers/partners that exists.
Sellers have 3 rows, one with role_id 1, role_id 2 and role_id 3.
Partner has role_id 1, role_id 4
So i tried this:
SELECT user_id FROM roles_users WHERE role_id IN (1) // MEMBERS ONLY
SELECT user_id FROM roles_users WHERE role_id IN (1,2) // ADMIN
SELECT user_id FROM roles_users WHERE role_id IN (1,2,3) // SELLER
SELECT user_id FROM roles_users WHERE role_id IN (1,4) // PARTNERS
But these queries does not work properly. They give me a count that is way over than its supposed to be. And i believe this is because that it does not EXCLUDE any rows,
I mean at the query when it should look for role_id 1, for members, it includes partners,admin,sellers too because it only check for if it theres any with the row role_id 1 and thats it.
So how can i do this right? So when it looks after members, it should also make sure that the user_id does not have any more rows with other role_ids like 2,3,4
group_concat is what you want:
select
case roles
when '1' then 'members'
when '1,2' then 'admins'
when '1,2,3' then 'sellers'
when '1,4' then 'partners'
else 'uh??'
end role,
count(user_id) nr_users from (
select user_id, group_concat(role_id order by role_id separator ',') roles
from roles_users
group by user_id
)
group by role
order by role;
And by the way, you could store roles more efficiently using a bitmask. Advantage: only one column per user id. Disadvantage: harder to build queries...
This will give you all combinations of privileges in the table with their count
SELECT
COUNT(*) AS num
GROUP_CONCAT(role_id ORDER BY role_id) AS privilegeset
FROM roles_users
GROUP BY user_id
Concerning sellers and partners:
As I understand it, sellers are the only ones that can have role_id = 3 and partners are the only ones that can have role_id = 4, correct?
If yes, finding sellers and partners is quite easy:
SELECT user_id FROM roles_users WHERE role_id = 3 // SELLER
SELECT user_id FROM roles_users WHERE role_id = 4 // PARTNERS
Not the most elegant, but a start
SELECT user_id
,CASE WHEN (role1 > 0 AND 0 = role2 AND 0 = role3 AND 0 = role4) THEN 'MEMBER'
WHEN (role1 > 0 AND role2 > 0 AND 0 = role3 AND 0 = role4) THEN 'ADMIN'
WHEN (role1 > 0 AND role2 > 0 AND role3 > 0 AND 0 = role4) THEN 'SELLER'
WHEN (role1 > 0 AND 0 = role2 AND 0 = role3 AND role4 > 0) THEN 'PARTNER'
ELSE 'Something else'
END AS type
FROM (
SELECT user_id
,SUM( CASE role_id WHEN 1 THEN 1 ELSE 0 END) AS role1
,SUM( CASE role_id WHEN 2 THEN 1 ELSE 0 END) AS role2
,SUM( CASE role_id WHEN 3 THEN 1 ELSE 0 END) AS role3
,SUM( CASE role_id WHEN 4 THEN 1 ELSE 0 END) AS role4
FROM roles_users
GROUP BY user_id
) x
EDIT: I guess this is answering the wrong question, it is showing the type of each user.
Here's a solution using INTERSECT and MINUS. Sadly these are not supported by MySQL yet but maybe someone will find this useful nonetheless.
-- Find users with Role 1
SELECT user_id FROM
(SELECT user_id, role_id FROM roles_users WHERE role_id = 1)
MINUS SELECT user_id FROM roles_users WHERE role_id NOT IN (1)
-- Find users with Roles 1 and 2
SELECT user_id FROM
(SELECT user_id, role_id FROM roles_users WHERE role_id = 1
INTERSECT SELECT user_id, role_id FROM roles_users WHERE role_id = 2)
MINUS SELECT user_id FROM roles_users WHERE role_id NOT IN (1,2)
-- Find users with Roles 1, 2, 3
SELECT user_id FROM
(SELECT user_id, role_id FROM roles_users WHERE role_id = 1
INTERSECT SELECT user_id, role_id FROM roles_users WHERE role_id = 2
INTERSECT SELECT user_id, role_id FROM roles_users WHERE role_id = 3)
MINUS SELECT user_id FROM roles_users WHERE role_id NOT IN (1,2,3)
-- Find users with Roles 1, 4
SELECT user_id FROM
(SELECT user_id, role_id FROM roles_users WHERE role_id = 1
INTERSECT SELECT user_id, role_id FROM roles_users WHERE role_id = 4)
MINUS SELECT user_id FROM roles_users WHERE role_id NOT IN (1,4)
Here's a join-based solution that hopefully works on any SQL.
SELECT user_id FROM roles_users RU0
LEFT JOIN roles_users RU1 ON RU0.user_id = RU1.user_id AND RU1.role_id = 1
LEFT JOIN roles_users RU2 ON RU0.user_id = RU2.user_id AND RU2.role_id = 2
LEFT JOIN roles_users RU3 ON RU0.user_id = RU3.user_id AND RU3.role_id = 3
LEFT JOIN roles_users RU4 ON RU0.user_id = RU4.user_id AND RU4.role_id = 4
WHERE RU1.user_id IS NOT NULL -- should have role 1
AND RU2.user_id IS NULL -- should NOT have role 2
AND RU3.user_id IS NULL -- should NOT have role 3
AND RU4.user_id IS NOT NULL -- should have role 4
Just vary the "IS NULL" and "IS NOT NULL" in the where clause to change which roles you want the user to have or not have.
Related
I am trying to calculate the how many users are female and how many male, after doing DISTINCT on the UserID
SELECT
COUNT(IF("Gender" = 'female', 1, NULL)) as Ufemale,
COUNT(IF("Gender" = 'male', 1, NULL)) as Umale
FROM (SELECT DISTINCT UserID FROM user_stats where Year='2019' and Account='P') as UID
If I execute the
SELECT DISTINCT UserID FROM user_stats where Year='2019' and Account='P'
It returns the unique UserID. However, if I combine it with count gender part, it returns Zero.
Here is how the values looks like
UserID | Gender
-----------------------------
2018359084885123 | male
1925823664195671 | female
2033134076795519 |
2122445674469149 | female
2315129265210413 | female
2018359084885123 | male
2122445674469149 | female
And the aim is to show
Ufemale | Umale
-------------------
3 | 1
Try this:
Tables
drop table if exists test;
create table test (userid bigint, gender char(6));
insert into test values
(2018359084885123,'male')
,(1925823664195671,'female')
,(2033134076795519, null)
,(2122445674469149,'female')
,(2315129265210413,'female')
,(2018359084885123,'male')
,(2122445674469149,'female');
Query
select
sum(case when gender = 'female' then 1 else 0 end) as ufemale,
sum(case when gender = 'male' then 1 else 0 end) as umale
from
(select distinct userid, gender from test) x
Result
ufemale umale
3 1
Example: https://rextester.com/JLQ19855
There is no Gender column in the results of the subquery:
SELECT DISTINCT UserID FROM user_stats where Year='2019' and Account='P'
as you may think.
What you are doing is comparing 2 strings: "Gender" and 'female' which are obviously not equal so this:
IF("Gender" = 'female', 1, NULL)
returns NULL and COUNT(NULL) returns 0.
Where is the Gender column?
Is it in user_stats table or in another table like Users?
If it is in user_stats then you need to write the query like this:
select
sum(t.gender = 'female') ufemale,
sum(t.gender = 'male') umale
from (
select distinct userid, gender
from user_stats
where Year='2019' and Account='P'
) t
If it is in the users table then you will need a join first:
select
sum(u.gender = 'female') ufemale,
sum(u.gender = 'male') umale
from (
select distinct userid, gender
from user_stats
where Year='2019' and Account='P'
) t inner join users u
on u.userid = t.userid
I would like to grab all the users that ONLY have two roles, which are 1 and 4.
One user role is stored like this:
user_id role_id
54321 1
54321 4
54322 1
54323 1
How can i make a query, that grabs the user_id 54321, because it Only have two roles and these two are 1 and 4?
I can use WHERE role_id IN (1, 4) but this will also grab users that have other roles.
WHERE role_id IN (1, 4) GROUP BY user_ID HAVING COUNT(DISTINCT role_id) = 2
http://gregorulm.com/relational-division-in-sql-the-easy-way/
This is an example of a set-within-sets query. I like to solve these with group by and having because that is the most general approach:
select user_id
from user_roles ur
group by user_id
having sum(role_id = 1) > 0 and
sum(role_id = 4) > 0 and
sum(role_id not in (1, 4)) = 0;
The having clause has three conditions. The first counts the number of times that role is 1, and the user_id passes if there is at least one such role. The second does the same for 4. The last checks that there are no other values.
I like this structure because it is flexible. If the condition were 1 and 4 and others are allowed, you would just drop the third clause. If the condition were 1 or 4 and no others, then it would look like:
having (sum(role_id = 1) > 0 or
sum(role_id = 4) > 0
) and
sum(role_id not in (1, 4)) = 0;
SELECT u3.user_id
FROM t u1, t u2, t u3
WHERE u1.role_id = 1 AND u2.role_id = 4
AND u3.user_id = u1.user_id
AND u2.user_id = u1.user_id
GROUP BY u3.user_id HAVING COUNT(u3.role_id) = 2;
Here is my table structure.(fun_friends)
id user_id,friend_id,status,createdat,updatedat
1 1 2 1 123456 125461
2 1 3 1 454545 448788
3 2 4 1 565659 898889
4 1 5 1 877878 878788
Here is the table structure of user_uploads
id user_id parent_id category_id title slug tags description video_type source video_link video_thumb
1 2 1 2 fun fun ['4','5'] coolvid 1 ytu link thumb
I need to show the latest upload of my friends
Can you tell me how can i join this tables together? i tried with
SELECT * FROM fun_friends WHERE (user_id= '".$_SESSION['user_row_id']."' AND `status` =1) OR (friend_id= '".$_SESSION['user_row_id']."' AND `status` =1)
and it is showing all friends of logged-in user
You can just join both table using user_id field. sample query bellow will return one record with latest user_uploads.id.
select *
from fun_friends a
inner join user_uploads b on a.user_id = b.user id
order by b.id desc limit 0,1
how about using UNION to get the friends user and wrapping it inside a subquery which later join on the other tabel,
SELECT usr.*
FROM user_uploads usr
INNER JOIN
(
SELECT user_ID AS ID
FROM Fun_Friends
WHERE friend_ID = 'ID_HERE'
UNION
SELECT friend_ID As ID
FROM Fun_Friends
WHERE ser_ID = 'ID_HERE'
) idList ON usr.user_ID = idList.ID
I have a MySQL query that I used to use to return mutual friends between two users, but now that I am recoding my website, I am trying to simplify this code, or at least make it better.
So here's my code below to check mutual friends:
SELECT a.friendID
FROM
(SELECT CASE WHEN userID = $id
THEN userID2
ELSE userID
END AS friendID
FROM friends
WHERE (userID = $id OR userID2 = $id)
AND state='1'
) a
JOIN
(SELECT CASE WHEN userID = $session
THEN userID2
ELSE userID
END AS friendID
FROM friends
WHERE (userID = $session OR userID2 = $session)
AND state='1'
) b
ON b.friendID = a.friendID
My table is set up like this:
userID -- userID2 -- state
1 ------- 2 ------- 1
2 ------- 3 ------- 1
1 ------- 3 ------- 0
(sorry, I don't know how to do the pretty database structure design, so if someone could edit that for me...)
but for the above, when user 1 is on user 3's profile, since user 1 and user 2 are friends, and user 2 and user 3 are friends, but user 1 and user 3 are not, it should return user 2 as a mutual friend. (state 1 means friendship accepted, state 0 means friendship pending, so only if state is 1 should it be counted as a friend)
Also note that userID and userID2 can be in any order, it depends on who requests who as a friend, so like the above query does, I need to also have the "friendID" returned, as the above query does right.
SELECT t.f FROM friends JOIN (
SELECT IF(userID = $id, userID2, userID) f
FROM friends
WHERE state AND $id IN (userID, userID2)
) t ON t.f IN (userID, userID2)
WHERE state AND $session IN (userID, userID2)
See it on sqlfiddle.
Not necessarily the fastest but simple to read:
Create View ActiveFriends As
Select
UserId, UserID2
From
friends
Where
State = '1'
Union All
Select
UserId2, UserID
From
friends
Where
State = '1'
Select
f1.userID2 as MutualFriendId
From
ActiveFriends f1
Inner Join
ActiveFriends f2
On f1.UserID2 = f2.UserID
Where
f1.UserID = $session And
f2.UserID2 = $id
http://sqlfiddle.com/#!2/5748f/1/0
You could do :
SELECT userID2
FROM friends_table
WHERE userID IN ( $id, $session )
AND state = 1
GROUP BY userID2
HAVING COUNT(userID2) >= 2
friends_table :
user_id | friend_id | state
-----------------------------
1 | 7 | 1
2 | 3 | 0
7 | 1 | 1
User 1 and 7 are friends, user 2 wants to be friends with user 3, but user 3 hasn't responded yet.
at the first glance I would say that using intersect is a good solution
so this query should do the trick for you
SELECT userID2
FROM friends
WHERE userID = $fromID
INTERSECT
SELECT userID2
FROM friends
WHERE userID = $theOtherID
EDIT:
another less clear solution would be
SELECT userID2
FROM friends INNER JOIN friends f2
USING (userID2)
WHERE friends.UserID = $fromID AND f2.userID = $theOtherID
I have a database with users and games. 1 game can have multiple users so I made a linking table called users_games. The crux is that a game can always only have 2 players since it is a board game. I know which player I am, i have my user_id and my email, but I would like to gain a result that gives me a list of all games I am in WITH the user_id and email of the other fellow. So a query that looks to all games I am in and give the other row, with the name of the player.
My tables:
games
id (int)
board (varchar) representation of the board
users
id (int)
email (varchar)
password (varchar MD5)
users_games
id (int)
user_id (int)
game_id (int)
For clarification this query
SELECT *
FROM `tic_users_games` AS ug
LEFT JOIN tic_users AS u
ON ug.user_id = u.id
RIGHT JOIN tic_games AS g
ON ug.game_id = g.id
And result
id user_id game_id id email password id board created updated
1 1 1 1 ME#gmail.com d56b699830e77ba53855679cb1d252da 1 0|0|0|0|0|0|0|0|0 2012-04-02 16:56:06 2012-04-02 16:56:06
2 2 1 2 FOE1#gmail.com d56b699830e77ba53855679cb1d252da 1 0|0|0|0|0|0|0|0|0 2012-04-02 16:56:06 2012-04-02 16:56:06
3 3 2 2 FOE2#gmail.com d56b699830e77ba53855679cb1d252da 2 0|0|0|0|0|0|0|0|0 2012-04-02 16:56:06 2012-04-02 16:56:06
4 1 2 1 ME#gmail.com d56b699830e77ba53855679cb1d252da 2 0|0|0|0|0|0|0|0|0 2012-04-02 16:56:06 2012-04-02 16:56:06
See: In the above case I just want 2 rows: game_id 1 and 2, with FOE1#... and FOE2#...
Thanks
This should do it for you. The syntax might not be exact for mysql but you should get there. Basically get all users that have games in the user_games table with the same game_ID as the games I am in:
SELECT
User_Games.Game_ID,
Users.ID,
Users.Email
FROM
Users
LEFT JOIN User_Games ON Users.ID = User_Games.User_Id
WHERE
Users.User_ID <> #yourUserID
AND EXISTS
(SELECT
NULL
FROM
User_Games AS MyUserGames
WHERE
User_Games.Game_ID = MyUserGames.Game_ID
AND MyUserGames.User_ID = #yourUserID)
Try this (where #userid represents the user you are searching games for, in this case, 1):
select *
from tic_users_games ug1
left join tic_users_games ug2 on ug1.game_id = ug2.game_id
left join tic_users u on ug2.user_id = u.id
right join tic_games g on ug2.game_id = g.id
where ug1.user_id = #userid
and ug2.user_id <> #userid
Demo: http://www.sqlfiddle.com/#!2/7b0f6/2