Mysql messages system - how query? - mysql

I work on my tiny messages system on MySQL.
I have the table "con":
id | user1 | user2
1 | 1 | 3
2 | 3 | 5
3 | 2 | 3
4 | 5 | 8
On, example logged user id = 3.
I want get from table user's id which user_id = 3 is talking.
Correct result for user id = 3:
id | user_id
1 | 1
2 | 5
3 | 2
Thanks :)

You can do this in the where and with a condition in the select:
select id,
(case when user1 = 3 then user2 else user1 end) as user
from con
where 3 in (user1, user2);
This only requires one pass over the table.

Select the two groups of results (user1 = 3 and user2 = 3) and merge them together using UNION.
SELECT id, user1 AS user_id
FROM con
WHERE user2 = '3'
UNION
SELECT id, user2 AS user_id
FROM con
WHERE user1 = '3'

SELECT a.id, a.user1 AS user_id FROM con a WHERE user2 = 3
UNION
SELECT b.id, b.user2 AS user_id FROM con b WHERE user1 = 3

Related

two separate joins on one query; counting all messages to and from one user

I'm trying to figure out how I can show the count of messages sent a received by each user in a database. I have a table of users, where I want to pull the userid (called id) and the username, and a table of messages, which have an id, a fromid (fkey from users), a toid (fkey from users), and a body (the text of the message). The result would be like this:
id | username | tocount | fromcount
1 | user1 | 2 | 3
2 | user2 | 1 | 1
3 | user3 | 3 | 1
4 | lastuser | 0 | 1
How can I accomplish this? I've tried a number of different join combinations, but I end up getting inaccurate results.
One method uses union all followed by group by:
select userid, sum(fromnum) as fromcnt, sum(tonum) as tocnt
from ((select fromid as userid, 1 as fromnum, 0 as tonum
from messages
) union all
(select toid as userid, 0, 1
from messages
)
) ft
group by userid;

Group by either column

Sample Table:
User1 | User2
------+------
123 | 555
123 | 1
123 | 2
456 | 2
555 | 456
12 | 123
12 | 456
Input: I enter the list (123,456) to look at rows containing either of those values. Then I want MySQL to check the opposite/other column in that row, and group the output by that value.
Output:
User | Count(*)
-----+---------
555 | 2
2 | 2
1 | 1
555 count is 2 because row 123, 555 and row 555, 456 both contain one of the inputs: 123 and 456.
I've tried looking at the CASE keyword, because the obvious obstacle here is grabbing the opposite/remaining column and using that as one of the returned values.
Completely wrong, but one of my half-finished approaches.
SELECT user, count(*)
FROM friendships
WHERE User1 IN (123,456) AS user
OR User2 IN (123,456) AS user
the tricky part with this is your criteria is the IN() is looking in the users1 column and then tries to find duplicates in the user2 column... so you need a join with a UNION
SELECT user2, COUNT(*)
FROM
( SELECT user1, user2
FROM friends f
WHERE f.user1 IN(123, 456)
UNION ALL
SELECT f.user1, f.user2
FROM friends f
JOIN friends f1 ON f1.user1 = f.user2
WHERE f.user1 IN(123, 456)
)t
GROUP BY user2;
WORKING FIDDLE

MySQL query to return a conversation between 2 users

I'm trying to generate a list of messages in a conversation between 2 users. There are 2 relevant tables:
inbox
id_msg
date_msg
id_user_from
id_user_to
message
user
id_user
name
In this example, I'm user ID 1 (Jon), having a chat with user ID 2 (Anna).
SELECT SQL_CALC_FOUND_ROWS
i.id_msg,
i.id_user_from AS from,
i.id_user_to AS to,
u.name,
i.message,
FROM inbox AS i
INNER JOIN user AS u ON (u.id_user = i.id_user_from OR u.id_user = i.id_user_to)
WHERE (i.id_user_from = 1 AND i.id_user_to = 2) OR (i.id_user_from = 2 AND i.id_user_to = 1)
ORDER BY date_msg DESC
The current problem is that the results are repeated. I'm receiving 2 repeated id_msg values each linked to each user's name, e.g.:
id | id_from | id_to | name | message
1 | 1 | 2 | Jon | Hi Anna!
1 | 1 | 2 | Anna | Hi Anna!
2 | 2 | 1 | Jon | Hello Jon
2 | 2 | 1 | Anna | Hello Jon
I should be receiving this:
id | id_from | id_to | name | message
1 | 1 | 2 | Jon | Hi Anna!
2 | 2 | 1 | Anna | Hello Jon
Any ideas? Thanks!
If you only want the from user to show up, you don't need to match the to user in the join condition;
SELECT SQL_CALC_FOUND_ROWS
i.id_msg, i.id_user_from from_id, i.id_user_to to_id,
u_from.name from_name, u_to.name to_name, i.message
FROM inbox AS i
INNER JOIN user AS u_from ON u_from.id_user = i.id_user_from
INNER JOIN user AS u_to ON u_to.id_user = i.id_user_to
WHERE (i.id_user_from = 1 AND i.id_user_to = 2)
OR (i.id_user_from = 2 AND i.id_user_to = 1)
ORDER BY date_msg DESC
This is because your join is using an or. You really have two names, so they should both be in the query. So, I think this might fix your problem:
SELECT SQL_CALC_FOUND_ROWS i.id_msg, i.id_user_from AS from, i.id_user_to AS to,
ufrom.name as fromname, uto.name as toname, i.message,
FROM inbox i INNER JOIN
user ufrom
ON ufrom.id_user = i.id_user_from
user uto
ON uto.id_user = i.id_user_to
WHERE (i.id_user_from = 1 AND i.id_user_to = 2) OR
(i.id_user_from = 2 AND i.id_user_to = 1)
ORDER BY date_msg DESC;

MySQL JOIN multiple results

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

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)