SQL select users that belong to two groups - mysql

I have a list of persons in a table. I then have another table where I correlate each person to one or more groups. Some persons have only one entry in the groups table but some have multiple.
I am now trying to SELECT list of persons that are in two specific groups. Person must be in BOTH groups in order to qualify.
My table with the basic information on the persons is base and the table with the group correlation is groups_registration. In fact I also have a third table where the groups names and further information are stored but it is not required for this query.
The groups I am trying to gather in this example are 4 and 11.
What I tried initially was:
SELECT base.*, groups_registration.person_id, groups_registration.group_id
FROM base
INNER JOIN groups_registration
ON base.id = groups_registration.person_id
WHERE (groups_registration.group_id = '4' AND groups_registration.group_id = '11')
ORDER BY base.name
This did not get my any response, I assume because no single row contains both group_id = 4 and group_id 11.
I have been searching through stackoverflow with no joy. Do you guys have any ideas?

Obviously, no row has both values. Use group by:
SELECT gr.person_id, groups_registration.group_id
FROM groups_registration gr
WHERE gr.group_id IN (4, 11)
GROUP BY gr.person_id
HAVING COUNT(DISTINCT gr.group_id) = 2;
I'll let you figure out how to join in the additional information from base.
Notes:
Use table aliases to make it easier to write and read queries.
Presumably, the ids are numbers. Compare numbers to numbers. Only use single quotes for date and string constants.
IN is better than long chains of OR/=.

You can use joins as shown below:
SELECT A.*, B.person_id, B.group_id
FROM base A
INNER JOIN
(SELECT gr.person_id, groups_registration.group_id
FROM groups_registration gr
WHERE gr.group_id IN (4, 11)
GROUP BY gr.person_id
HAVING COUNT(DISTINCT gr.group_id) = 2) B
ON A.id = B.person_id;
This will give you all the desired fields.

Related

Select * but group by a certain column, only for a certain column

Here is my table structure
Company table
|id|name|company_id
Images table
|id|file|type|company_id
Now, I am trying to group by type from images, it can have 2 types (logo|banner), but if I use group by type that will do it for all the rows, not just where the id = company id
My query so far:
SELECT * FROM companies c
LEFT JOIN images i
ON i.comp_id = c.id
As mentioned above if I do the following
SELECT * FROM companies c
LEFT JOIN images i
ON i.comp_id = c.id
group by type
It will group ALL rows (as you would expect), so how can I group by type and only where the company_id (from images) is the same?
You can group by multiple fields:
GROUP BY `type`, `company_id`
Sidenote: using GROUP BY without any aggregate functions is usually a mistake, and most RDBMS will not let you GROUP BY without specifying all non-aggregated fields; but am I correct assuming you are working your way towards that?

Combine rows and put their values in seperate column in mysql

I want to combine two or more rows which have the same name and put their values in two separate column. The problem can be clearer with the following images:
My expected output is:
You can check the fiddle here: fiddle
What I have tried so far is with the MySQL code:
Select subjects, mark_score, activity
FROM(
SELECT subjects, mark_score,
(SELECT regd, subjects, mark_score
FROM exo_i WHERE entry='7' and regd='19') as activity
FROM exo_i WHERE regd='19' GROUP BY subjects)t
As discussed in the comment, the requirement is to display "marks" for the "FA1" entry and "activity" for the "SA1" activity. Assuming there can't be multiple rows with these values (i.e., the combination of regd, subjectsa and activity is unique), you could have a subquery for each of these activities, and join them:
SELECT a.regd, a.subjects, a.marks, b.activity
FROM (SELECT regd, subjects, marks
FROM mytable
WHERE entry = 'FA1') a
JOIN (SELECT regd, subjects, marks AS activity
FROM mytable
WHERE entry = 'SA1') b ON a.regd = b.regd AND a.subjects = b.subjects

MySQL - 3 tables, is this complex join even possible?

I have three tables: users, groups and relation.
Table users with fields: usrID, usrName, usrPass, usrPts
Table groups with fields: grpID, grpName, grpMinPts
Table relation with fields: uID, gID
User can be placed in group in two ways:
if collect group minimal number of points (users.usrPts > group.grpMinPts ORDER BY group.grpMinPts DSC LIMIT 1)
if his relation to the group is manually added in relation tables (user ID provided as uID, as well as group ID provided as gID in table named relation)
Can I create one single query, to determine for every user (or one specific), which group he belongs, but, manual relation (using relation table) should have higher priority than usrPts compared to grpMinPts? Also, I do not want to have one user shown twice (to show his real group by points, but related group also)...
Thanks in advance! :) I tried:
SELECT * FROM users LEFT JOIN (relation LEFT JOIN groups ON (relation.gID = groups.grpID) ON users.usrID = relation.uID
Using this I managed to extract specified relations (from relation table), but, I have no idea how to include user points, respecting above mentioned priority (specified first). I know how to do this in a few separated queries in php, that is simple, but I am curious, can it be done using one single query?
EDIT TO ADD:
Thanks to really educational technique using coalesce #GordonLinoff provided, I managed to make this query to work as I expected. So, here it goes:
SELECT o.usrID, o.usrName, o.usrPass, o.usrPts, t.grpID, t.grpName
FROM (
SELECT u.*, COALESCE(relationgroupid,groupid) AS thegroupid
FROM (
SELECT u.*, (
SELECT grpID
FROM groups g
WHERE u.usrPts > g.grpMinPts
ORDER BY g.grpMinPts DESC
LIMIT 1
) AS groupid, (
SELECT grpUID
FROM relation r
WHERE r.userUID = u.usrID
) AS relationgroupid
FROM users u
)u
)o
JOIN groups t ON t.grpID = o.thegroupid
Also, if you are wondering, like I did, is this approach faster or slower than doing three queries and processing in php, the answer is that this is slightly faster way. Average time of this query execution and showing results on a webpage is 14 ms. Three simple queries, processing in php and showing results on a webpage took 21 ms. Average is based on 10 cases, average execution time was, really, a constant time.
Here is an approach that uses correlated subqueries to get each of the values. It then chooses the appropriate one using the precedence rule that if the relations exist use that one, otherwise use the one from the groups table:
select u.*,
coalesce(relationgroupid, groupid) as thegroupid
from (select u.*,
(select grpid from groups g where u.usrPts > g.grpMinPts order by g.grpMinPts desc limit 1
) as groupid,
(select gid from relations r where r.userId = u.userId
) as relationgroupid
from users u
) u
Try something like this
select user.name, group.name
from group
join relation on relation.gid = group.gid
join user on user.uid = relation.uid
union
select user.name, g1.name
from group g1
join group g2 on g2.minpts > g1.minpts
join user on user.pts between g1.minpts and g2.minpts

SQL query: What groups is a given member NOT a member?

I have three tables in MySQL,
groups (key: group_id)
members (key: member_id)
group_member_relations key: group_id, member_id
The last table has combinations of members and groups (members that have joined that group).
I've been struggling with a way to perform a single query that gives me a list of members and groups that are NOT IN the group_member_relations table. (Basically I want to eventually ask the question "What groups is a given member not a member"). I can do this the hard way in code but was wondering if a single query was possible.
Not a SQL wiz at all, but have used it a lot over the last 20 years, mostly basic stuff. This is obviously over my head. Made many attempts over the last few days but, embarrassingly don't seem to get close.
Any pointers from the sql wizards out there..
Groups that a member is not in:
select *
from group
where id not in (
select group_id
from group_member_relations
where member_id = ?)
The following query will list all groups available and the members that are not present on each group. The query will also give all the columns for each table.
SELECT a.*, b.*
FROM members a
CROSS JOIN groups b
LEFT JOIN group_member_table c
ON a.memberID = c.memberID AND
b.groupID = c.groupID
WHERE c.memberID IS NULL OR -- actually this condition is already enough
c.groupID IS NULL
SQLFiddle Demo

MySQL returning results from one table based on data in another table

Before delving into the issue, first I will explain the situation. I have two tables such as the following:
USERS TABLE
user_id
username
firstName
lastName
GROUPS TABLE
user_id
group_id
I want to retrieve all users who's first name is LIKE '%foo%' and who is a part of a group with group_id = 'givengid'
So, the query would like something like this:
SELECT user_id FROM users WHERE firstName LIKE '%foo'"
I can make a user defined sql function such as ismember(user_id, group_id) that will return 1 if the user is a part of the group and 0 if they are not and this to the WHERE clause in the aforementioned select statement. However, this means that for every user who's first name matches the criteria, another query has to be run through thousands of other records to find a potential match for a group entry.
The users and groups table will each have several hundred thousand records. Is it more conventional to use the user defined function approach or run a query using the UNION statement? If the UNION approach is best, what would the query with the union statement look like?
Of course, I will run benchmarks but I just want to get some perspective on the possible range of solutions for this situation and what is generally most effective/efficient.
You should use a JOIN to get users matching your two criteria.
SELECT
user_id
FROM
users
INNER JOIN
groups
ON groups.user_id = users.users_id
AND groups.group_id = given_id
WHERE
firstName LIKE '%foo'
You don't need to use either a UNION or a user-defined function here; instead, you can use a JOIN (which lets you join one table to another one based on a set of equivalent columns):
SELECT u.user_id
FROM users AS u
JOIN groups AS g
ON g.user_id = u.user_id
WHERE g.group_id = 'givengid'
AND u.firstName LIKE '%foo'
What this query does is join rows in the groups table to rows in the users table when the user_id is the same (so if you were to use SELECT *, you would end up with a long row containing the user data and the group data for that user). If multiple groups rows exist for the user, multiple rows will be retrieved before being filtered by the WHERE clause.
Use a join:
SELECT DISTINCT user_id
FROM users
INNER JOIN groups ON groups.user_id = users.user_id
WHERE users.firstName LIKE '%foo'
AND groups.group_id = '23'
The DISTINCT makes sure you don't have duplicate user IDs in the result.