mysql join table on condition that compares two records - mysql

I am trying to create a left join with an on statement that has requirements in two records. Essentially, i am trying to determine if two users are members of a group via a groupId and userId
This is where I started
LEFT JOIN groups AS g1 ON g1.status = "active" AND g1.userId = "user1"
LEFT JOIN groups AS g2 ON g2.status = "active" AND g1.groupId = g2.groupId AND g2.userId = "user2"
The problem with the two the joins is that the first join is going to select the first available record that meets this condition. The second join will be generated from the first join due to the groupId. This may work if the groupId for the second is the same. However, this is not the case as users can be members of a bunch of different groups. Essentially, I am trying to create a join statements that attempts to find a similar group (via groupId) between the two users. If the group is not there is just returns null (reason for me using left join).
How can I create a join that compares a similar groupId between two records?
Thanks
Here is the entire query
SELECT u*, code.description AS statusText...., CASE WHEN u.id = "ME" THEN 1 ELSE g2.groupId END AS public
FROM users AS u
LEFT JOIN codes AS code ON code.code = u.status
LEFT JOIN groups AS g1 ON g1.status = "active" AND g1.userId = u.id
LEFT JOIN groups AS g2 ON g2.status = "active" AND g1.groupId = g2.groupId AND g2.userId = "ME"
WHERE u.id = :u
LIMIT 1
Where ME equals your user login and :u is the user we are looking up
Essentially, i am trying to compare if you and another user a part of the same group.
Results would look like an array with the following details
[userId] => 1
[date] => 2015-06-16 00:00:00
[player] => first name
[email] => email
....
[public] => 1 <- this is the important variable i am trying to get. I want to return a number that i will treat as a boolean. The number tells me that i have a group in common with the user i am looking up
The group table
| userId | groupId | colum1 | ... |
+--------+---------+--------+------+
| 1 | 1 | 1 | ALL |
+--------+---------+--------+------+
| 1 | 5 | 1 | NONE |
+--------+---------+--------+------+
| 6 | 1 | 1 | ALL |
+--------+---------+--------+------+
| 2 | 3 | 1 | NONE |
+--------+---------+--------+------+
From this example of the table data you can see that users 1 and users 6 are part of the same group. If this was the only data in the table than my join statement would work. However, this is not the case and if user 1 and ME were both part of group 5 my statement would fail
I would prefer not to use a subquery

Related

MySQL select row with two matching joined rows from another table

Hey I try to select a row from a table with two matching entries on another one.
The structure is as following:
----------------- ---------------------
| messagegroups | | user_messagegroup |
| | | |
| - id | | - id |
| - status | | - user_id |
| | | - messagegroup_id |
----------------- | |
---------------------
There exist two rows in user_messagegroup with the ids of two users and both times the same messagegroup_id.
I would like to select the messagegroup where this two users are inside.
I dont get it.. so I would appreciate some help ;)
The specification you provide isn't very clear.
You say "with the ids of two users"... if we take that to mean you have two user_id values you want to supply in the query, then one way to to find the messagegroups that contain these two specific users:
SELECT g.id
, g.status
FROM messagegroups g
JOIN ( SELECT u.messagegroup_id
FROM user_messagegroup u
WHERE u.user_id IN (42, 11)
GROUP BY u.messagegroup_id
HAVING COUNT(DISTINCT u.user_id) = 2
) c
ON c.messagegroup_id = g.id
The returned messagegroups could also contain other users, besides the two that were specified.
If you want to return messagegroups that contain ONLY these two users, and no other users...
SELECT g.id
, g.status
FROM messagegroups g
JOIN ( SELECT u.messagegroup_id
FROM user_messagegroup u
WHERE u.user_id IS NOT NULL
GROUP BY u.messagegroup_id
HAVING COUNT(DISTINCT IF(u.user_id IN (42,11),u.user_id,NULL)) = 2
AND COUNT(DISTINCT u.user_id) = 2
) c
ON c.messagegroup_id = g.id
For improved performance, you'll want suitable indexes on the tables, and it may be possible to rewrite these to eliminate the inline view.
Also, if you only need the messagegroup_id value, you could get that from just the inline view query, without the need for the outer query and the join operation to the messagegroups table.

Get groups(only allowed) for user using single mysql query

Recently i came across one interesting problem. Let add some tables namely User, Group and their Join table User_Group. Following is the dummy representation of the tables
User
user_id | user_name
1 | a
2 | b
3 | c
Group
group_id | group_code
1 | G1
2 | G2
3 | G3
User_Group
userid | groupid | exclusion
1 | 1 | 0
1 | 2 | 0
2 | 1 | 1
Well the problem is to get the list of groups for the user on which he have access on.
General assumption of the user access in the group is :
If user have no entry in User_group join table the user has access on
all groups (ie. G1, G2 and G3)
If user have entry in User_Group with
exclusion 0 then that user have access in that group only (user 1 has
access in G1 and g2)
If user have entry in User_Group with exclusion
1 then that user have no access in that group but he has access in
other all other groups(user 2 is denied only on g1 and should have
access in G2 and G3)
Here's the query that get me results for case 1 & 3 but fails for case 2.
SELECT g.id, g.code, ug.user_groups_id, ug.exclusion FROM group g
LEFT JOIN user_group ug ON ug.group_id = g.id
AND ug.user_id = 1 -- works for user 2 and 3 but fails for user 1
WHERE (ug.exclusion = 0 OR ug.exclusion IS NULL)
Please suggest if we can get the list of groups using just single query
Interesting problem.
You can solve this by generating a list of all users and all groups (using cross join). Then use left join to match back to the list of groups that are known. The logic is then:
Case 1 is to use all the groups when there is no match.
Case 2 is to use only the groups that do match.
Case 3 is to use only the groups that do not match.
Here is the query:
select u.userid,
(case when max(ug.exclusion) is null -- all groups
then group_concat(g.groupcode)
when max(ug.exclusion) = 0 -- only those included
then group_concat(case when ug.groupid is not null then g.groupcode end)
when max(ug.exclusion) = 1 -- exclude these
then group_concat(case when ug.groupid is null then g.groupcode end)
end)
from users u cross join
groups g left join
user_groups ug
on u.userid = ug.userid and g.groupid = ug.groupid
group by u.userid;
Do note that this only uses the exclusion flag in the aggregate -- basically assuming that it is the same on all rows in user_groups. In fact, this flag could go at the user level rather than at the user_groups level. This assumption is consistent both with the sample data and the explanation of the problem.

MySQL join multiple rows from one table while selecting only a single row from the others

Maybe a bit of a strange title description, but i basically want to achieve something the GROUP_CONCAT() function does, only then keep the double entries.
I have four tables i want to join, client, doctor, physio and records
Depending on the variable $client i want to get the client details, attending doctor and therapist (one single row from three tables) and join all records for that user.
Say that in this case the $client = 1. The records table has five records where the column r_client_id = 1. If i run a query like below i only get one record from the records table, namely the first occurrence where r_client_id = 1 (which makes sense of course):
SELECT
client.c_id, client.c_name
doctor.d_name,
physio.p_name,
records.r_record
FROM
adm_clients AS client
INNER JOIN
norm_client_doctor AS ncd ON ncd.ncd_client_id = client.c_id
INNER JOIN
adm_doctor AS doctor ON doctor.d_id = ncd.ncd_doctor_id
INNER JOIN
norm_client_physio AS ncp ON ncp.ncp_client_id = client.c_id
INNER JOIN
adm_physio AS physio ON physio.p_id = ncp.ncp_physio_id
LEFT JOIN
adm_doctor_records AS records ON records.r_client_id = client.c_id
WHERE
client.c_id = '".$client."'
Now assume the five records where r_client_id = 1 are like so:
+------+-------------+-------------------+----------+
| r_id | r_client_id | r_record | r_date |
+------+-------------+-------------------+----------+
| 1 | 1 | regular visit | 10/10/12 |
+------+-------------+-------------------+----------+
| 3 | 1 | emergency control | 24/10/12 |
+------+-------------+-------------------+----------+
| 7 | 1 | regular visit | 08/09/12 |
+------+-------------+-------------------+----------+
| 18 | 1 | delivery | 03/01/12 |
+------+-------------+-------------------+----------+
| 20 | 1 | health checkup | 10/12/11 |
+------+-------------+-------------------+----------+
I want my output to be in an array like so:
Client 1
- Name Doctor
- Name Physio
Records
- Emergency control, 24/10/12
- Regular visit, 10/10/12
- Regular visit, 08/09/12
- Delivery, 03/01/12
- Health checkup, 10/12/11
The closest one i can image is a to add a GROUP_CONCAT() on the records, but that, of course, groups the 'regular visit', so i'll get 4 rows instead of 5
GROUP_CONCAT(DISTINCT records.r_record SEPARATOR '|')
[..]
echo(str_replace("|","<br>",$show->r_record));
Anybody an idea how to display all the matching records? I have the feeling i'm close, but i'm out of options by now..
Edit:
I forgot to mention that when i remove the DISTINCT, it displays all the records twice..
SOLVED:
Got it working like so:
GROUP_CONCAT(DISTINCT
CONCAT (records.r_date, '~', records.r_record, '~', records.r_paraph)
SEPARATOR '|') AS clientDoctorRecords,
Try:
SELECT
client.c_id, client.c_name
doctor.d_name,
physio.p_name,
GROUP_CONCAT(records.r_record)
FROM
adm_clients AS client
INNER JOIN
norm_client_doctor AS ncd ON ncd.ncd_client_id = client.c_id
INNER JOIN
adm_doctor AS doctor ON doctor.d_id = ncd.ncd_doctor_id
INNER JOIN
norm_client_physio AS ncp ON ncp.ncp_client_id = client.c_id
INNER JOIN
adm_physio AS physio ON physio.p_id = ncp.ncp_physio_id
LEFT JOIN
adm_doctor_records AS records ON records.r_client_id = client.c_id
WHERE
client.c_id = '".$client."'
GROUP BY
client.c_id
If you want r_date to come along with record in one column, then you can use plain CONCAT first and then do a GROUP_CONCAT on it.

Problem with one of my LEFT JOIN and SUM the result of it

So I got a question about LEFT JOIN, this code returns different values for totalPoints depending on if the user got the group or not. (if user don't got group or event it returns the correct value)
I just want to grasp how to get the LEFT JOIN flow_has_vote ON flow_has_vote.flow_id=flows.id to work every time. I did a solution before with three query's, one that gets the group and event rule, one that checks if the user got the group or event considering the security and one to get the flow...
And I guess I could solve this by having two query's, one that gets the group and event rules and also check if the user got the group and event and then one that gets the flow depending on the user should have access to it.
Right now I'm getting every information needed in ONE query and then checking with IF statements if it should be printed or not...
So, my question is, is it possible to get the SUM(flow_has_vote.points) AS totalPoints to work this way? And do you know how?
And also I'm a bit curios, is one query the best way to work with this? Would it be justified to use two when you take into account performance?
SELECT
flows.id AS flowId,
flows.security,
SUM(flow__has__vote.points) AS totalPoints,
users.id AS userId,
users.alias,
flows.event_id AS eventId,
events.group_id AS groupId,
events.membershipRules AS eMR,
groups.membershipRules AS gMR,
user__has__group.permission AS userHasGroup,
user__has__event.permission AS userHasEvent
FROM
users,
events LEFT JOIN user__has__event ON user__has__event.user_id = '.$userId.',
groups LEFT JOIN user__has__group ON user__has__group.user_id = '.$userId.',
flows LEFT JOIN flow__has__vote ON flow__has__vote.flow_id=flows.id
WHERE
flows.user_id = users.id AND
events.id = flows.event_id AND
groups.id = events.group_id AND
flows.id='.$flowId
And if you wonder what the SQL-statement is doing, getting the information for the flow(post), the information about the event and group that the flow is in, checking the user access to the group and event and also getting all the votes for the flow...
This is how the tables looks like...
FLOWS id,security,event_id,user_id
USERS id, alias
EVENTS id, name group_id, membershipRules
GROUPS id, name, membershipRules
USER__HAS__GROUP user_id,group_id,permission
USER__HAS__EVENT user_id,event_id,permission
FLOW__HAS__VOTE flow_id,user_id,points
This is the result I wish for...
+--------+----------+-------------+--------+--------+---------+---------+-----+-----+--------------+--------------+
| flowId | security | totalPoints | userId | alias | eventId | groupId | eMR | gMR | userHasGroup | userHasEvent |
+--------+----------+-------------+--------+--------+---------+---------+-----+-----+--------------+--------------+
| 1 | 2 | 1337 | 5 | Pontus | 15 | 2 | 2 | 2 | 4 | 4 |
+--------+----------+-------------+--------+--------+---------+---------+-----+-----+--------------+--------------+
and one more example...
+--------+----------+-------------+--------+--------+---------+---------+-----+-----+--------------+--------------+
| flowId | security | totalPoints | userId | alias | eventId | groupId | eMR | gMR | userHasGroup | userHasEvent |
+--------+----------+-------------+--------+--------+---------+---------+-----+-----+--------------+--------------+
| 1 | 2 | 1337 | 6 | Kezia | 15 | 2 | 2 | 2 | null | null |
+--------+----------+-------------+--------+--------+---------+---------+-----+-----+--------------+--------------+
Enjoy your life ~ Pontus
So, basically the main point (IMHO) is not to include conditions on tables you LEFT JOINed in the WHERE clause, since this makes the LEFT JOIN behave like an INNER JOIN.
Start with trying this query (although I am sure you will have to make adjustments as I am not sure exactly what you want as a result, more about this later):
SELECT
flows.id AS flowId,
flows.security,
SUM(flow__has__vote.points) AS totalPoints,
users.id AS userId,
users.alias,
flows.event_id AS eventId,
events.group_id AS groupId,
events.membershipRules AS eMR,
groups.membershipRules AS gMR,
user__has__group.permission AS userHasGroup,
user__has__event.permission AS userHasEvent
FROM users,
LEFT JOIN user__has__event
ON user__has__event.user_id = users.id,
LEFT JOIN events
ON user__has__event.event_id = events.id
LEFT JOIN user__has__group
ON user__has__group.user_id = users.id,
LEFT JOIN groups
ON user__has__group.group_id = groups.id
AND groups.id = events.group_id
LEFT JOIN flows
ON flows.user_id = users.id
AND events.id = flows.event_id
AND flows.id='.$flowId'
LEFT JOIN flow__has__vote
ON flow__has__vote.flow_id = flows.id
WHERE users.id = '.$userId.'
GROUP BY users.id
Here, I LEFT JOINed everything to the user, and also grouped by the user. I have a feeling you will want to add columns to the group by (flows.id?, events.id?)
Also, you may want to turn some of the LEFT JOINs to JOIN, so you will get only users who have a 'flow', for example.

Select one row from this table and all related rows from other table

table user
id | name | address
3 | Jacko | 33A Herewini
table user_photo
id | userid | thumb | full
1 | 3 | 3k1j_thumb.jpg| 3k1j.jpg
1 | 3 | 3k1j_thumb.jpg| 3k1j.jpg
2 | 14 | 44r_thumb.jpg| 44r.jpg
2 | 14 | 55t_thumb.jpg| 55t.jpg
2 | 14 | 12f_thumb.jpg| 12f.jpg
I got the user id, I want to select his name and address and all his photos
PS: what tool/software you use to draw the table line (the +---+) ?
Edit: then how would you put the name in a div and all the photos in a ul
my html look like this
<div class='name'></div>
...600 elements...
<ul class='photos'></ul>
To get the address, you can just query that table:
SELECT * FROM user WHERE id = 3
To get the photos, you can query the user photo table:
SELECT * FROM user_photo WHERE userid = 3
If you want to get everything at once, you can join the two tables together on the user id:
SELECT * FROM user u
LEFT JOIN user_photo up ON up.userid = u.id
WHERE u.id = 3
Note though that the address info will of course be duplicated on every row
Learn about LEFT JOIN, see e.g. http://www.postgresql.org/docs/8.2/static/sql-select.html
SELECT * FROM user AS u LEFT JOIN user_photo AS p ON p.userid = u.id
Adjust as needed.
To combine the results of two tables linked by one column (id in this case), you would use a JOIN.
You can read up on JOINs in the MySQL Reference Manual
SELECT *
FROM user u
LEFT OUTER JOIN user_photo up ON up.userid = u.id