How do I join 2 columns in MySql? - mysql

I have the following table structure in my code and I am trying to pull username and name fields from users table, but the query currently pulls only from_user_id data. How do I modify this so that I get two separate columns that lists username and name for both to_user_id and from_user_id?
SELECT f.id, from_user_id, to_user_id, STATUS, u.username, u.name
FROM friend f
left JOIN users u ON f.from_user_id = u.id
WHERE f.id IN(
SELECT source_id
FROM notification
WHERE user_id = 5 AND notification_read = 1)
users table:
CREATE TABLE `users` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`username` VARCHAR(60) NOT NULL,
`password` VARCHAR(64) NOT NULL,
`enabled` TINYINT(4) NOT NULL DEFAULT '1',
`email` VARCHAR(100) NOT NULL,
`name` VARCHAR(100) NOT NULL,
`created_on` DATETIME NOT NULL,
`role` VARCHAR(50) NULL DEFAULT 'ROLE_USER',
PRIMARY KEY (`id`),
UNIQUE INDEX `username` (`username`)
)
and friend table:
CREATE TABLE `friend` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`from_user_id` BIGINT(20) NOT NULL,
`to_user_id` BIGINT(20) NOT NULL,
`status` INT(2) NOT NULL,
`requested_date` DATETIME NULL DEFAULT NULL,
`accepted_date` DATETIME NULL DEFAULT NULL,
PRIMARY KEY (`id`),
INDEX `from_user_id` (`from_user_id`),
INDEX `to_user_id` (`to_user_id`)
)
and a notification table:
CREATE TABLE `notification` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`user_id` BIGINT(20) NOT NULL,
`activity_type` TINYINT(4) NOT NULL,
`source_id` BIGINT(20) NOT NULL,
`parent_id` BIGINT(20) NULL DEFAULT NULL,
`parent_type` TINYINT(4) NULL DEFAULT NULL,
`notification_read` TINYINT(4) NOT NULL DEFAULT '0',
`created_on` DATETIME NOT NULL,
PRIMARY KEY (`id`),
INDEX `user_id` (`user_id`),
INDEX `created_on` (`created_on`)
)

You need to perform two joins against users - one for each side of the friend relationship, and include the appropriate columns in the SELECT list from both of those joins against users.
SELECT
f.id,
from_user_id,
to_user_id,
STATUS,
-- uf is an alias for the "from" user
-- You must alias the columns to distinguish them
uf.username AS from_username,
uf.name AS from_name,
-- ut is an alias for the "to" user
ut.username AS to_username,
ut.name AS to_name
FROM
friend f
-- Join first for the from user info
LEFT JOIN users uf ON f.from_user_id = uf.id
-- Join again for the to user info
LEFT JOIN users ut ON f.to_user_id = ut.id
WHERE f.id IN(
SELECT source_id
FROM notification
WHERE user_id = 5 AND notification_read = 1
)
A further note... You can substitute an INNER JOIN against notification instead of the IN () subquery, and you may achieve better performance.
SELECT
DISTINCT /* needed assuming multiple notification.source_id per f.id */
f.id,
from_user_id,
to_user_id,
STATUS,
uf.username AS from_username,
uf.name AS from_name,
ut.username AS to_username,
ut.name AS to_name
FROM
friend f
LEFT JOIN users uf ON f.from_user_id = uf.id
LEFT JOIN users ut ON f.to_user_id = ut.id
-- Join notification instead of the IN () subquery
INNER JOIN notification
ON f.id = notification.source_id
AND notification.user_id = 5
AND notification_read = 1

Related

join query does not return null values

I have this table with units/homes
CREATE TABLE `units` (
`id` bigint NOT NULL AUTO_INCREMENT,
`cancellation_policy` varchar(255) DEFAULT NULL,
`description` varchar(255) DEFAULT NULL,
`image_url` varchar(255) NOT NULL,
`price` decimal(19,2) NOT NULL,
`region` varchar(255) NOT NULL,
`title` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UKha7gwhuig6p6vftvoghfi2b7g` (`title`,`image_url`),
UNIQUE KEY `UK_pdd7pto9vch2kb58kohy96a5f` (`image_url`),
UNIQUE KEY `UK_58rre8c1gk28a7d5p6wguiti9` (`title`)
)
and this one with reviews
CREATE TABLE `reviews` (
`id` bigint NOT NULL AUTO_INCREMENT,
`description` varchar(255) DEFAULT NULL,
`stars` int NOT NULL,
`user_id` varchar(255) NOT NULL,
`unit_id` bigint NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UKc3rd8vjkpbcda34jomifuybu9` (`user_id`,`unit_id`),
KEY `FKbgbdator49pjrbriaktrbv1q2` (`unit_id`),
CONSTRAINT `FKbgbdator49pjrbriaktrbv1q2` FOREIGN KEY (`unit_id`) REFERENCES `units` (`id`)
)
and I want to get all movies together with the average rating. However this query does not return me back movies without ratings (NULL) values. This query does not return movies without ratings
select
u.id, u.cancellation_policy, u.description, u.image_url, u.price, u.region, u.title, round(avg(stars),0) as ratings
from units u
inner join
reviews r
ON u.id = r.unit_id
group by u.id
What is the correct way to get all movies including those w/o stars?
A LEFT JOIN would show you all units even those that have no reviews
SELECT
u.id,
u.cancellation_policy,
u.description,
u.image_url,
u.price,
u.region,
u.title,
ROUND(AVG(IFNULL(stars,0)), 0) AS ratings
FROM
units u
LEFT JOIN
reviews r ON u.id = r.unit_id
GROUP BY u.id
Try a LEFT JOIN instead of the INNER JOIN

Joining different tables based on column value

I have a table called notifications:
CREATE TABLE `notifications` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`type` varchar(20) NOT NULL DEFAULT '',
`parent_id` int(11) DEFAULT NULL,
`parent_type` varchar(15) DEFAULT NULL,
`type_id` int(11) DEFAULT NULL,
`etc` NULL
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=25 DEFAULT CHARSET=utf8;
Each notification is related to a different table, the value of parent_type field specifies the name of the table that I want to * join the table with. All target tables have several similar columns:
CREATE TABLE `tablename` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`is_visible` tinyint(1) NOT NULL,
`etc` NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
Currently I'm using this query for selecting notifcations that their related row in the target table exists and their is_visible field is 1:
SELECT n.id,
FROM notifications n
LEFT JOIN books b ON n.parent_id = b.id AND n.parent_type = 'book' AND b.is_visible = 1
LEFT JOIN interviews i ON n.parent_id = i.id AND n.parent_type = 'interview' AND i.is_visible = 1
LEFT JOIN other tables...
WHERE n.user_id = 1
GROUP BY n.id
But since it is a LEFT JOIN it returns the notification if it matches any table or not, how can I rewrite it so it doesn't return notifications that don't match with any row in the target table? I have also tried the CASE statement unsuccessfully.
I'm not 100% sure the syntax is right and I have no chance to test it right now, but the idea should be clear.
SELECT DISTINCT n.id
FROM notifications n
JOIN (
(SELECT b.id, 'book' AS type FROM books b WHERE b.is_visible = 1)
UNION
(SELECT i.id, 'interview' AS type FROM interviews i WHERE i.is_visible = 1)
) ids ON n.parent_id = ids.id AND n.parent_type = ids.type
WHERE n.user_id = 1

Select from three tables - mysql and php

I've got the three tables below from which I wish to get the following selects:
1.) Select all items from collaboration and group them by group_id in group_members for a given user
This means that when the user logs in, (s)he will see all (and only) collaboration items belonging to the groups where he is group member.
2.) For each group_id, select all collaboration items.
This means that when a user selects any group (group_id) from 1 above, he's going to see all the collaborationitems belonging to the selected group (group_id)
Constraints: Each user MUST be a group member. The userstable is there to supply firstnameand lastname of user.
This is what I tried for 1 to no avail!
function OrderByGroup_id($username) {
$data = array();
$currenttime = time();
$q = "
SELECT *
FROM collaboration
INNER JOIN group_members ON collaboration.group_id = group_members.group_id
INNER JOIN users ON users.username = group_members.username
WHERE collaboration.parent_id IS NULL and collaboration.is_comment = 0
AND group_members.username = :user group by collaboration.group_id";
$sq = $this->connection->prepare($q);
$sq->execute(array(':user' => $username));
while($row = $sq->fetch()) {
$json = array();
$json['title'] = $row['title'];
$json['question'] = $row['content'];
$json['firstname'] = $row['firstname'];
$json['lastname'] = $row['lastname'];
$json['timestamp'] = $row['timestamp'];
$json['key'] = $row['group_id'];
$data[] = $json;
}
$allposts =json_encode($data);
return $allposts= json_decode($allposts, true);
}
Here are the tables
CREATE TABLE IF NOT EXISTS `collaboration` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`parent_id` int(11) DEFAULT NULL,
`group_id` varchar(255) DEFAULT NULL,
`author` varchar(30) NOT NULL,
`title` varchar(255) DEFAULT NULL,
`content` text NOT NULL,
`is_comment` tinyint(1) unsigned NOT NULL,
`file` tinyint(1) unsigned NOT NULL DEFAULT '0',
`points` int(11) DEFAULT NULL,
`timestamp` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `users` (
`firstname` varchar(30) NOT NULL,
`lastname` varchar(30) NOT NULL,
`username` varchar(30) NOT NULL,
PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `group_members` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(30) NOT NULL,
`group_id` varchar(50) NOT NULL,
`status` tinyint(1) unsigned NOT NULL,
`timestamp` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Thanks for your input!
The following select does deliver the request for question 1. However, I know the query is not optimal because I do select entries that I don't need at the moment. For example the use of *
I would like to select only title, content, timestamp, group_id, from collaboration and firstname, lastname, from users
SELECT * FROM collaboration
INNER JOIN group_members ON collaboration.group_id = group_members.group_id
INNER JOIN users ON users.username = collaboration.author
WHERE collaboration.parent_id IS NULL and collaboration.is_comment = 0
AND group_members.username = :user
GROUP BY collaboration.group_id
ORDER BY collaboration.timestamp DESC

MySQL query for searching subset of two tables

I have two tables:
CREATE TABLE IF NOT EXISTS `comments` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`photograph_id` int(11) NOT NULL,
`created` datetime NOT NULL,
`author` varchar(255) NOT NULL,
`body` text NOT NULL,
`email` varchar(255) NOT NULL,
`liked` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `photograph_id` (`photograph_id`)
)
And this:
CREATE TABLE IF NOT EXISTS `photographs` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`filename` varchar(255) NOT NULL,
`type` varchar(100) NOT NULL,
`size` int(11) NOT NULL,
`caption` varchar(255) NOT NULL,
`liked` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`)
)
I am having trouble merging these two into one query. In this query I would like to have sorting call of number of comments that every photo have. In Comments table, there is column photograph_id, that links to the photo id in Photographs table. Thanks for the help.
For photo's with 1 or more comments do:
SELECT p.id, COUNT(*) as commentcount FROM photographs p
INNER JOIN comments c ON (p.id = c.photograph_id)
GROUP BY p.id
ORDER BY commentcount DESC
If you also want photo's with zero comments do:
SELECT p.id, COUNT(c.id) as commentcount FROM photographs p
LEFT JOIN comments c ON (p.id = c.photograph_id)
GROUP BY p.id
ORDER BY commentcount DESC
SELECT *,
(SELECT COUNT(*) FROM comments
WHERE photographs.id = ccomments.photograph_id)) commentcount
FROM photographs
This will probably be faster than the join method. Maybe. You will need to experiment.

Mysql query generation

This is my user table schema
CREATE TABLE IF NOT EXISTS `ehobe_user` (
`user_id` bigint(20) NOT NULL,
`user_email` varchar(80) NOT NULL,
`user_password` varchar(50) NOT NULL,
`user_fname` varchar(255) NOT NULL,
`user_lname` varchar(255) NOT NULL,
`user_terms` enum('yes','no') NOT NULL DEFAULT 'yes',
`is_active` enum('yes','no') NOT NULL DEFAULT 'yes',
`created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`user_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
And this is my friends table schema
CREATE TABLE IF NOT EXISTS `ehobe_friends` (
`user_id1` bigint(20) NOT NULL,
`user_id2` bigint(20) NOT NULL,
`relationship_id` int(1) NOT NULL COMMENT '1 - user1 request, 2- user2 request, 3 - friends, 4- user1 blocked, 5 - user2 blocked'
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
So i need to select the user first name and last name who are my frinds in the friends table.
Suppose you want friends of USER_ID #67.
Try:
select u.user_id, u.user_fname, u.user_lname
from ehobe_user u
inner join ehobe_friends f1 on (u.user_id = f1.user_id1)
where f1.user_id2 = 67
union
select u.user_id, u.user_fname, u.user_lname
from ehobe_user u
inner join ehobe_friends f2 on (u.user_id = f2.user_id2)
where f2.user_id1 = 67
Logically, you want to get all of the friends in an array and check each user for matching info. I would give you code, but I don't know what language you're writing this in.