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
Related
I built a website for the fantasy movie league that I run (similar to fantasy football but for motion pictures) using PHP and MySQL. Each movie's profile page has a place where you can leave comments and I have that part working properly. Now, I want to add the ability to "like" individual comments but I'm having some trouble. I think I have the "likes" table set up properly and I'm able to add a like to each individual comment, but displaying the likes inline with each comment is another story.
Here is the code for the "comments" table:
CREATE TABLE `comments` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`movie_id` int(11) NOT NULL,
`player_id` tinyint(2) NOT NULL,
`date_time` datetime NOT NULL,
`comment` text CHARACTER SET utf8 NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Here is the code for the "likes" table:
CREATE TABLE `likes` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`player_id` int(2) NOT NULL,
`comment_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `player_comment` (`player_id`,`comment_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
Here is the code for the "players" table:
CREATE TABLE `players` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`first_name` varchar(10) NOT NULL,
`username` char(10) NOT NULL,
`password` char(32) NOT NULL,
`email` varchar(100) NOT NULL DEFAULT '',
`active` tinyint(1) NOT NULL,
`admin` tinyint(1) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Now, here is the SQL statement that I use to grab all the comments for any particular movie:
SELECT comments.id as comment_id, comments.player_id, comments.date_time, comments.comment,
players.first_name
FROM comments
LEFT JOIN players on players.id = comments.player_id
WHERE movie_id = $movie_id
ORDER BY date_time ASC
My problem is, I'm not sure how to grab all of the likes for each comment in the same statement. I want to be able to display each player's first name that has liked that particular comment. So I want it to look like this:
Line 1: $comment
Line 2: $commenter_first_name
Line 3: $liker_first_name1, $liker_first_name2, $liker_first_name3 likes this
I'm able to get all of the likers for an individual comment like this:
SELECT first_name
FROM likes
LEFT JOIN players on players.id = likes.player_id
WHERE comment_id = 264
ORDER BY first_name ASC
But I don't know how to incorporate this without using the actual comment_id.
Hope this makes sense, I welcome any possible help.
How about something like:
SELECT comments.id as comment_id, comments.player_id, comments.date_time, comments.comment, likers.names
FROM comments
LEFT JOIN
(SELECT comment_id, GROUP_CONCAT(first_name) AS names FROM likes
INNER JOIN players ON likes.player_id = players.id GROUP BY comment_id) AS likers
ON comments.id = likers.comment_id
WHERE movie_id = $movie_id
ORDER BY date_time ASC
The "likers" table should have something like:
| (comment_id) | Bob,Jim,Kathy |
For better list formatting, you can specify a separator:
GROUP_CONCAT(first_name SEPARATOR ', ')
Cant you just add the linkes to the comments table?
CREATE TABLE `comments` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`movie_id` int(11) NOT NULL,
`player_id` tinyint(2) NOT NULL,
`date_time` datetime NOT NULL,
`comment` text CHARACTER SET utf8 NOT NULL,
`likes` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
And then when somebody likes the comment, you just also add +1 to the likes in comment DB, not only in likes DB. And when somebody removes his like, you juset change the number to -1
Like this:
"UPDATE comments SET likes=likes+1 WHERE id='$comment_id'";
The problem I see is, that you want to show the likers...
So maybe something like this:
"SELECT comments.*, likes.* FROM comments INNER JOIN likes comments.id=likes.comment_id WHERE movie_id='$movie_id'";
Your select:
"SELECT comments.id as comment_id, comments.player_id, comments.date_time, comments.comment, players.first_name, likes.player_id
FROM comments
LEFT JOIN players ON players.id=comments.player_id
INNER JOIN likes ON comments.id=likes.comment_id
WHERE movie_id='$movie_id'
ORDER BY date_time ASC";
The other thing is, that it will only return you the ID's of the peoples that liked the comment. So I would suggest you to save also users name to the likes table.
I started developing chat application for my website.
First I did some javascript part, before I got to backend.
And Now just created database structure:
CREATE TABLE IF NOT EXISTS `wp_bp_my_chat` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`from` varchar(255) NOT NULL DEFAULT '',
`to` varchar(255) NOT NULL DEFAULT '',
`message` text NOT NULL,
`sent` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`recd` int(10) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `to` (`to`),
KEY `from` (`from`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
Now, having this databse, I want to make a request to see all messages grouped by "from" OR "To"
Think of it as facebook messages, when you go to actual page, there is a left sidebar with messages grouped by conversation.
the output should be like:
conversation between "user_1" and "user_2" (unread) 2 hours ago
conversation between "user_1" and "user_3" (unread) 3 hours ago
converstation between "user_1" and "user_5" 5 hours ago
so my messages are grouped like conversations.
I might have 10 message from user_2 but it should be displayed as one (and info from last one)
Any Ideas how I go next?
As I have not done any php side yet You can even suggest changing database to adjast for your solution.
Thanks.
I assume you would run this for one person ('user_1') for their conversations, which means they can be either the from or the to. I also assume that it make no difference if they are the from or the to, but to group by the other person in the conversation. If so, try this. (You should put some sample data in SQLFiddle for testing)
SELECT MostRecent.MainPerson AS MainPerson
, MostRecent.OtherPerson AS OtherPerson
, MostRecent.Sent AS Sent
, IF(wp_bp_my_chat.recd = 0, 'Unread','Read') AS Status
FROM wp_bp_my_chat
JOIN (
SELECT 'user_1' AS MainPerson
, IF(msgs.`from` = 'user_1',msgs.to, msgs.`from`) AS OtherPerson
, MAX(msgs.sent) AS sent
FROM wp_bp_my_chat AS msgs
WHERE msgs.`from` = 'user_1' OR msgs.`to` = 'users_1'
GROUP BY MainPerson, OtherPerson) AS MostRecent
ON (wp_bp_my_chat.`from` = MostRecent.MainPerson OR wp_bp_my_chat.`to` = MostRecent.MainPerson)
AND (wp_bp_my_chat.`from` = MostRecent.OtherPerson OR wp_bp_my_chat.`to` = MostRecent.OtherPerson)
AND MostRecent.sent = wp_bp_my_chat.sent
ORDER BY sent DESC
To get the results you described in your update, use:
SELECT count(*), max(sent)
FROM wp_bp_my_chat
WHERE to = 'name of recipient'
GROUP BY from
count(*) gives you the number of messages and max(sent) gives you the time of the latest message, which you can use to calculate the "hours ago" part of the output.
I don't see a flag in your table for whether the message has been read. You'd need to add that in order to add the "(unread)" text.
This can be done with a simple group by query:
SELECT * FROM `wp_bp_my_chat` WHERE `to` = {my_id} GROUP BY `from`
This will give you all the chats that I as a user have had.
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.
I am building a messaging system similar to facebook's (where it displays messages as threads).
My current table design is:
CREATE TABLE IF NOT EXISTS messages (
mid int(11) NOT NULL auto_increment,
subject text NOT NULL,
message text NOT NULL,
fromid varchar(255) NOT NULL default '',
toid varchar(255) NOT NULL default '',
status varchar(255) NOT NULL default '',
date varchar(255) NOT NULL default '',
time varchar(255) NOT NULL,
PRIMARY KEY (mid)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=2825 ;`
I am retrieving results with this select statement:
SELECT
IF(messages.toid = '$uid' OR messages.toid = '$uid', messages.fromid, messages.toid) friend1,
messages.message, messages.fromid, messages.toid, messages.date, messages.status, messages.time
FROM messages
WHERE (messages.toid='$uid' OR messages.fromid='$uid')
AND messages.status!='2'
GROUP BY friend1 ASC
ORDER BY messages.time DESC, messages.mid DESC
This gives me the right results except for it displays the first post from a thread, I would like for it to display the most recent post in a thread.
What am I doing wrong?
This is a nice article directly related to your question: http://kristiannielsen.livejournal.com/6745.html
Taking a wild stab at your specific problem, your query would probably need to look something like below (untested!):
SELECT
IF(derived_messages.toid = '$uid', derived_messages.fromid,
derived_messages.toid) friend1,
derived_messages.message, derived_messages.fromid, derived_messages.toid,
derived_messages.date, derived_messages.status, derived_messages.time
FROM
(SELECT *
FROM messages
ORDER BY time desc) derived_messages
WHERE (derived_messages.toid='$uid' OR derived_messages.fromid='$uid')
AND derived_messages.status!='2'
GROUP BY friend1 ASC
ORDER BY derived_messages.time DESC, derived_messages.mid DESC
Btw this clause looked fishy to me (in your original post):
IF(messages.toid = '$uid' OR messages.toid = '$uid',
messages.fromid, messages.toid) friend1
What's the "OR" doing between identical conditions? You can probably skip the second condition, I think.
I have a seemingly simple task but I cannot seem to find an elegant solution using 1 query...
Problem:
I have a table of recorded 'clicks' on 'posts', where each post is part of a 'category'.
I want to find the 16 highest clicked posts in the last 30 days -- but I want to avoid duplicate categories.
It seems very simple actually, but I seem to be stuck.
I know how to get the most clicked in last 30, but I can't figure out how to avoid duplicate cats.
SELECT cat_id,
post_id,
COUNT(post_id) AS click_counter
FROM cs_coupon_clicks
WHERE time_of_click > DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY post_id
ORDER BY click_counter DESC
I tried to get creative/hacky with it... it's close but not correct:
SELECT cat_id,
Max(sort) AS sortid
FROM (SELECT cat_id,
post_id,
COUNT(post_id) AS click_counter,
CONCAT(COUNT(post_id), '-', post_id) AS sort
FROM cs_coupon_clicks
WHERE time_of_click > DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY cat_id, post_id) t1
GROUP BY cat_id
ORDER BY cat_id ASC
Any help would be greatly appreciated as I am not really a MySQL expert. I may end up just doing some PHP logic in the end, but I am very curious as to the correct way to approach a problem like this.
Thanks guys.
EDIT (structure):
CREATE TABLE `cs_coupon_clicks` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`src` varchar(255) NOT NULL DEFAULT '',
`cat_id` int(20) NOT NULL,
`post_id` int(20) NOT NULL,
`tag_id` int(20) NOT NULL,
`user_id` int(20) DEFAULT NULL,
`ip_address` char(30) DEFAULT NULL,
`referer` varchar(255) NOT NULL,
`browser` varchar(10) DEFAULT NULL,
`server_var` text NOT NULL,
`time_of_click` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `cat_id` (`cat_id`),
KEY `post_id` (`post_id`),
KEY `user_id` (`user_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
TEMP WORKING SOLUTION (HACKY):
SELECT
cat_id,
MAX(sort) AS sortid
FROM (
SELECT
cat_id,
post_id,
COUNT(post_id) AS click_counter,
RIGHT(Concat('00000000', COUNT(post_id), '-', post_id), 16) AS SORT
FROM cs_coupon_clicks
WHERE time_of_click > DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY cat_id, post_id
) AS t1
GROUP BY cat_id
ORDER BY sortid DESC
There is no easy single query solution to this problem, it's a group-wise maximum kind of problem based on a temporary table (the one with counts) that would require self-joins.
Assuming your database grows big enough (otherwise just go for your php logic) I would go for a statistics table, holding info about categories, posts and click counts:
CREATE TABLE `click_cnts` (
`cat_id` int(20) NOT NULL,
`post_id` int(20) NOT NULL,
`clicks` int(20) NOT NULL,
PRIMARY KEY (`cat_id`,`post_id`),
KEY `cat_id` (`cat_id`,`clicks`)
)
and fill it using the same query as the first one in the question:
INSERT INTO click_cnts(cat_id, post_id, clicks)
SELECT cat_id, post_id, COUNT(post_id) AS click_counter
FROM cs_coupon_clicks
WHERE time_of_click > NOW() - INTERVAL 30 DAY
GROUP BY cat_id,post_id
You could update this table using triggers or running update query periodically (do users really need info up to the very last second? probably not...) and save a lot of processing as finding most clicks for each category on indexed table requires a lot less time using a classic group-wise max approach:
SELECT cg.cat_id, cu.post_id, cg.most_clicks
FROM
( SELECT cat_id, max(clicks) as most_clicks FROM click_cnts
GROUP BY cat_id ) cg
JOIN click_cnts cu
ON cg.cat_id = cu.cat_id
AND cu.post_id = ( SELECT cc.post_id FROM click_cnts cc
WHERE cc.cat_id = cg.cat_id
AND cc.clicks = cg.most_clicks
LIMIT 1 )
ORDER BY cg.most_clicks DESC
LIMIT 16
Shot in the dark here. Did you try Select DISTINCT cat_id