MySQL JOIN multiple results - mysql

I am trying to select all of the roles a specific user has access to within a specific server. This is for a system that allows a user to manage one or more services. The amount of access a user has is assigned by whoever the service belongs to. Roles are grouped and that group is then what gets assigned to a user. A user may have more than one group.
This is the query that I made and expected to work, but it doesn't. I am guessing it doesn't work because the serverPermissions table can return more than 1 groupId based on what a user is assigned.
SELECT serverGroupRoles.roleId FROM `serverGroupRoles`, `serverPermissions`, `servers`
WHERE servers.identifier='someUniqueString' AND
serverPermissions.serverId=servers.id AND
serverPermissions.userId=1 AND
serverGroupRoles.groupId=serverPermissions.groupId
Here's a visual look of the tables, 'servers' table has other data, but it's unrelated.
servers table, identifier is a unique key:
id | identifier | ...
--------------------------
1 | someString | ...
2 | someString02 | ...
serverPermissions table:
serverId | groupId | userId
--------------------------------
1 | 1 | 1
1 | 2 | 1
1 | 2 | 2
2 | 3 | 1
3 | 4 | 1
serverGroupRoles table:
groupId | roleId
------------------
1 | 1
1 | 2
1 | 3
2 | 1
2 | 3
3 | 4
4 | 2
The roleId's are mapped in the application to a certain action.
This is what I am trying to accomplish, but with 1 query:
If you did something like,
SELECT id FROM `servers` WHERE identifier = 'someString'
Returns
id
--
1
Then took the id that was returned from that,
SELECT groupId FROM `serverPermissions` WHERE serverId = 1 AND userId = 1
Then it would return
groupId
-------
1
2
Then with each groupId,
SELECT roleId FROM `serverGroupRoles` WHERE groupId = #
And the end result,
roleId
------
1
2
3
Is there a good way to do this with 1 query?
Edit, query that accomplishes the task:
SELECT DISTINCT sgr.roleID
FROM serverPermissions sp
INNER JOIN servers s ON s.id = sp.serverID
INNER JOIN serverGroupRoles sgr ON sgr.groupID = sp.groupID
WHERE sp.userID = 1
AND s.identifier = 'someString'

It's still a bit early here, but would this do what you want:
SELECT DISTINCT sgr.roleID
FROM serverPermissions sp
INNER JOIN serverGroupRoles sgr ON sgr.groupID = sp.groupID
WHERE sp.serverID = 1
AND sp.userID = 1
I could be off the mark here as I'm not sure where the servers table comes into this. If you're looking for data from that table you can join it in too:
SELECT DISTINCT sgr.roleID, s.fieldName
FROM serverPermissions sp
INNER JOIN servers s ON s.id = sp.serverID
INNER JOIN serverGroupRoles sgr ON sgr.groupID = sp.groupID
WHERE sp.serverID = 1
AND sp.userID = 1

Is this what you want?
SELECT roleId
FROM `serverGroupRoles`
WHERE groupId in (SELECT groupId
FROM `serverPermissions`
WHERE serverId = 1 AND userId = 1
)
Perhaps you actually want "SELECT distinct roleID" to eliminate duplicates.
You can extend this for servers, but I would do it as a set of joins:
SELECT distinct roleId
FROM `serverGroupRoles` sgr join
`serverPermissions` sp
on sgr.groupId = sp.groupId join
`server` s
on sp.serverid = s.id
WHERE s.identifier = 'someString' AND sgr.userId = 1

Related

MySQL fetch rows if every columns equals the same thing

I have a table like this :
---------------------------------------
| Actions |
---------------------------------------
| action_id | user_id | action_active |
---------------------------------------
| 1 | 1 | 0 |
---------------------------------------
| 2 | 2 | 1 |
---------------------------------------
| 3 | 1 | 0 |
---------------------------------------
| 4 | 2 | 0 |
---------------------------------------
I want to retrieve all rows where a user has all of his rows as action_active = 0. If he has just one action_active as 1, don't retrieve it.
In this example, it should only retrieve the row 1 and 3, since the user 1 has all of his rows at action_active = 0.
I thought about something like this, but I'm not sure about how right it is :
SELECT *
FROM Actions AS a
WHERE action_active = ALL (SELECT action_active FROM actions as s WHERE action_active = 0 where a.idx_user = s.idx_user)
I'm not sure my query is right.
Thanks !
Calculate sum in a sub-query to find users with all zero values and join that with main select
SELECT a.*
FROM actions a
JOIN (SELECT user_id, SUM(action_active) AS sum
FROM actions
GROUP BY user_id) AS sum_a ON sum_a.user_id = a.user_id
WHERE sum = 0
Use NOT EXISTS:
SELECT a.*
FROM actions a
WHERE NOT EXISTS (SELECT 1
FROM actions a2
WHERE a2.user_id = a.user_id AND
a2.action_active <> 0
);
This should have better performance than a solution using group by -- and this makes direct use use of an index on actions(user_id, action_active).
You can also phrase this using a LEFT JOIN:
SELECT a.*
FROM actions a LEFT JOIN
actions a2
ON a2.user_id = a.user_id AND a2.action_active <> 0
WHERE a2.user_id IS NULL;

MySQL - Select N random rows of a different user where only value is contained less or equal toX

I want to select N random rows from a table, but in all of these rows a specific value may only occur X times.
Table "reviews":
*--------------------*
| ID | CODE_REVIEWER |
*--------------------*
| 1 | 2 |
| 1 | 3 |
| 1 | 4 |
*--------------------*
Table "users" (I left out a lot of unimportant stuff:
*----*
| ID |
*----*
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
*----*
Example output:
For X = 3:
*-----------*
| REVIEWER |
*-----------*
| 4 |
| 1 |
| 5 |
*-----------*
For X = 2:
*-----------*
| REVIEWER |
*-----------*
| 1 |
| 5 |
| 3 |
*-----------*
For X = 1 (empty):
*-----------*
| REVIEWER |
*-----------*
So, it must be a ResultSet containing a few IDs that are different from the ID X, but these IDs may only occur in "table 2" as a "code_reviewer" N times.
So everybody can be the "reviewer" FOR 3 people, and everbody can be reviewed BY 3 people
Thanks!
Edit:
This is what I got so far:
select newid from (select id, count(*) as num from (select * from users
where id != ?) as users group by id order by RAND() LIMIT ?) as sb
where num < 3 and newid not in (select code_reviewer from reviews where id = ?)
It works perfectly, apart from that it sometimes returns for example
*---*
| 2 |
| 1 |
| 2 |
*---*
(Contains the 2 twice, which shouldn't be so)
Unfortunately, I know MSSQL and not MySQL. I will try to answer using MSSQl, and hopefully that will lead you in the right direction.
I use variables to determine how many rows I should return, and then use a simple NEWID to act as a randomizer. (It is my understanding that you would order by RAND() in MySQL instead of NEWID())
declare #userId int
select #userId = 1
declare #existingReviewCount int
select #existingReviewCount = COUNT(*) from Reviews where Id = #userId
declare #requiredRowCount int
select #requiredRowCount = 3 - #existingReviewCount
select top (#requiredRowCount) Id from Users
where #userId != Id
order by NEWID()
Now replace #userId with 1 and it will return an empty set.
This seems to be essentially a top n per group problem. There are a few ways to solve that. Here is a quick and dirty way that will give you a comma separated list of id's that you need. If you want to just explode these in your code you are good to go.
select u.*,
-- r_counts.cnt as reviews_count,
substring_index(
group_concat(u_rev.id order by rand()),
',',
greatest(3-r_counts.cnt,0)) as reviewers
from users u
join users u_rev on u.id != u_rev.id
left join (
select u.id, count(r.id) as cnt
from users u
left join reviews r on u.id = r.id
group by u.id
) r_counts on r_counts.id = u.id
left join (
select u.id, count(r.id) as cnt
from users u
left join reviews r on u.id = r.reviewer
group by u.id, r.reviewer
) as did_review_counts
on did_review_counts.id = u_rev.id
where u.id = 11
and did_review_counts.cnt < 3
group by u.id;
If you need the results another way, google "top n per group mysql" and check out some of the solutions there.
Note: the 3 above would be your review number target. Edit: Now this would need to be run only 1 at a time. Then rerun after each review was done.

Join results from two MySQL tables

I have two MySQL tables from which i can't get joined results.
The first one is just a list of companies and their names:
companies:
____________________________
companyid | companyname |
1 comp1
2 comp2
The second one is the list of user roles:
roles:
_____________________________________________________
roleid | uid | role | companyid | suspended |
1 1 dir 1 0
2 1 manag 2 0
I want to get a result that would look like:
_______________________
companyname | role |
comp1 dir
comp2 manag
My query below returns nothing...
SELECT companies.companyid, roles.role FROM companies INNER JOIN roles ON roles.companyid=companies.companyname where (uid = 1 and suspended = 0)
Check you joining condition.
Try this,
select a.companyname,b.role from companies a, roles b
where a.companyid=b.companyid and
(b.uid = 1 and
b.suspended <> 0);
You are joining roles.companyid=companies.companyname which is incorrect since companyname is a string, not an integer.
Try this instead:
SELECT companies.companyid, roles.role FROM companies INNER JOIN roles ON roles.companyid=companies.companyid where (uid = 1 and suspended <> 0);

SELECT value x WHERE x has same unknown value AND WHERE value y is in array

I have a table that stores product groups.
table "products_groups"
id | id_group | id_product
============================
1 | 1 | 1
2 | 1 | 2
3 | 1 | 3
4 | 2 | 1
5 | 2 | 2
6 | 3 | 2
I need a SQL query that will find the id_group that contains all id_product's that are in the given array.
product_ids = array(1); // should return no results
product_ids = array(1,2); // should only return id_group 2
product_ids = array(1,2,3); // should only return id_group 1
product_ids = array(1,2,3,4); // should return no results
I played/searched around, ended up stuck at
SELECT p1.id_group
FROM products_groups p1, products_groups p2
WHERE p1.id <> p2.id
AND p1.id_group = p2.id_group
AND (
p1.id_product = 1
OR p1.id_product = 2
OR p1.id_product = 3
)
But it obviously is not giving me the result I am looking for. I don't know if I am thinking too simple or too complex.
Notes: Of course the id_product values will be dynamically generated in the SQL. It will eventually be used with PHP/Codeigniter
Background info: Each product has a price, but products can be in a product group which has a package price. That is why I need to know for each order if the products are in a group.
This problem is called Relational Division
SELECT id_group
FROM products_groups
WHERE id_product IN (1,2)
GROUP BY id_group
HAVING COUNT(*) = 2
SQL of Relational Division
or something like this,
SELECT id_group
FROM products_groups
GROUP BY id_group
HAVING SUM(id_product IN (1,2)) = COUNT(*) AND
COUNT(*) = 2
SQLFiddle Demo

Query to find all no associated entities

I have two tables, USERS and USERS_ASSOCIATIONS
For simplistic sake they look like this
USERS USERS_ASSOCIATION
----- --------------------------
|id | |id |fk_id| fk_assoc_id |
----- --------------------------
| 1 | | 1 | 1 | 2 |
| 2 | | 2 | 1 | 3 |
| 3 | --------------------------
| 4 |
-----
users can be associated to each other so for this example user with ID of 1 is associated to user 2 and 3 but not to 4.
I am trying to create a query that will find all users that are not associated to a specific user. So for the example of user 1 the result of the query will be 4 , if the user were 2 then the result of the query would be 1 , 3 , and 4 because user two has no associations.
So far have have this
SELECT * from USERS WHERE AND USERS.id <> ( SELECT * FROM USERS_ASSOCIATION as UA INNER JOIN USESR as U ON UA.fk_assoc_id = U.id AND UA.fk_id = 1);
I know this is wrong, the sub query returns a list of all of the USER_ASSOCIATIONS that are found for a particular user.
Looks like you just need a "not in" rather than the "<>"... This should return the list of users that "are not in" the subquery.
SELECT *
from USERS
WHERE AND USERS.id not in ( SELECT * FROM USERS_ASSOCIATION as UA INNER JOIN USESR as U ON UA.fk_assoc_id = U.id AND UA.fk_id = 1);
select *
from USERS
where id <> 1
and id not in (select fk_id from USERS_ASSOCIATION where fk_assoc_id = 1)
and id not in (select fk_assoc_id from USERS_ASSOCIATION where fk_id = 1)