I am currently working on a messaging system for one of my project. I am using single table to store messages and replies(i.e with parent_id).
Here is the table structure:
CREATE TABLE IF NOT EXISTS `messages` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`parent_id` int(10) unsigned NULL,
`sender_id` int(10) unsigned NOT NULL,
`receiver_id` int(10) unsigned NOT NULL,
`who` enum('bride','member') NOT NULL,
`subject` varchar(200) NOT NULL,
`body` text NOT NULL,
`sent_date` datetime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=10 ;
Where parent_id is the id of the message to which we replied. if parent_id=NULL then its a new message
Where who is the who sent that message- i.e bride or members. I have two separate tables for bride & member details.
Now I want a query to list messages in INBOX(just like GMAIL- bunch of conversations[messages+replies to that]).
Can you please help me to build the query?
This is what I was working on :
SELECT id
, parent_id
, sender_id
, receiver_id
, who
, subject
, body
, sent_date
FROM `messages`
WHERE sent_date IN ( SELECT MAX( sent_date ) FROM `messages` WHERE receiver_id = 1 GROUP BY sender_id )
AND who = 'bride'
AND receiver_id = 1
ORDER
BY id DESC
LIMIT 0, 8
This will give you all top-level messages (ie. not a reply) sent to a person identified with ID 1, as well as all direct replies to these messages (1 level deep, no recursion):
SELECT
messages.*,
replies.*
FROM messages
LEFT JOIN messages AS replies ON (replies.parent_id = messages.id)
WHERE messages.receiver_id = 1
ORDER BY id messages.sent_date, replies.sent_date
Now, from your sample code, it looks like you only want the latest message (and their replies), then:
SELECT
lastMessage.*,
replies.*
FROM (
SELECT * FROM messages ORDER BY sent_date
WHERE messages.receiver_id = 1
LIMIT 1
) AS lastMessage
LEFT JOIN messages AS replies ON (replies.parent_id = lastMessage.id)
ORDER BY id replies.sent_date
I do not understand the condition who = 'bride' though.
Related
Before I dive in, This example below is what I hope to achieve.
Meanwhile I Have a database table that is structured like so.
CREATE TABLE `notifications` (
`id` int(11) NOT NULL,
`recipient_id` int(11) NOT NULL,
`sender_id` int(11) NOT NULL,
`unread` tinyint(4) NOT NULL DEFAULT '1',
`type` varchar(255) NOT NULL,
`reference_id` int(11) NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
`recipient_id` - Notification Reciever
`sender_id` - Notification Sender
`unread` - Mark if notificaton has been read or not
`type` - Holds types of notification, Comment, Likes etc
`reference_id` - the reference like a post id
`created_at` - Time noti was created.
and a PHP to fetch data from the database table.
$query = $this->database->query("SELECT recipient_id, unread, type, reference_id, post_title, post_name, COUNT(reference_id)
FROM notifications n INNER JOIN posts p ON p.id = n.reference_id WHERE n.recipient_id = $user AND n.type = 'post_comment' AND n.unread = 1
GROUP BY n.reference_id HAVING COUNT(n.reference_id) >= 1 ORDER BY unread DESC LIMIT 8")->fetchAll();
return $query;
I'm grouping the results I get by the reference_id if its >= 1
this reason is so I don't get duplicate notification that has the same reference_id.
with this query so far I am able to get the data from the database table and display like so.
Someone commented on your post "I love to code"
but I want to display to the user like the example above or like this below.
James, John and others commented on your post "I Love to Code"
thats if there is more than 1 or 2 sender_id with the same reference_id
this is where I am stuck and don't know which other step to take, please any help I can get is appreciated.
thanks
Ok we have inbox table where we keep messages that users send to each other. Here is the table:
CREATE TABLE IF NOT EXISTS `inbox` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`fromid` int(10) unsigned NOT NULL DEFAULT '0',
`toid` int(10) DEFAULT NULL,
`message` text CHARACTER SET utf8 NOT NULL,
`time` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`),
KEY `toid` (`toid`),
KEY `fromid` (`fromid`),
KEY `fromid_2` (`fromid`,`toid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 ;
fromid and toid are id's of the users. We have their id's, times when the message is sent. What we need is a query that would return all messages that are not replied by 'our users' (admins).
Table accounts keeps track of users. To simplify:
CREATE TABLE IF NOT EXISTS `accounts` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`our` int(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
So basically, we need a query that gives us the users WHOSE messages WERE NOT ANSWERED by admins (our users), their count and the date of the last message they sent to ADMIN, ordered from last to oldest.
So far we only have some basic queries, we didn't come up with anything reasonable that I could post.
Thanks in advance.
EDIT: From what I see we first need to find last interaction from two DISTINCT users in inbox table... then check & filter only those that were sent TO our users
How about this?
SELECT i.* FROM inbox as i
WHERE (i.toid, i.fromid) NOT IN
(SELECT i2.fromid, i2.toid FROM inbox as i2 WHERE i2.`time` >= i1.`time` AND i2.id = 1);
Another way using join:
SELECT DISTINCT i1.*
FROM inbox as i1 LEFT JOIN inbox as i2
ON i1.toid = 1 AND
i1.fromid = i2.toid AND
i1.toid = i2.fromid AND
i1.`time` <= i2.`time`
WHERE i2.id IS NULL;
Two possible solutions presented below: LEFT JOIN solution should perform better.
LEFT JOIN solution
SELECT
i.fromid, COUNT(*) AS unread, MAX(i.time) AS lastmsg
FROM inbox AS i
INNER JOIN accounts AS a
ON i.toid = a.id
LEFT JOIN inbox AS i2
ON i.fromid = i2.toid AND i.toid = i2.fromid AND i.time <= i2.time
WHERE a.our = 1 AND i2.id IS NULL
GROUP BY i.fromid
ORDER BY lastmsg DESC;
NOT IN solution
SELECT
i.fromid, COUNT(*) AS unread, MAX(i.time) AS lastmsg
FROM inbox AS i
INNER JOIN accounts AS a ON i.toid = a.id
WHERE a.our = 1 AND
(i.toid, i.fromid)
NOT IN (SELECT i2.fromid, i2.toid FROM inbox AS i2 WHERE i2.time >= i.time)
GROUP BY i.fromid
ORDER BY lastmsg DESC;
I'm making a social plugin for my website, and I have a friends table that holds all accepted friend requests, and I need to display all posts from the users friend AND the users posts in order of the date, so I've tried this sql query:
SELECT DISTINCT `social_posts`.*, `social_friends`.*, `users`.*
FROM `social_posts`
JOIN `social_friends`
ON `fUID` = '1' AND `friend` = `pUID` OR `pUID` = '1'
JOIN `users`
ON `friend` = `uid`
ORDER BY `date` DESC
Structure
CREATE TABLE `social_friends` (
`fID` int(11) NOT NULL AUTO_INCREMENT,
`fUID` int(11) NOT NULL,
`friend` int(11) NOT NULL,
PRIMARY KEY (`fID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `social_posts` (
`pid` int(11) NOT NULL AUTO_INCREMENT,
`pUID` int(11) NOT NULL,
`body` text NOT NULL,
`date` datetime NOT NULL,
PRIMARY KEY (`pid`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
fUID is the users (viewing) user ID, and friend is the users friend, and pUID is the user ID of the user who made the post.
But this shows two of each post, even with SELECT DISTINCT, and I'm out of ideas on how to figure this out.
1 - Can you give as more info (the fields) of the tables?
What is fUID and pUID
2 - Try and change
ON `fUID` = '1' AND `friend` = `pUID` OR `pUID` = '1'
to ON friend = pUID and put fUID = 1 OR pUID = 1 in the WHERE clause
I'm trying to create a ranking table based on how many likes/upvotes a user had on all his items in total. User in the upvotes table links to id of the user that made the like, but I think you don't need this.
Hopefully by giving these tables everything will get clear.
I think the trick here is to get all the upvotes by each item and merge them together towards a user this item was from to get a total likes for each user and then rank all the users based on this total. Of course doing this will probably be a slow query so I need a very performant way to handle this.
The hard thing is here mainly that the upvotes table doesn't include the user id.
3 tables:
CREATE TABLE `items` (
`id` int(255) NOT NULL AUTO_INCREMENT,
`user_id` int(255) NOT NULL,
`img` varchar(500) NOT NULL,
`message` varchar(200) NOT NULL,
`created_on` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`active` int(11) NOT NULL DEFAULT '1',
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=19 DEFAULT CHARSET=latin1;
CREATE TABLE `upvotes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user` int(255) NOT NULL,
`item_id` int(255) NOT NULL,
`created_on` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=latin1;
CREATE TABLE `users` (
`id` int(255) NOT NULL AUTO_INCREMENT,
`email` varchar(255) NOT NULL,
`password` binary(60) NOT NULL,
`first_name` varchar(255) NOT NULL,
`last_name` varchar(255) NOT NULL,
`active` int(1) NOT NULL DEFAULT '1',
`created_on` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `email` (`email`)
) ENGINE=MyISAM AUTO_INCREMENT=17 DEFAULT CHARSET=latin1;
I need a performant query giving me the ranking of each user ranked on how many likes they got on all their items?
I managed to write this:
SELECT #rank := #rank + 1 AS rank, m.*
FROM (SELECT
users.first_name as first_name,
users.last_name as last_name,
count(upvotes.item_id) as total
FROM upvotes
INNER JOIN users
ON users.id = (SELECT items.user_id FROM items WHERE items.id = upvotes.item_id LIMIT 1)
GROUP BY users.id
ORDER BY total DESC
) m, (SELECT #rank := 0) r
But I reckon this will be super slow when the database grows...
You can do a simple join query in order to get the total likes for each item of user and order your results with the resulting count in descending order
SELECT u.*,i.*,COUNT(DISTINCT up.user) `total_user_likes_item`
FROM users u
JOIN items i ON(i.user_id = u.id)
JOIN upvotes up ON(up.item_id = i.id)
GROUP BY u.id,i.id
ORDER BY u.id,i.id,total_user_likes_item DESC
Edit from comments For user total likes you remove i.id from group by as below query
SELECT u.*,COUNT(DISTINCT up.user) `total_user_likes_item`
FROM users u
JOIN items i ON(i.user_id = u.id)
JOIN upvotes up ON(up.item_id = i.id)
GROUP BY u.id
ORDER BY total_user_likes_item DESC
I'll try answer your question:
In table users you can add row sum_upvotes. Every time when someone get one like (vote) you will increment this column by:
UPDATE users
SET sum_upvotes = sum_upvotes + 1
;
Of course, you will insert a column in table upvotes.
Finally, you query to select users and order them by upvotes will look like this
SELECT first_name, last_name
FROM users
ORDER BY sum_upvotes
;
Hope this helps.
I'm writing a private messaging system for a website. A user can communicate with one of more other users (including himself / herself). I have three tables
- users
- conversation_list
- conversation_messages
for my database as shown below.
The users table holds all users
The conversation_messages table holds the messages written, each message has a conversation_id
The conversation_list holds the list of participants in each conversation
Each conversation is of course, identified by the unique conversation_id
Now, I wish to query the following from the tables:
- getInboxMessages <-- Difficult
Get all new messages directed to a user.
Replies to a message should be grouped as a conversation
and the latest reply previewed
- getOutboxMessages
Get all messages sent by the user.
Replies to messages should be grouped as a conversation
and only the latest reply previewed
- getConversation
Each message with all replies
- isUnreadMessage
Check if a message has been read or not
- getNumberOfUnreadMessages
Get the number of unread messages
What I have done so far is shown below.
getOutboxMessages, getConversation, isUnreadMessage and getNumberOfUnreadMessages do work, although the queries may not be optimal! I have real trouble getting the getInboxMessages.
I can only get the first thread of a conversation in the inbox. Any other threads (replies) do not aplear as new messages, but rather as sent messages (outbox).
However, any reply to the message (part of a conversation) does not appear as a new message!
An ideas? Any support in getting the right query for getInboxMessages
as well as optimizing the queries is heartily welcome!
CREATE TABLE IF NOT EXISTS `conversation_list` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` varchar(50) NOT NULL,
`conversation_id` int(11) NOT NULL,
`added_by` varchar(50) NOT NULL,
`date_created` int(11) NOT NULL,
`date_lastPost` int(11) NOT NULL,
`date_lastView` int(11) NOT NULL,
`status` tinyint(1) NOT NULL,
UNIQUE KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `conversation_messages` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`sender` varchar(50) DEFAULT NULL,
`conversation_id` int(11) NOT NULL,
`message` text NOT NULL,
`date_created` int(11) NOT NULL,
`status` tinyint(1) NOT NULL,
UNIQUE KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `users` (
`username` varchar(30) NOT NULL,
`password` varchar(40) default NULL,
`usersalt` varchar(8) NOT NULL,
`userid` varchar(32) default NULL,
`userlevel` tinyint(1) unsigned NOT NULL,
`email` varchar(50) default NULL,
`timestamp` int(11) unsigned NOT NULL,
`regdate` int(11) unsigned NOT NULL,
PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
QUERIES
getInboxMessages
================
SELECT *
FROM conversation_messages AS m
JOIN
(SELECT mx.conversation_id,
MAX(mx.date_created) AS MaxTime
FROM conversation_messages AS mx
GROUP BY mx.conversation_id) AS mx ON m.conversation_id = mx.conversation_id
AND m.date_created = mx.MaxTime
JOIN
(SELECT mu.conversation_id
FROM conversation_list AS mu
WHERE mu.user_id = :subuser
GROUP BY mu.conversation_id) AS mux ON m.conversation_id = mux.conversation_id
JOIN conversation_list AS mu ON m.conversation_id = mu.conversation_id
GROUP BY mu.conversation_id
ORDER BY m.date_created DESC
getOuboxMessages
================
SELECT *
FROM conversation_messages AS m
JOIN
(SELECT mx.conversation_id,
MAX(mx.date_created) AS MaxTime
FROM conversation_messages AS mx
GROUP BY mx.conversation_id) AS mx ON m.conversation_id = mx.conversation_id
AND m.date_created = mx.MaxTime
JOIN
(SELECT mu.conversation_id, mu.user_id
FROM conversation_list AS mu
WHERE mu.added_by = :subuser
GROUP BY mu.conversation_id) AS mux ON m.conversation_id = mux.conversation_id
JOIN conversation_list AS mu ON m.conversation_id = mu.conversation_id
ORDER BY m.date_created DESC
isUnreadMessage
===============
SELECT * FROM conversation_list WHERE date_created >= date_lastView AND conversation_id = :messageid AND user_id = :subuser
getConversation
===============
SELECT conversation_id, message, sender, date_created
FROM conversation_messages
WHERE conversation_id = :submessage_id
ORDER BY date_created DESC
getNumberOfUnreadMessages
=========================
SELECT l.conversation_id, count(*)
FROM conversation_list l
JOIN conversation_messages m ON m.conversation_id = l.conversation_id AND m.date_created >= l.date_lastview
WHERE l.user_id = :subuser
GROUP BY l.conversation_id