Conversations SQL Structure (Group chat) - mysql

In the past week I have been trying to come up with the best structure for a conversations chat, after doing a lot of searching, and trying to find better answers. The current structure I have so far, looks like this:
It must be possible to:
Have up to (n) participants in a group. (Let's say n = 5)
Check if a conversation already exists between the participants (this has been bothering me..)
Show the latest conversations, sorted by unread and timestamp of last message (should work)
Show the latest conversations (sort by unread and timestamp)
SELECT * FROM chat_participants WHERE pageId = ? AND isMuted=0 ORDER BY isRead DESC, lastmod DESC
Check if a conversation already exists between participants
The client user will be able to send a new message to a specific end user, or start a new group chat with up to 5 participants. Instead of starting a new conversation each time, I would like to check whether or not the conversation exists already, to append the message there instead. I hope it makes sense. This specific feature has been bothering me pretty much all the time, when I have been trying to come up with a good structure. Does anyone have an idea to solve this "peacefully" with this structure, or do you have better ideas? Thanks :)
I tried things like checking if the page id's existed in the chat_participants with the same chat id, but this brings up its own disadvantages. This is something I tried, but obviously doesn't work as intended, as it returns (x) records, which could be entirely different chatId values.
SELECT * FROM chat_participants WHERE pageId IN(<id1>,<id2>)
I appreciate your input on this. Have a great day ahead! :)
Edit (Answer to #Gordon Linoff)
Example of recipients table:
I used your code such as this: (This one works)
SELECT cp.chatId FROM chatParticipants cp GROUP BY cp.chatId HAVING GROUP_CONCAT(pageId ORDER BY pageId) IN('p1,p2')
But when I try to check another conversation, with other recipients, such as below, it does not.
SELECT cp.chatId FROM chatParticipants cp GROUP BY cp.chatId HAVING GROUP_CONCAT(pageId ORDER BY pageId) IN('deb47fba9bc6936ed76e831578baa480,123')
Also, why does it only seem to work when the values are wrapped within the same quotation marks? E.g if this is used, nothing is returned as well IN ('p1', 'p2')
Edit 2
So I combined some of the suggested answers here, and came up with this query.
SELECT chatId FROM chatParticipants GROUP BY chatId HAVING SUM(pageId NOT IN('deb47fba9bc6936ed76e831578baa480','123')) = 0
It works, sort of, the only issue I have found so far with this above query, is that something like the below query, still returns a chatId value. (Even though (144) does not exist in chatParticipants)
SELECT chatId FROM chatParticipants GROUP BY chatId HAVING SUM(pageId NOT IN('deb47fba9bc6936ed76e831578baa480','144','123')) = 0
Any ideas on how to solve this?

If you want to know if a chatId exists for a specific group of participants then one method is aggregation. For instance, to find chats where exactly 1, 2, and 3 participate:
select cp.chatid
from chat_participants cp
group by cp.chatid
having group_concat(pageid order by pageid)= '1,2,3';

Related

Finding Max Date Along with User ID with One Group By in MySQL

I am trying to figure out the number of visits a site has, the most recent visit and who the user was in the most recent visit. The only item I'm unable to get at the moment is the user who visited the site most recently.There is a user_id column in the data. My current query is below. I'm thinking I will have to use a subquery but want to see if someone has a better method.
Select
site_id,
COUNT(id),
MAX(date_visit)
FROM site_views
GROUP BY 1
The most efficient method with the right indexes is usually:
select sv.*
from site_views sv
where sv.date_visit = (select max(sv2.site_visit) from site_views sv2 where sv2.site_id = sv.sidte_id);

Chat mySQL table, select statement to find unique conversations

I have a table of chat messages:
id, nToUserID, nFromUserID, strMessage
I'm trying find unique occurrences of messages between two users. NOT all the messages, just, is there at least one message to a user or from a user. I'll use this to show a list of "conversations" which could then be clicked to view the full chat thread.
I tried using a DISTINCT select, but that appeared to still give me multiple records between the same users.
I thought about a left JOIN, but again that appears to give me multiple or empty records.
If I understand correctly, you could use least() and greatest():
select distinct least(nToUserID, nFromUserID), greatest(nToUserID, nFromUserID)
from t;
If you want the other users, then use:
select distinct (case when nToUserID = ? then nFromUserID else nToUserID end) as userID
from t
where ? in (nToUserID, nFromUserID);
The ? is the id of the user you want the connections to.
You can use the count function to get the unique id pairs and their respective number of conversations.
SELECT nFromUserId, nToUserId, count(id) FROM `table` GROUP BY nFromUserId, nToUserId
Though this doesn't work if you need to count nFromUserId and nToUserId interchangeably.

SQL query help( Yes,I have already tried nested select)

this is a screenshot of the database I am talking about
So suppose I have a database full of people with their ID numbers along with a year in which they made an entry plus their favorite show in that year. The years are always in the range 2014-2018, but not every one has an entry for each year. How can I count the total number of people who have consistently had the same show as their favorite show over all the years they have been recorded for.
I tried doing a nested selected but I kept getting error. I have checked other SQL related questions here talk about calculate 'change over the years' but none of those answers are compatible with my database and the solution wasn't transferable.
I think you need something like this:
See my SQLFiddle
select id, favorite_show, count(id) as total from people
group by id, favorite_show
having count(id) > 1
Hmmm . . . this gets the people who have only one show:
select count(*)
from (select person
from t
group by person
having min(show) = max(show)
) p;
You can count the number of different favorite shows someone has, and if that's 1 then they've had the same favorite every time.
SELECT COUNT(*)
FROM (SELECT 1
FROM yourTable
GROUP BY person_id
HAVING COUNT(DISTINCT favorite_show) = 1) AS x

ORDER BY # of rows with same option ID from different table

I've made a voting script which already works, but I wanted to practice some MySQL to try and do sorting/filtering of the results via SQL queries instead of getting an entire table as an array and working with that using loops.
I've ran into an issue with sorting the options of a vote based on the amount of times it was voted on. My DB has 3 tables related to this script:
vote_votes - which contains the cast votes, one row per vote cast
vote_options - which contains the possible options for every vote
vote_list - which contains the list of votes with title, type etc.
My original script just got all the options that matched the currently visited vote's ID using
SELECT * FROM vote_options WHERE vote_options.vid = $voteID
then counted the rows matching the option's unique ID inside vote_votes in an array, then sorted it based on the amount of rows with a custom sorting function.
I want to do the same in one query, and I think it's possible, I just don't know how. Here's my current SELECT statement:
SELECT
options.optid as id,
options.value as value
FROM vote_options options
WHERE vid = {$vote['vid']};");
Basically, inside vote_votes, each entry has a unique entryid and an optid column, and I want to add it to the query in a way that these entries are counted as WHERE vote_votes.optid = options.optid (Option IDs are unique, so no need to also look for vote ID).
I was hoping this would work, but it's obviously wrong. This is the closest I got before giving up and asking a question here.
SELECT
options.optid as id,
options.value as value
FROM vote_options options
WHERE vid = {$vote['vid']}
ORDER BY (
SELECT COUNT(*)
FROM vote_votes
WHERE vote_votes.optid = options.optid
) DESC;
I've found the solution, it was just a matter of moving the SELECT in brackets to the correct place:
SELECT
options.optid as id,
options.value as value,
options.vid as voteid,
(
SELECT COUNT(*)
FROM vote_votes votes
WHERE votes.optid = options.optid
) as votes
FROM vote_options options
WHERE options.vid = {$vote['vid']}
ORDER BY votes DESC;

Finding all polls that a specific user hasn't voted on yet

I have a website that displays poll after poll to a register user, and stores his vote in a table.
A simple structure of the database would be as follows (there are much more fields, but they don't contribute much to the question, so I removed them):
Polls(pollId)
Votes(pollId, userId, vote)
Now I want to run a MySql query to select all polls within the 'Polls' table, except for the polls that the current user had already voted on (which could be determined from the 'Votes' table)
Is it possible to make that using a MySql select statement alone, or will I have to select all polls first, and use some Php logic to filter out the ones already voted on by the user?
Thanks in advance!
SELECT *
FROM Pools
WHERE id NOT IN (
SELECT poolId
FROM Votes
WHERE userId = 142
)
Try something like this:
select p.pollid from polls p where p.pollid not in (select v.pollid from votes v where userId = {USERID})