Select from three tables - mysql and php - mysql

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

Related

MySQL returning all matches from a table and indicating if an id is on another table

How can I return, on a select, a field that indicates that an id was found?
My goal is to return all songs(song) from a specific source(source) checking if an user(user) has it or not (user_song).
The query I made almost works. If I remove 'hasSong' (which Im trying to indicate that an user has a song or not), I can see all songs.
If I keep 'hasSong', I see all songs repeating the song for each user.
QUERY:
SELECT DISTINCT(song.id) AS id_song, CONCAT(song.article, ' ', song.name) AS name
FROM `song`
LEFT JOIN `user_song` ON `song`.`id` = `user_song`.`id_song`
LEFT JOIN `user` ON `user`.`id` = `user_song`.`id_user`
JOIN `song_source` ON `song`.`id` = `song_source`.`id_song`
WHERE `song_source`.`id_source` = '1'
AND ( `user_song`.`id_user` = '3' OR song.id = song_source.id_song )
ORDER BY `song`.`name` ASC
DB:
CREATE TABLE `song` (
`id` int(11) NOT NULL,
`article` varchar(10) NOT NULL,
`name` varchar(150) NOT NULL,
`shortname` varchar(150) NOT NULL,
`year` int(11) NOT NULL,
`artist` int(11) NOT NULL,
`duration` int(11) NOT NULL,
`genre` int(11) NOT NULL,
`updated` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `song_source` (
`id_song` int(11) NOT NULL,
`id_source` int(11) NOT NULL
)
CREATE TABLE `source` (
`id` int(11) NOT NULL,
`article` varchar(10) NOT NULL,
`name` varchar(150) NOT NULL,
`updated` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `user` (
`id` int(11) NOT NULL,
`email` varchar(100) NOT NULL,
`password` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `user_song` (
`id_user` int(11) NOT NULL,
`id_song` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
The specification isn't entirely clear, ...
To return all songs (with no repeated values of song.id) that are from a particular source (id_source='1'),
along with an indicator, a value of 0 or 1, that tells us if there's a row in user_song that matches on id_song and is related to a particular user,(id_user = '3')
something like this:
SELECT s.id AS id_song
, MAX( CONCAT(s.article,' ',s.name) ) AS name
, MAX( IF(us.id_user = '3' ,1,0) ) AS has_song
FROM `song` s
JOIN `song_source` ss
ON ss.id_song = s.id
AND ss.id_source = '1'
LEFT
JOIN `user_song` us
ON us.id_song = s.id
AND us.id_user = '3'
GROUP BY s.id
ORDER BY MAX(s.name)
There are a couple of other query patterns that will return an equivalent result. For example, we could use a correlated subquery in the SELECT list.
SELECT s.id AS id_song
, MAX( CONCAT(s.article,' ',s.name) ) AS name
, ( SELECT IF( COUNT(us.id_user) >0,1,0)
FROM `user_song` us
WHERE us.id_song = s.id
AND us.id_user = '3'
) AS has_song
FROM `song` s
JOIN `song_source` ss
ON ss.id_song = s.id
AND ss.id_source = '1'
GROUP BY s.id
ORDER BY MAX(s.name)
These queries are complicated by the fact that there are no guarantees of uniqueness in any of the tables. If we had guarantees, we could eliminate the need for a GROUP BY and aggregate functions.
Please consider adding PRIMARY and/or UNIQUE KEY constraints on the tables, to prevent duplication. The way the tables are defined, we could add multiple rows to song with the same id value. (And those could have different name values.)
(And the queries would be much simpler if we had some guarantees of uniqueness.)

How to use SUM when foreign key and primary key matched in MYSQL

Currently I have 2 tables which is document and item table.
CREATE TABLE IF NOT EXISTS `document` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`dateDoc` datetime NOT NULL,
`numDoc` varchar(45) NOT NULL,
`idCategory` int(11) DEFAULT NULL,
`subject` varchar(200) DEFAULT NULL,
`status` varchar(1) NOT NULL DEFAULT 'A',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=155 ;
CREATE TABLE IF NOT EXISTS `item` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`idDoc` int(11) NOT NULL,
`numItem` int(11) NOT NULL,
`amount` decimal(14,4) NOT NULL,
`idTax` varchar(30) DEFAULT NULL,
`rateTax` decimal(5,2) DEFAULT NULL,
`amtTax` decimal(12,4) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=244 ;
How to sum up the amount and amtTax in Table 'item' if document(id) = item (idDoc).
For example :
(document table)
Document id = 1;
(item table)
id = 1;
idDoc = 1;
amount = 50.00;
amtTax = 10.00;
id = 2;
idDoc = 1;
amount = 30.00;
amtTax = 6.60;
I suppose to get RM96.60.
How to sum up the amount and amtTax in mysql as a new column named (Total) if idDoc match with document table's id?
Thank you for your help!
If you want to select all documents with their sums of amounts and amt taxes, use a join with group by over the documents:
SELECT SUM(item.amount) + SUM(item.amtTax) AS Total
FROM document
LEFT JOIN item
ON document.id = item.idDoc
If you want to select just a single document (e.g. the one with id 1), you don't need to use group by, just add where clause:
SELECT document.*, sum(item.amount) + sum(item.amtTax) as Total
FROM document LEFT JOIN item ON document.id = item.idDoc
WHERE document.id = 1

How do I join 2 columns in 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

how can i get all post for me and for user who i follow him

i have 3 table.
1st is for user info.
2nd is for post where user insert to it and relation with user table by user_id.
3rd is for user who i follow him and relation with user table by user_id.
now in home page i need to show all posts where i insert on it and where user i follow him insert into table post.
i will try to make this MySQL by :-
SELECT * FROM users ,
(SELECT * FROM events where ev_user_id in
(
( select * from follow where follow.fo_user_id = '".$user_id."' )
, '".$user_id."'
)
) as post
where post.ev_user_id = users.id
order by post.ev_date DESC limit $to , $from
where $user_id is id for user.
here i get error that:-
Operand should contain 1 column(s)
if i follow one user its work, but when i follow more than one user, its display above error.
how can i get all post for me and for user who i follow him
====================================
events table is the table for post
CREATE TABLE `events` (
`ev_id` int(11) NOT NULL auto_increment,
`ev_user_id` int(11) NOT NULL,
`ev_type` varchar(222) NOT NULL,
`ev_text` text NOT NULL,
`ev_pic` varchar(222) NOT NULL,
`ev_date` varchar(22) NOT NULL,
PRIMARY KEY (`ev_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=211 ;
table for user which i follow him
CREATE TABLE `follow` (
`fo_id` int(11) NOT NULL auto_increment,
`fo_user_id` int(11) NOT NULL,
`fo_user_id_follow` int(11) NOT NULL,
`fo_date` varchar(22) NOT NULL,
PRIMARY KEY (`fo_id`),
UNIQUE KEY `fo_user_id` (`fo_user_id`,`fo_user_id_follow`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=15 ;
table for user info
CREATE TABLE `users` (
`id` bigint(20) unsigned NOT NULL auto_increment,
`fullname` varchar(222) NOT NULL,
`username` varchar(60) NOT NULL,
`password` varchar(64) NOT NULL,
`email` varchar(50) NOT NULL,
`address` varchar(300) NOT NULL,
`phone` varchar(20) NOT NULL,
`skype` varchar(20) NOT NULL,
`facebook` varchar(40) NOT NULL,
`msn` varchar(90) NOT NULL,
`mobile` varchar(20) NOT NULL,
`more` text NOT NULL,
`time` varchar(50) NOT NULL,
`lastlogin` varchar(50) NOT NULL,
`role_id` tinyint(1) NOT NULL default '2',
`code` varchar(7) NOT NULL,
`active` tinyint(4) NOT NULL default '3',
`wieght` int(11) NOT NULL,
`tall` int(11) NOT NULL,
`birthdate` varchar(20) NOT NULL,
`gender` varchar(5) NOT NULL,
`fat` int(11) NOT NULL,
`fittnes` int(11) NOT NULL,
`status` int(11) NOT NULL,
`pic` varchar(222) NOT NULL,
`regdate` varchar(22) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `email` (`email`),
UNIQUE KEY `username` (`username`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=396 ;
This should get you the posts and user info of the users your $user_id follows ordered by descending date.
SELECT * FROM events
JOIN follow ON events.ev_user_id = follow.fo_user_id
JOIN users ON events.ev_user_id = user.id
WHERE follow.fo_user_id_follow = '".$user_id."'
ORDER BY events.ev_date DESC
LIMIT $to , $from
Is this what you wanted? not completelly sure.
EDIT: to add also your own posts as well as the ones form the users you follow.
SELECT * FROM events
JOIN follow ON events.ev_user_id = follow.fo_user_id
JOIN users ON events.ev_user_id = user.id
WHERE follow.fo_user_id_follow = '".$user_id."'
OR events.ev_user_id = '".$user_id."'
ORDER BY events.ev_date DESC
LIMIT $to , $from
EDIT: the enquirer's exact solution, Daren had understood the follow relationship reversed.
SELECT * FROM events
JOIN follow ON events.ev_user_id = follow.fo_user_id_follow
JOIN users ON events.ev_user_id = users.id
WHERE follow.fo_user_id = '".$user_id."'
OR events.ev_user_id = '".$user_id."'
ORDER BY events.ev_date DESC
LIMIT $to , $from"
The error message you're getting, is because you're using the IN operator against a subquery that returns more than one column. Maybe rewrite your SQL to something like this:
SELECT * FROM users ,
(SELECT * FROM events where ev_user_id in
(
( select user_id from follow where follow.fo_user_id = '".$user_id."' )
, '".$user_id."'
)
) as post
where post.ev_user_id = users.id
order by post.ev_date DESC limit $to , $from
I guess - You should provide specific column instead of * at the line -
( select * from follow where follow.fo_user_id = '".$user_id."' )
you could try with the below line -
select fo_user_id_follow from follow where follow.fo_user_id = '".$user_id."'
what about the following?
SELECT * FROM users
INNER JOIN follow ON fo_user_id=users.id
INNER JOIN events ON ev_user_id IN (users.id,fo_user_id_follow)
WHERE users.id='$user_id' ORDER BY post.ev_date DESC LIMIT $to , $from
You will probably get too many columns as it will list all the columns from all joined tables.
Edit: added ' around user_idjust in case users.id is a varchar column ...
Edit: widened criterion for user_id: ev_user_id IN (users.id,fo_user_id_follow)
rewrite:
SELECT * FROM follow
INNER JOIN users ON users.id=ev_user_id IN (users.id,fo_user_id_follow)
INNER JOIN events ON ev_user_id=users.id
WHERE users.id='$user_id' ORDER BY post.ev_date DESC LIMIT $to , $from
This should give you information about all users (including yourself) and all posts from these users that follow the user identified by $user_id. Probably not a good choice either, since you will have the full user information given again and again for each post.
It would be better to have two queries and two result tables: 1. user info, 2. posts of these users.
SELECT * FROM user
WHERE users.id IN (
SELECT fo_user_id_follow FROM follow WHERE fo_user_id=$user_id )
OR users.id=$user_id
and
SELECT * FROM events
WHERE ev_user_id IN (
SELECT fo_user_id_follow FROM follow WHERE fo_user_id=$user_id )
OR ev_user_id=$user_id

MySQL join problem

I want to join a pages table and menu table.
CREATE TABLE IF NOT EXISTS `pages` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL DEFAULT '',
`keywords` varchar(255) NOT NULL DEFAULT '',
`description` varchar(255) NOT NULL DEFAULT '',
`path` varchar(255) NOT NULL DEFAULT '',
`content` text NOT NULL,
`status` enum('active','inactive') NOT NULL DEFAULT 'active',
`category_id` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=21 ;
CREATE TABLE IF NOT EXISTS `menus` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`shortdesc` varchar(255) NOT NULL,
`page_id` varchar(60) NOT NULL,
`status` enum('active','inactive') NOT NULL,
`parentid` int(11) NOT NULL,
`order` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=79 ;
I have errors with the following SQL.
function generateTree(&$tree, $parentid = 0) {
$res = $this->db->query('SELECT M.*, P.name AS PageName
WHERE M.parentid = $parentid
ORDER BY M.order asc, M.parentid asc
FROM menus AS M
LEFT JOIN pages AS P
ON P.id = M.page_id');
...
...
Can you tell what I am doing wrong?
Thanks in advance.
You've got your SQL syntax mixed up
$res = $this->db->query('
SELECT
M.*, P.name AS PageName
FROM
menus AS M
LEFT JOIN pages AS P ON P.id = M.page_id
WHERE
M.parentid = $parentid
ORDER BY
M.order asc, M.parentid asc
');
BTW, you should bot be using variables in the SQL string. Use parameterized queries instead (mysyqli*).