I am trying to formulate a query to select a conversation based on members passed in, this can be many participants.
Users can have multiple conversation with the same users in, and they can rename conversations.
Here is my messages members table:
CREATE TABLE `messages_members` ( `relation_id` int(11) NOT NULL, `user_id` int(11) NOT NULL, `date` datetime NOT NULL, `seen` smallint(6) NOT NULL, PRIMARY KEY (`relation_id`,`user_id`), KEY `seen` (`seen`,`date`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
And the messages relation table:
CREATE TABLE `messages_relation` ( `id` int(11) NOT NULL AUTO_INCREMENT, `sender_id` int(11) NOT NULL, `date` datetime NOT NULL, `name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=49 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
Messages table for reference:
CREATE TABLE `messages` ( `id` int(11) NOT NULL AUTO_INCREMENT, `relation_id` int(11) NOT NULL, `user_id` int(11) NOT NULL, `message` longtext COLLATE utf8mb4_unicode_ci NOT NULL, `date` datetime NOT NULL, `updated` smallint(6) NOT NULL, PRIMARY KEY (`id`), KEY `messages` (`relation_id`,`user_id`), KEY `date` (`date`) ) ENGINE=InnoDB AUTO_INCREMENT=135 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
My latest solution is not working right as it is selecting a wrong conversation based on members count passed in.
Only thing I can think of at the momemt is to take the limit off the query and then loop through each result and do some checks to see if the members match what was asked for.
I wonder if there is a more elegant solution using a query though to do this.
Here is my current query: EDIT: Updated query as was doing the count properly.
SELECT relation_id AS relation, GROUP_CONCAT(user_id) AS members, COUNT(user_id) AS selected_members, (SELECT COUNT(user_id) FROM messages_members WHERE relation_id = relation) AS total_members FROM messages_members WHERE user_id IN( ". $inQuery ." ) GROUP BY relation_id HAVING total_members = ? AND selected_members = ?
I am passing in each users_id from an array, and also the total count of the array to match total members.
For more clarity to try explain what im trying to do there could be multiple conversations with simialir participants:
- Conversation A : user1, user2, user3
- Conversation B : user1, user2
- Conversation C : user1, user2, user5
I am trying to just select conversation A based on the three users passed in, user1, user2, user3
Some sample data from the messages_members table: relation_id, user_id, date, seen
relation_id | user_id | date | seen |
---|---|---|---|
2 | 1 | 2020-11-18 19:16:54.000 | 0 |
5 | 4 | 2020-11-18 19:53:34.000 | 0 |
5 | 6 | 2020-11-18 19:53:34.000 | 0 |
14 | 1 | 2020-11-19 00:02:44.000 | 0 |
14 | 3 | 2020-11-19 00:02:44.000 | 0 |
19 | 1 | 2020-11-19 00:16:32.000 | 0 |
19 | 3 | 2020-11-19 00:16:32.000 | 0 |
20 | 3 | 2020-11-19 00:17:37.000 | 0 |
20 | 4 | 2020-11-19 00:17:37.000 | 0 |
21 | 1 | 2020-11-19 00:18:09.000 | 0 |
21 | 3 | 2020-11-19 00:18:09.000 | 0 |
21 | 6 | 2020-11-19 00:18:09.000 | 0 |
22 | 1 | 2020-11-19 00:18:45.000 | 0 |
22 | 4 | 2020-11-19 00:18:45.000 | 0 |
22 | 6 | 2020-11-19 00:18:45.000 | 0 |
23 | 1 | 2020-11-19 00:19:06.000 | 0 |
23 | 3 | 2020-11-19 00:19:06.000 | 0 |
23 | 4 | 2020-11-19 00:19:06.000 | 0 |
24 | 3 | 2020-11-19 00:19:42.000 | 0 |
24 | 4 | 2020-11-19 00:19:42.000 | 0 |
24 | 6 | 2020-11-19 00:19:42.000 | 0 |
25 | 3 | 2020-11-19 01:41:44.000 | 0 |
25 | 5 | 2020-11-19 01:41:44.000 | 0 |
43 | 1 | 2022-02-28 17:38:34.000 | 0 |
43 | 54 | 2022-02-28 17:38:35.000 | 0 |
46 | 1 | 2022-03-16 23:24:43.000 | 0 |
46 | 5 | 2022-03-16 23:24:43.000 | 0 |
47 | 1 | 2022-03-16 23:25:51.000 | 0 |
47 | 3 | 2022-03-16 23:25:51.000 | 0 |
47 | 5 | 2022-03-16 23:25:51.000 | 0 |
48 | 1 | 2022-03-17 00:19:26.000 | 0 |
2 | 5 | 2020-11-18 19:16:54.000 | 1 |
23 | 6 | 2020-11-19 00:19:06.000 | 1 |
47 | 6 | 2022-03-16 23:25:51.000 | 1 |
48 | 15 | 2022-03-17 00:19:26.000 | 1 |
54 | 3 | 2022-03-19 00:19:22.000 | 1 |
54 | 5 | 2022-03-19 00:19:22.000 | 1 |
55 | 1 | 2022-03-19 00:23:18.000 | 1 |
55 | 3 | 2022-03-19 00:23:18.000 | 1 |
55 | 5 | 2022-03-19 00:23:18.000 | 1 |
For some examples using the above data, relation_id is one conversation.
So using the bottom 3 examples,
- If i want to look for a conversation just between user 1 and user 3, it should not return relation_id 55, because it also has user 5 in it.
- Same as relation_id 47 as that has user 5 in it too.
- It should just return relation_id 14 which only has user1, and user3. There is more conversations just between user1 and user3 but I would just return the first one it finds.
Advertisement
Answer
I’m not a php guy, but something like this would return only conversations involving user_id 1 and user_id 3:
SELECT mm.relation_id , GROUP_CONCAT(mm.user_id) AS User_List FROM message_members mm WHERE mm.user_id IN (1,3) -- find user_id 1 or 3 AND NOT EXISTS ( -- Does not involve other user_id's SELECT NULL FROM message_members ex WHERE ex.relation_id = mm.relation_id AND ex.user_id NOT IN (1,3) -- exclude user_id 1 or 3 ) GROUP BY mm.relation_id -- Where "2" is the unique number of users -- i.e. Find user 1 and user 2 = 2 distinct user_id's HAVING COUNT(DISTINCT mm.user_id) = 2 ;
Results:
relation_id | User_List ----------: | :-------- 14 | 1,3 19 | 1,3
db<>fiddle here