a three way join mysql tables and php? - php

user_table
user_id | username
1 | solomon
2 | muna
message table
id user_id| message
1 1 | this is my first message
2 1 | this is my seocnd message
3 2 | this is muna messgae
relationship table
leader | follower
1 | 2
|
what i want to do is a three table join to bring out muna's friend messages, currently muna is following solomon(as shown on the followers table),
so i want the display to be like this on mona homepage
Solomon "this is my my first message
----------------------------------
solomon "this is my second message"
p.s. this is just an example database, i just wanted to see how to approach the problem, and this is what i have attempted so far!
select username, message, leader
from user, message, relationship where user.user_id =notes.user_id
and user.user_id = relationship.leader and where user_id = $_SESSION['user_id']
*the session id is muna which is user_id 2

heres one way of doing it
SELECT m.message,u.username FROM relationships r, messages m,users u WHERE m.user_id = r.leader AND r.leader = u.user_id AND u.user_id = 1
Spaced out
SELECT
m.message,u.username #here you can use m.* to retrieve all data
FROM
relationships r, #Here you can define what tables you want to use
messages m, #within your query, always remember to do table_name letter
users u #this will stop ambiguity within the queries
WHERE
m.user_id = r.leader #make sure the message user id is the same as the leader id
AND
r.leader = u.user_id #same as above just making the messages be filtered to the user_id
AND
u.user_id = 1 #your overall id will be here, if this was 2 it would affect WHERE, and AND above.
Within application
$query = mysql_query('SELECT m.message,u.username FROM relationships r, messages m,users u WHERE m.user_id = r.leader AND r.leader = u.user_id AND u.user_id = ' . intval($_SESSION['user_id']));
while($row = mysql_fetch_assoc($query))
{
echo sprintf("%s: %s",$row['username'],$row['message']);
echo "\r\n--------------------------------------------";
}
This is tested and works fine on the following table structure.
CREATE TABLE IF NOT EXISTS `messages` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`message` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ;
INSERT INTO `messages` (`id`, `user_id`, `message`) VALUES
(1, 1, 'test'),
(2, 1, 'test 2'),
(3, 2, 'test 3');
CREATE TABLE IF NOT EXISTS `relationships` (
`leader` int(11) NOT NULL,
`follower` int(11) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
INSERT INTO `relationships` (`leader`, `follower`) VALUES
(1, 2);
CREATE TABLE IF NOT EXISTS `users` (
`user_id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) NOT NULL,
PRIMARY KEY (`user_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
INSERT INTO `users` (`user_id`, `username`) VALUES
(1, 'solomon'),
(2, 'muna');

I'm not really that sure what your question is. Does this look like an answer to it?
SELECT u.username,
m.message
FROM user u
JOIN message m
ON u.user_id =m.user_id
JOIN relationship r
ON u.user_id = r.leader
WHERE u.user_id = $_SESSION['user_id']

Related

Missing rows due to too strict WHERE statement

In the example code below we have attempted to minimize our problem into a simplified test case.
We have 3 tables: users, images and ratings.
1: We want to print all entries from the ‘ratings’ table.
2: In the ‘images’ table, there can be several images uploaded by the same users into different sections of the site, e.g. ‘user_profile_picture’ or ‘user_cover_photo’.
3: When printing the ratings, we want to fetch entries from the ‘images’ table if the user has uploaded a profile picture (section = ‘profile_picture’).
The WHERE statement in the query in the example code fails for users with no profile pictures uploaded.
How can we make sure to print all entries, as well as only grab the image connected to the user if section = profile_picture …?
CREATE TABLE `test_ratings` (
`id` int(4) UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` int(4) UNSIGNED NOT NULL,
`rating` enum('1','2','3','4','5') DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
INSERT INTO `test_ratings` (`id`, `user_id`, `rating`) VALUES
(2, 2, '4'),
(1, 1, '5');
CREATE TABLE `test_users` (
`id` int(4) UNSIGNED NOT NULL AUTO_INCREMENT,
`username` varchar(30) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
INSERT INTO `test_users` (`id`, `username`) VALUES
(1, 'Test person 1'),
(2, 'Test person 2');
CREATE TABLE `test_users_images` (
`id` int(4) UNSIGNED NOT NULL AUTO_INCREMENT,
`filename` varchar(80) NOT NULL,
`section` enum('user_cover_photo','user_profile_picture') DEFAULT NULL,
`user_id` int(4) UNSIGNED DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
INSERT INTO `test_users_images` (`id`, `filename`, `section`, `user_id`) VALUES
(1, 'rtfos.jpg', 'user_profile_picture', 2),
(2, 'exer8.jpg', 'user_cover_photo', 2);
<?php
$mysqli = new mysqli("localhost", "root", "", "website");
$result = $mysqli->query('SELECT image.filename, user.username, ratings.rating
FROM test_ratings ratings
INNER JOIN test_users user ON user.id = ratings.user_id
INNER JOIN test_users_images image ON image.user_id = user.id
WHERE image.section = \'user_profile_picture\'');
while ($row = $result->fetch_object()) {
echo '<p>' . $row->username . ' - ' . $row->filename . '</p>';
}
?>
As already commented, start with the user table and left join to it. Include the image section filter in the ON clause of the respective join.
SELECT image.filename,
user.username,
ratings.rating
FROM test_users user
LEFT JOIN test_users_images image
ON image.user_id = user.id
AND image.section = 'user_profile_picture'
LEFT JOIN test_ratings ratings
ON user.id = ratings.user_id;
db<>fiddle
P.S.: It's a WHERE clause, not a statement.
By changing the INNER JOIN to a LEFT JOIN for image.sections you keep all rows from ratings (if the user still exists in the database). To include users without a profile image you have to add a condition that image.section is allowed to be NULL.
SELECT image.filename, user.username, ratings.rating
FROM test_ratings ratings
INNER JOIN test_users user ON user.id = ratings.user_id
LEFT JOIN test_users_images image ON image.user_id = user.id
WHERE image.section = 'user_profile_picture' OR image.section IS NULL
db<>fiddle

PHP MysqlI - How to Select 1 Row? (from select)

I am a bit stumped on how i should be able to Define 1 post ID, and only select from that row
my query:
SELECT
posts.post_id,
posts.title,
COUNT(post_likes.id) AS likes,
GROUP_CONCAT(user.name SEPARATOR '|') AS liked
FROM
posts
LEFT JOIN post_likes ON post_likes.post_id = posts.post_id
LEFT JOIN user ON post_likes.user = user.id
GROUP BY
posts.post_id
my end goal is to get all from row 3 (only row 3), not get all rows. when i run the query above, i get every row from mysql.
example:
$query = "
SELECT
posts.post_id,
posts.title,
COUNT(post_likes.id) AS likes,
GROUP_CONCAT(user.name SEPARATOR '|') AS liked
FROM
posts
LEFT JOIN post_likes ON post_likes.post_id = posts.post_id
LEFT JOIN user ON post_likes.user = user.id
GROUP BY
posts.post_id
";
$result = mysqli_query($connect, $query);
while($row = mysqli_fetch_array($result))
{
echo '<h3>'.$row["title"].'</h3>';
echo 'Like';
echo '<p>'.$row["likes"].' People like this</p>';
if(count($row["liked"]))
{
$liked = explode("|", $row["liked"]);
echo '<ul>';
foreach($liked as $like)
{
echo '<li>'.$like.'</li>';
}
echo '</ul>';
}
}
returns with all rows,
what i need to do is just return the 3rd row (from the post_id, and only that row)
CREATE TABLE IF NOT EXISTS `posts` (
`post_id` int(11) NOT NULL AUTO_INCREMENT,
`title` text,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ;
-- posts
INSERT INTO `posts` (`id`, `title`) VALUES
(1, 'Ajax Jquery Drag and Drop Shopping Cart using PHP Mysql'),
(2, 'Make PHP Hashtag system by using Regular Expression'),
(3, 'Ajax Jquery Column Sort with PHP & MySql'),
(4, 'Drag and drop Upload multiples File By Ajax JQuery PHP');
--
CREATE TABLE IF NOT EXISTS `post_likes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user` int(11) NOT NULL,
`post` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=46 ;
--table
CREATE TABLE IF NOT EXISTS `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
--
INSERT INTO `user` (`id`, `name`) VALUES
(1, 'john'),
(2, 'jack');
CREATE TABLE `post_likes` (
`id` int NOT NULL,
`user` int NOT NULL,
`post_id` int NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
--
-- Dumping data for table `post_likes`
--
INSERT INTO `post_likes` (`id`, `user`, `post_id`) VALUES
(11111, 3, 3),
(91, 2, 3);
final edit, i kinda just said screw it and just did this:
$query1 = 'SELECT * FROM posts WHERE post_id = "$page_id"';
$result1 = mysqli_query($connect, $query1);
$query2 = 'SELECT * FROM post_likes WHERE post_id = "$page_id"';
$result2 = mysqli_query($connect, $query2);
if ($result3 = $connect->query('SELECT * FROM post_likes WHERE post_id = "'.$page_id.'"')) {
$row_cnt = $result3->num_rows;
printf("Result set has %d rows.\n", $row_cnt);
}
thanks to all who helped though, i really appreciate it.
You can use the function ROW_NUMBER() (MySQL 8 MariaDB 10.4)
The principal thing is, that MySQL needs an order to determine which row number is what
I selected
OVER(ORDER BY COUNT(post_likes.id))
But it can be every column from the from clause
CREATE TABLE IF NOT EXISTS `posts` (
`post_id` int(11) NOT NULL AUTO_INCREMENT,
`title` text,
PRIMARY KEY (`post_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ;
-- posts
INSERT INTO `posts` (`post_id`, `title`) VALUES
(1, 'Ajax Jquery Drag and Drop Shopping Cart using PHP Mysql'),
(2, 'Make PHP Hashtag system by using Regular Expression'),
(3, 'Ajax Jquery Column Sort with PHP & MySql'),
(4, 'Drag and drop Upload multiples File By Ajax JQuery PHP');
--
CREATE TABLE IF NOT EXISTS `post_likes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user` int(11) NOT NULL,
`post` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=46 ;
--table
CREATE TABLE IF NOT EXISTS `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
--
INSERT INTO `user` (`id`, `name`) VALUES
(1, 'john'),
(2, 'jack');
WITH CTE AS (SELECT
posts.post_id,
posts.title,
COUNT(post_likes.id) AS likes,
GROUP_CONCAT(user.name SEPARATOR '|') AS liked
,ROW_NUMBER() OVER(ORDER BY COUNT(post_likes.id)) rn
FROM
posts
LEFT JOIN post_likes ON post_likes.post = posts.post_id
LEFT JOIN user ON post_likes.user = user.id
GROUP BY
posts.post_id)
SELECT post_id,title, likes,liked FROM CTE WHERE rn = 3
post_id | title | likes | liked
------: | :--------------------------------------- | ----: | :----
3 | Ajax Jquery Column Sort with PHP & MySql | 0 |
SELECT post_id,title, likes,liked FROM
(SELECT
posts.post_id,
posts.title,
COUNT(post_likes.id) AS likes,
GROUP_CONCAT(user.name SEPARATOR '|') AS liked
,ROW_NUMBER() OVER(ORDER BY COUNT(post_likes.id)) rn
FROM
posts
LEFT JOIN post_likes ON post_likes.post = posts.post_id
LEFT JOIN user ON post_likes.user = user.id
GROUP BY
posts.post_id) t1
WHERE rn = 3
post_id | title | likes | liked
------: | :--------------------------------------- | ----: | :----
3 | Ajax Jquery Column Sort with PHP & MySql | 0 |
db<>fiddle here

Select last distinct pair

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;

MySQL query display problem

How can I display the users friends correctly on the users and friends side using my MySQL tables? My current code screws this up.
Users Friends Table
CREATE TABLE users_friends (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
user_id INT UNSIGNED UNSIGNED NOT NULL,
friend_id INT UNSIGNED NOT NULL,
friendship_status TINYINT(1) UNSIGNED NOT NULL DEFAULT 0,
date_created DATETIME NOT NULL,
PRIMARY KEY (id),
KEY user_id (user_id),
KEY friend_id (friend_id)
);
Users Table
CREATE TABLE users (
user_id INT UNSIGNED NOT NULL AUTO_INCREMENT,
username VARCHAR(255) NULL,
avatar VARCHAR(255) NULL,
password CHAR(128) NOT NULL,
PRIMARY KEY (user_id),
UNIQUE KEY (username)
);
My Current MySQL Code
SELECT users.*, users_friends.*
FROM users
INNER JOIN users_friends ON users.user_id = users_friends.friend_id
WHERE (users_friends.user_id = '" . $user_id . "'
OR users_friends.friend_id = '" . $user_id . "')
AND users_friends.friendship_status = 1
GROUP BY users_friends.date_created
Try this query. This should give you all friends for a particular user based on your database relationship.
SELECT A.user_id, A.friend_id, B.username, B.avatar
FROM users_friends AS A
INNER JOIN users AS B
ON A.friend_id = B.user_id
AND A.user_id = #userToBeSearched
UNION
SELECT A.friend_id, A.user_id, B.username, B.avatar
FROM users_friends AS A
INNER JOIN users AS B
ON B.user_id = A.user_id
AND A.friend_id = #userToBeSearched
Note : I have removed the Group By clause for now because
I am afraid i do not quite understand your schema enough to make sense of why you would be grouping rows only by date_created
Adding the grouping clause will involve making another nested table as you will not be able to select columns not in the Group Clause.
Data used to test:-
Table : user
user_id |username| avatar | password
1 A AAAA
2 B BBBB
3 C CCCCC
4 D DDDDD
Table : user_friends
id | user_id | friend_id | friendship_status | date_created
1 1 2 1 Friday, October 22, 2010 3:09:44 PM
2 2 3 1 Friday, October 22, 2010 3:43:18 PM
3 2 4 1 Friday, October 22, 2010 3:43:26 PM

How can I join in data from a key : value meta table in mysql?

Say I have three tables in my database:
CREATE TABLE `users` (
`user_id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`username` VARCHAR(16) NOT NULL
);
CREATE TABLE `users_meta` (
`meta_id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`user_id` INT NOT NULL ,
`key` VARCHAR(200) NOT NULL ,
`value` TEXT NOT NULL
);
CREATE TABLE `posts` (
`post_id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`user_id` INT NOT NULL ,
`content` TEXT NOT NULL
);
The table users_meta is just a key-value store of information about users, such that we can add any piece of information we want.
Say I added a key => value pair to the users_meta table for each user where the key was "age", and the value was a number representing their age.
Given this set of circumstances, what's the best way to select the first 10 posts ordered by user age?
I like putting the condition of the join in the join itself to be clear that I want a limited join:
SELECT p.post_id, p.content
FROM users u
INNER JOIN users_meta um
ON (u.user_id = um.user_id) AND um.key = 'age'
INNER JOIN posts p
ON (p.user_id = u.user_id)
ORDER BY um.value
limit 10
If you order by user age only, you will select 10 posts of the same user (the youngest one).
I would suggest to denormalize and store age in users table directly.
Agree with #KOHb, but if that's exactly what you want, here is the query:
SELECT TOP 10 p.id, p.content
FROM users u JOIN users_meta um ON (u.user_id = um.user_id)
JOIN posts p ON (p.user_id = u.user_id)
WHERE um.key = 'age'
ORDER BY um.value

Categories