How to select ONLY posts from friends and viewing user - php

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

Related

"Liking" individual comments in PHP/MySQL

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.

Users ranking table on how many likes/upvotes users had

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.

Query (Inbox and Outbox, etc) for Private Messaging System - PHP and MySQL

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

MySQL + CodeIgniter + Click Tracking Unique & Total CLicks

Trying to track outbound clicks on advertisements, but im having troubles constructing the query to compile all the statistics for the user to view and track.
I have two tables, one to hold all of the advertisements, the other to track clicks and basic details on the user. ip address, timestamp, user agent.
I need to pull all of map_advertisements information along with Unique Clicks based on IP Address, and Total Clicks based on map_advertisements.id to be showin in a table with rows. 1 row per advertisement and two of its columns will be totalClicks and totalUniqueClicks
Aside from running three seperate queries for each advertisement is there a better way to go about this?
I am using MySQL5 PHP 5.3 and CodeIgniter 2.1
#example of an advertisements id
$aid = 13;
SELECT
*
count(acl.aid)
count(acl.DISTINCT(ip_address))
FROM
map_advertisements a
LEFT JOIN map_advertisements_click_log acl ON a.id = acl.aid
WHERE
a.id = $aid;
map_advertisements
-- ----------------------------
-- Table structure for `map_advertisements`
-- ----------------------------
DROP TABLE IF EXISTS `map_advertisements`;
CREATE TABLE `map_advertisements` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`youtube_id` varchar(255) NOT NULL,
`status` int(11) NOT NULL DEFAULT '1',
`timestamp` int(11) NOT NULL,
`type` enum('video','picture') NOT NULL DEFAULT 'video',
`filename` varchar(255) NOT NULL,
`url` varchar(255) NOT NULL,
`description` varchar(64) NOT NULL,
`title` varchar(64) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;
map_advertisements_click_log
-- ----------------------------
-- Table structure for `map_advertisements_click_log`
-- ----------------------------
DROP TABLE IF EXISTS `map_advertisements_click_log`;
CREATE TABLE `map_advertisements_click_log` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`aid` int(11) NOT NULL,
`ip_address` varchar(15) NOT NULL DEFAULT '',
`browser` varchar(255) NOT NULL,
`timestamp` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=26 DEFAULT CHARSET=latin1;
A problem seems to be in your query there is no column with the name totalClicks in your table and distinct keyword is also used incorrectly. Try this:
SELECT *, count(acl.id) as totalClicks, count(DISTINCT acl.ip_address) as uniqueClicks
FROM map_advertisements a
LEFT JOIN map_advertisements_click_log acl ON a.id = acl.aid
WHERE a.id = $aid;

Friend system in PHP/MySQL?

I'm trying to write a friend system that works like this:
User A sends friend request to user B.
User B accepts friend request from user A.
Users A and B are now friends.
Here is my database structure:
CREATE TABLE IF NOT EXISTS `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(16) NOT NULL,
`password` char(32) NOT NULL,
`email` varchar(80) NOT NULL,
`dname` varchar(24) NOT NULL,
`profile_img` varchar(255) NOT NULL DEFAULT '/images/default_user.png',
`created` int(11) NOT NULL,
`updated` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `email` (`email`),
UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=8 ;
CREATE TABLE IF NOT EXISTS `friend_requests` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_a` int(11) NOT NULL DEFAULT '0',
`user_b` int(11) NOT NULL DEFAULT '0',
`viewed` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=8 ;
CREATE TABLE IF NOT EXISTS `friends` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_a` int(11) NOT NULL DEFAULT '0',
`user_b` int(11) NOT NULL DEFAULT '0',
`friend_type` int(3) NOT NULL DEFAULT '1',
`friends_since` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
I can list a users friends with "SELECT * FROM friends WHERE user_a = $userid OR user_b = $userID", but how can I get data such as username or profile_img from the users table?
I think you have overthunk (!) your problem a bit.
You could have just a Usertable and a Friendshiptable. Your Friendship-table could contain UserID int, FriendID int, Created_at datetime, Confirmed_at datetime.
select * from User
left outer join Friendship on User.ID = Friendship.UserID
left outer join User as Friend on Friendship.FriendID = Friend.ID
where User.ID = <some users ID>
Edit: I may have overthunk it myself the first time around ;)
select * from Friendship
inner join User on Friendship.FriendID = User.ID
where Friendship.UserID = <some users ID>
This would get a users friends...
(MSSQL syntax)
Oh, i forgot. When Confirmed_at is null the Friendship-request is not yet confirmed.
SELECT DISTINCT
a.username,
a.profile_img
FROM
users a
WHERE
a.id in (SELECT user_a FROM friends WHERE user_a = $userid OR user_b = $userID)
and a.id <> $userid
UNION
SELECT
b.username,
b.profile_img
FROM
users b
WHERE
b.id in (SELECT user_b FROM friends WHERE user_a = $userid OR user_b = $userID)
and b.id <> $userid
You'll need to use joins to join the table friends to users.
Eg:
SELECT * FROM friends as f INNER JOIN users AS u ON u.id = f.user_a WHERE user_a = $userid

Categories