Generate a table from the data of another table - mysql

What I'm trying to do is that I have a table called messages. It has 5 fields ID, SENDER, RECIPIENT, MESSAGE and TIMESTAMP. Each user gets an id when he uses this app. Each message that he sends using this app gets stored in this table with the id of the recipient.
Now what I want is to be able to list the most recent message of each conversation and then list each conversation in order of the recency of their last message. Lets call this table CONVERSATIONS.
Each conversation between two users XX and YY is defined as a table generated by
SELECT * FROM MESSAGES WHERE SENDER=XX and RECIPIENT=YY ORDER BY TIMESTAMP
In CONVERSATIONS table we only want the most recent message of each of the conversations in the MESSAGES table.
I don't want to use PHP and was looking for a solution only using MySQL. Thank you.

If I understood you correctly, you may try something like this.
select MESSAGES.*,
concat(SENDER|RECIPIENT, "r", SENDER&RECIPIENT) as combo
from MESSAGES inner join (
select max(TIMESTAMP) maxtime,
concat(SENDER|RECIPIENT, "r", SENDER&RECIPIENT) as combo2
from MESSAGES group by combo2
) subq
on combo = combo2 and MESSAGES.TIMESTAMP = subq.maxtime
order by MESSAGES.TIMESTAMP desc
Here standard technique of grouping is used (from here), but also I used combo expression, which is commutative injection from (SENDER,RECIPIENT) to strings.
It'll return you all rows with max timestamps grouped by unordered pair {SENDER,RECIPIENT}

Related

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.

Query for average response time in mysql

I have a table with columns:
id , conversation_id , session_id , user_id , message , created_at
every time a user starts a conversation with an employee, a new session starts (different session number).all messages between every employees and users are stored in this table. the created_at column is a timestamp. I need to filter out sessions by employee number, and calculate the average response time between the first message a user sends and the first message sent back by a specific employee, for every session disregarding outlying data where either a customer or employee did not reply ( only one user in the session)
i know this is complicated but please help!
in this example in the user_id column, 4 is the employee ( keep in mind there are other employees). everytime a new conversation starts the session_id changes. i have to go through each session for a specific employee, take the timestamp of the first message sent by the customer as well as the employee, take the difference, sum all the differences and then take an average, while making sure that the session actually contains two users ( filtering outlying data).
So far, ive come up with this:
SELECT * FROM messages
WHERE session_id IN (
SELECT session_id FROM messages
WHERE user_id =4 )
GROUP BY session_id, user_id
to get the first message from each customer and employee (gives something like this)
so from this specific example, i would omit line 41040 as it only as the session contains only 1 person (column 3, id 1028) and is considered outlying data
I'm actually appalled by some of the comments... StackOverflow is meant to be a community for helping others. Why bother even taking up comment space if you're gonna complain about my ponctuation or give a vague, useless answer?
Anyways, i figured it out.
Basically, i joined the same table multiple times but only queried the necessary data. In the first join, I queried the messages table with the employee messages and grouped them by session number. In the second join, i did the same procedure but only extracted the messages from the user. By joining them on the session id, it automatically omits any sessions where either a user or employee is not present. By default, the groupby returns the first set of data from the group ( in this situation i didn't have to manipulate the groupby because I was actually looking for the first message in the session), I then took the average of the difference between the message timestamp for the user and employee.In this specific situation, the number 4 is the employee number. Here is what the query looks like Also, the HAVING AVG_RESP > 0 was necessary in this situation to remove outlying data when tests are performed :
SELECT AVG(AVG_RESP)
FROM(
SELECT TIME_TO_SEC(TIMEDIFF(t.created_at, u.created_at )) AS AVG_RESP
FROM (
SELECT * FROM messages
WHERE session_id IN (
SELECT session_id FROM messages
WHERE user_id = 4) AND user_id = 4
GROUP BY session_id
) AS t
JOIN(
SELECT * FROM messages
WHERE session_id IN (
SELECT session_id FROM messages
WHERE user_id = 4) AND user_id != 4
GROUP BY session_id
) as u
ON t.session_id = u.session_id
GROUP BY t.session_id
HAVING AVG_RESP > 0
) as ar
Hopefully this helps someone in the future, unlike the people who leave ridiculous, useless comments.

MySQL Query - Loading records if the owner is a friend

I've got a system on my website which is very similar to Facebook, where you can post statuses and your people can comment on your status, like it etc. This all gets inserted in the database in the following format, with child tables of the likes and comments with foreign keys set up in case the parent status gets deleted, the likes and comments get deleted with it.
I also have a friends table which contains the user ID of the user that started the friend request, the user ID of the user that has to either accept it or deny it, and the status of the record, whether it's accepted, denied or pending.
There's also a "users" table which contains the normal malarkey, such as emails, passwords etc. All records have a unique ID however, in the column "userID".
The query I have at the moment loads all statuses regardless of whether the status owner is your friend or not. The current query looks like this (I'm working in ColdFusion so ## are the variables passed to the function)
SELECT *,
(SELECT COUNT(*) FROM status_likes WHERE likeStatusID=statusID) AS StatusLikeCount,
(SELECT COUNT(*) FROM status_comments WHERE SID=statusID) AS StatusCommentCount
FROM status, users
WHERE statusOwner=userID
AND statusType='user'
ORDER BY statusDateTime DESC
LIMIT #args.indexStart#,#args.indexEnd#;
I need this query to only load statuses if the owner of the status is your friend. I can call a query to load a users friends and append a string containing the user ID's of all the friends, such as: "652,235,485,975" etc.
I tried doing an IN in the query so there was an extra line:
AND (statusOwner=#val(args.userID)# OR statusOwner IN (#usersFriendsString#))
However this brought back duplicate results and when I tried GROUP BY on the status owner, it didn't bring back records that it should have.
Any MySQL gurus out there able to help?
You should use something like that :
SELECT
s.*,
(SELECT COUNT(*) FROM status_likes WHERE likeStatusID=s.statusID) AS StatusLikeCount,
(SELECT COUNT(*) FROM status_comments WHERE SID=s.statusID) AS StatusCommentCount
FROM Users u
JOIN Friend f ON f.friendOwner = u.id
JOIN Status s ON s.statusOwner = f.id
WHERE u.id = <...>
ORDER BY s.statusDateTime DESC
You can use WHERE clause if you can't use a JOIN instruction.
Or you can use a IN instruction populated by a SELECT that retrieve all requiered status ids.

How to make multiple tables share the id column in MySQL

This question has been asked before but never really answered (at least the way I want to do it). I have three tables in my database, threads, responses and votes. I need them to share the id field only. So I can do something like
SELECT * FROM threads AS t JOIN responses AS r JOIN votes AS v
WHERE id = 15
and will only retrieve a thread a response or (exclusive) a vote record with id = 15.
Is there a possible way to do this without creating an extra table? I'm not asking if it's a good idea (which is probably not), but if it's possible and how to do it.
If I understand right, you want to fetch thread, or response, or vote with id = 5. There is no opportunity to provide id field uniqueness across tables (i.e. prevent create response and vote with the same id value) in MySQL, but you can do it in your app.
And you can fetch needed row this way:
SELECT id, name, 'thread' AS `type` FROM threads WHERE id=5
UNION
SELECT id, name, 'response' AS `type` FROM responses WHERE id=5
UNION
SELECT id, name, 'vote' AS `type` FROM votes WHERE id=5
Your select will throw an error, because the id field in the where clause is ambiguous. I suppose there is an id field in all of these three tables.
SELECT * FROM threads AS t JOIN responses AS r JOIN votes AS v
WHERE id = 15
So even if the id field is "somehow shared" between these fields, you will have to make your where clause unambiguous:
SELECT * FROM threads AS t JOIN responses AS r JOIN votes AS v
WHERE t.id = 15
Furthermore, I suppose threads and responses have a many-to-one relationship which means there can be several response corresponding to a single thread. This means the responses table would have a thread_id field. The id field in the responses table could not have the same value as the id field in the corresponding thread record, since the id field must be unique in both tables.
The same logic goes to the relationship between responses and votes (a single response would have many votes).
Hence I conclude there is no possible way to share the id field between these three tables.

Query Construction Combining UNION and LEAST

I have an email subscription table and a user table. I need to combine the two to get all the emails, since it's possible to create an account without subscribing and vice versa. Easy enough so far:
SELECT email FROM emailcapture
UNION
SELECT email FROM cpnc_User
Now, this gets me the complete list of all emails. For each email on this combined list, I need to add an extra piece of information: the created date. Both emailcapture and cpnc_User tables have a "created" field. The created date should be the earlier of the two dates, if both dates exist, or, if only one exists and the other is NULL, it should just be the one that exists.
How can I change this query so that it returns this extra piece of information, the created date? Keep in mind that the new query I seek should return exactly the same number of rows as the query above.
Thanks,
Jonah
SELECT i.email, MIN(i.date_creation) FROM
(SELECT email, date_creation FROM emailcapture
UNION ALL
SELECT email, date_creation FROM cpnc_User) as InnerTable i
GROUP BY i.email