I am working on a PM system where I'd like to have the previous sent PMs for one conversation, listed above the last received PM. But my question is: how do I go about setting up such a table in a database? I toyed for a while about using an id for each specific conversation, but what would the source for that id be? I can't use auto increment (it seems), because I'm using it for the primary "id" column.
Or maybe there's a completely different way I can experiment with the already available columns (id, from, to, subject, message, sent, read, deleted); but how? Please help a lost man out.
You could add a origin_id column to your table that contains the id of the root/original message, or NULL if it's a new discussion (root).
Then you can get the root messages by filtering those than have origin_id = NULL and then group by origin_id to get the message thread.
Okay, so I have got it partly solved...
I used another table containing the one column which holds the subject of the PM. I also have a new column in the regular "pms" table that holds the same ID to be able to join the tables together.
However, when I select all the PMs to show them in the inbox, I have not found a way to group the conversations in order by if they're read or not. I'm currently using this SQL query:
SELECT *
FROM `pms`
JOIN `pm_conversations` ON (pms.ConvID = pm_conversations.ID)
WHERE pms.To='username'
GROUP BY pm_conversations.ID
ORDER BY pms.ID
I came up with this:
SELECT MAX(pms.ID) as pmIDS,
pms.*,
pm_conversations.*
FROM `pms`
JOIN `pm_conversations` ON (pms.ConvID = pm_conversations.ID)
WHERE `To`='".$UserActive."'
GROUP BY pm_conversations.ID
ORDER BY pmIDS DESC
Related
I am building a mobile chat web application and I have stuck in a problem. Coming straight to the issue, I have a screen which displays messages list from all users (like WhatsApp). I want to display the last message sent or received between the users in the list (as in the screenshot below). My current query extracts the message from the 1st row for all users right now. That's not what I want.
Little more brief details of what is happening
As you can see in messages table, the fields msg_from and msg_to represents the sender and the receiver respectively. In my data, the messages are transferred between user 1 & 8 and user 1 & 11. For user 1 & 8 the last record fetched should be record 9 which has msg_message Are you there? and similarly, for user 1 & 11 the last record to be fetched would be record 10 which has msg_message Would you like to join?. But currently, for all users the record getting fetched is the 1st record with message How are you?. What changes should my query have to get the desired result? Please have a look at the fiddle below.
Fiddle Here: DB Fiddle
I learned a lot from researching in order to solve this. When grouping, groupBy will take the first row of non-grouped columns (suck as msg_message), so we may order it when joining with the help of a subquery, just like this:
SELECT swp_by, swp_to, msg_from, msg_to, mem_fname, mem_lname, mem_last_activity, msg_message, GREATEST(MAX(msg_time), swipes.swp_date) as msgdate, COUNT(msg_id) as msgcnt FROM swipes
LEFT JOIN
(
SELECT * FROM messages order by msg_time desc -- this is the magic, we use this subquery to order before grouping
)
messages ON
((
messages.msg_from = swipes.swp_by
AND messages.msg_to = swipes.swp_to)
OR (messages.msg_from = swipes.swp_to
AND messages.msg_to = swipes.swp_by
))
solution is in your fiddle: https://www.db-fiddle.com/f/xnh4jiUb8rDLFHpL2gWHrM/5
I think I got expected output
I have a table with data relating to a user, and two important columns:
refer_count, which is updated when a new entry is made in the table with the referred_by column set to that users user_id, and referred_by which is the user_id of the of the user that referred them.
I want to select the users from the table that have the highest number of referrals after a certain date.
For example:
If there are 3 users, one of which referred the other 2 (lets say users 2 and 3), however user 2 was referred on the 2/12/14, whereas user 3 was referred on the 3/1/15.
If the cutoff is 1/12/14, then user 1 is returned with refer_count set to 2, but if the cutoff is after 2/12/14, then user 1 is returned with refer_count set to 1.
I've been thinking of how to do this, but I can't think of a way that would work. Is there a way?
This is via MySQL.
EDIT: I think I may need to provide for information.
The date registered (register_date) is used as the refer date. I need the refer_count to be updated with the number of users referred after the cutoff, however I need to get the actual user. This is for a 'top referrers' table. I can't figure out why I'm having so much trouble thinking of a way to do this.
SELECT user_id FROM usertable WHERE (referal_date BETWEEN '2014-12-2' AND CURDATE())ORDER BY refer_count DESC;
That's the rough idea.
You should look into normalizing your tables if you're keeping that all in the same table, though. It'd be better to keep referals in a seperate table.
Get the row with the maximum in refer_count with a Date condition for your referal_date such that it's after the certainDate:
SELECT user_id FROM table WHERE refer_count = (SELECT MAX(refer_count) FROM table) AND referal_date>certainDate;
Note that WHERE is before SELECT so it will not get the highest count first, but will filter with the date condition then get the highest count.
Edit: Updated query based on edited question.
I apologize for the improperly worded title; couldn't think of a better one.
I have an app that sends and receives messages. I have two tables, one for sent, one for received:
Sent:
id
status
sent_date
user_id
sent_message
sent_from
sent_to
another_field1
another_field2
comments
Received:
id
received_date
user_id
received_message
received_from
received_to
another_field3
another_field4
comments
I'm trying to display a conversation thread based on these two tables. I need to JOIN the two by the user_id, WHERE received.received_from=sent.sent_to AND received.received_to=sent.sent_from AND sent.status=1 [means it was sent], then sort them by both the two date columns--so the messages are displayed in conversation order.
I saw other similar questions that used a union to do this, but I'm not sure that would work in my case due to different columns. My display, done in PHP, would be something like this:
Conversation with {sent_to}, ordered by date:
{sent_date} From me: {sent_message}
{received_date} From {received_from}: {received_message}
{received_date} From {received_from}: {received_message}
{sent_date} From me: {sent_message}
Also, I realize that I may have structured the database inefficiently. I'm open to suggestions on how to better optimize the structure and how it works. Thank you.
EDIT: I just tried doing Alain Collins's suggestion, but I'm having trouble figuring out how to separate the sent fields from the received fields. I'm really trying to put the two tables together, but not combining the actual fields. Here's what I have so far, which is totally wrong:
SELECT *,
case when sent.sent_date is not null then sent.sent_date
else received.received_date end AS date
FROM sent JOIN received ON (received.user_id = sent.user_id)
WHERE sent.user_id = ".$_SESSION['id']." ORDER BY date ASC
This is joining the received and sent messages into one row. I still need them separated. Any tips on how to do this? Would it perhaps be somewhat easier to have both sent and received message in a single table?
EDIT 2: To clarify, I want the columns from both tables separate. Meaning, the ROWS returned should only have either sent columns or received columns, but the rows should be returned in order of the date, as mentioned above. So I'd get rows something like this:
Row 1: Array(Date, Sent_Message, Sent_From)
Row 2: Array(Date, Received_Message, Received_From)
Row 3: Array(Date, Received_Message, Received_From)
Row 4: Array(Date, Sent_Message, Sent_From)
So the only two thing in common with the two tables is 1) the user_id having the conversation and 2) the dates. The dates must somehow be combined because they are currently "sent_date" and "received_date".
You might have a an easier go of it if you structure your tables around a 'thread_id' instead of trying to match up by 'sent_to=recd_from'. When a new message is created (ie not in response) create a new thread with a new message id. Then any response to this will reference the original thread_id and you can sort the responses by message id to get the order of the thread.
Create a column that contains the date from the underlying table and sort by that:
select case
when sent.sent_date is not null then sent.sent_date
else received.received_date
end as message_date
...
order by message_date;
I figured out the proper query for what I'm trying to do:
(SELECT sent_date AS date, sent_message, sent_from
FROM sent WHERE user_id = ".$_SESSION['id'].")
UNION ALL (SELECT received_date AS date, received_message, received_from
FROM received WHERE user_id = ".$_SESSION['id'].")
ORDER BY date ASC
I've made one table for all comments on a social network site: comment
Also, I've one table for all comments assigned to one comment: comment_assign
So, I built a function comment() to implent it easily in each section type (images, userpage, groups, etc). In case of $_GET['s']==user, I want to have wallposts as well as comments on these wallposts. All stored in 'comment'.
I've got this scheme to display this:
1. sql query to get the comments
2. html output
3. another sql query inside this html output to get specified assigned comments of a comment (wallpost in this case)
Now the problem is that my first query displays all comments. Also comments that are supposed to be subcomments. So my question is, if there's any way to specify in this first query, when I get all my comments, to say: Look in comment_assign if this comment_id is available. And if it is, don't display this comment, because it's a subcomment (that I'll display in mentioned step 3).
Maybe this whole structure may be changed? I would appreciate any suggestions. Even hard to realized ones, but which would be the most efficient.
Table structure:
comment
id, uid, nid, site, text, date
comment_assign
comment_id, assign_id
First SQL Query example, which doesnt work to avoid displaying all the comments (also assigned ones). See the last line:
SELECT *
FROM `comments` AS c
LEFT JOIN `comment_assign` AS ca ON ca.`comment_id` = c.`id`
LEFT JOIN `users` AS u ON c.`uid` = u.`id`
WHERE c.`nid`='".$nid."'
AND c.`site`='".$_GET['s']."'
AND ca.`comment_id` != c.`id`
If I understand you correctly, you select all the comments from the comment table. You then want to check to see if comment.id is present in comment_assign.comment_id. If it is present, it is a sub-comment. Is that correct?
You can do it two ways - the clean way is to add another field to the comment table and put assign_id there, since each comment can only be associated with another comment, or is a top-level comment (*assign_id is NULL*).
Alternatively, you could LEFT JOIN both tables. Every row where assign_id is NULL, is a wall comment, every row where it has a value means it is assigned as a sub-comment. i.e.
SELECT id, uid, site, text, date
FROM comment
LEFT JOIN comment_assign ON (comment.id = comment_assign.comment_id)
WHERE comment_assign.assign_id IS NULL;
I have a voting script which pulls out the number of votes per user.
Everything is working, except I need to now display the number of votes per user in order of number of votes. Please see my database structure:
Entries:
UserID, FirstName, LastName, EmailAddress, TelephoneNumber, Image, Status
Voting:
item, vote, nvotes
The item field contains vt_img and then the UserID, so for example: vt_img4 and both vote & nvotes display the number of votes.
Any ideas how I can relate those together and display the users in order of the most voted at the top?
Thanks
You really need to change the structure of the voting table so that you can do a normal join. I would strongly suggest adding either a pure userID column, or at the very least not making it a concat of two other columns. Based on an ID you could then easily do something like this:
select
a.userID,
a.firstName,
b.votes
from
entries a
join voting b
on a.userID=b.userID
order by
b.votes desc
The other option is to consider (if it is a one to one relationship) simply merging the data into one table which would make it even easier again.
At the moment, this really is an XY problem, you are looking for a way to join two tables that aren't meant to be joined. While there are (horrible, ghastly, terrible) ways of doing it, I think the best solution is to do a little extra work and alter your database (we can certainly help with that so you don't lose any data) and then you will be able to both do what you want right now (easily) and all those other things you will want to do in the future (that you don't know about right now) will be oh so much easier.
Edit: It seems like this is a great opportunity to use a Trigger to insert the new row for you. A MySQL trigger is an action that the database will make when a certain predefined action takes place. In this case, you want to insert a new row into a table when you insert a row into your main table. The beauty is that you can use a reference to the data in the original table to do it:
CREATE TRIGGER Entries_Trigger AFTER insert ON Entries
FOR EACH ROW BEGIN
insert into Voting values(new.UserID,0,0);
END;
This will work in the following manner - When a row is inserted into your Entries table, the database will insert the row (creating the auto_increment ID and the like) then instantly call this trigger, which will then use that newly created UserID to insert into the second table (along with some zeroes for votes and nvotes).
Your database is badly designed. It should be:
Voting:
item, user_id, vote, nvotes
Placing the item id and the user id into the same column as a concatenated string with a delimiter is just asking for trouble. This isn't scalable at all. Look up the basics on Normalization.
You could try this:
SELECT *
FROM Entries e
JOIN Voting v ON (CONCAT('vt_img', e.UserID) = v.item)
ORDER BY nvotes DESC
but please notice that this query might be quite slow due to the fact that the join field for Entries table is built at query time.
You should consider changing your database structure so that Voting contains a UserID field in order to do a direct join.
I'm figuring the Entries table is where votes are cast (you're database schema doesn't make much sense to me, seems like you could work it a little better). If the votes are actually on the Votes table and that's connected to a user, then you should have UserID field in that table too. Either way the example will help.
Lets say you add UserID to the Votes table and this is where a user's votes are stored than this would be your query
SELECT Users.id, Votes.*,
SUM(Votes.nvotes) AS user_votes
FROM Users, Votes
WHERE Users.id = Votes.UserID
GROUP BY Votes.UserID
ORDER BY user_votes
USE ORDER BY in your query --
SELECT column_name(s)
FROM table_name
ORDER BY column_name(s) ASC|DESC