MySQL query for searching subset of two tables - mysql

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.

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

Product database - MySQL query with multiple JOINs

First, my table structure:
Products Table:
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`price` decimal(10,2) NOT NULL,
`list_price` decimal(10,2) NOT NULL,
`brand` int(11) NOT NULL,
`category` int(11) NOT NULL,
`image` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`description` text COLLATE utf8_unicode_ci NOT NULL,
`featured` tinyint(4) NOT NULL DEFAULT '0',
`deleted` tinyint(4) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
Categories Table:
`id` int(11) NOT NULL AUTO_INCREMENT,
`category` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`parent` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
Brand Table:
`id` int(11) NOT NULL AUTO_INCREMENT,
`brand` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`)
Stock Table:
`id` int(11) NOT NULL AUTO_INCREMENT,
`product_id` int(11) NOT NULL,
`size` varchar(4) COLLATE utf8_unicode_ci NOT NULL,
`stock` int(11) NOT NULL,
`sold` int(11) NOT NULL,
`reserved` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `product_sizes` (`product_id`,`size`),
KEY `product_id` (`product_id`),
CONSTRAINT `stock_ibfk_1` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`)
I wanted a single SQL query that grabs all the products by whatever criteria, and adds the total stock and total sold from the STOCK table, the brand name from the BRAND table, the child and parent categories from the CATEGORY table.
This was my attempt, which doesn't work:
"SELECT
a.title,
COALESCE(SUM(e.stock),0),
COALESCE(SUM(e.sold),0),
a.price,
c.category AS 'parent',
d.category AS 'child',
b.brand,
a.featured
FROM
products a
JOIN brand b
ON a.brand = b.id
JOIN categories c
ON a.category = c.id
LEFT JOIN categories d
ON c.parent = d.id
JOIN stock e
ON a.id = e.product_id
WHERE a.deleted = 0 ORDER BY a.title ASC"
Initially I had a series of queries instead - first getting all the products, then grabbing the categories, then the brands, then the stock/sold. I'm just wondering if I can do it all in the one query?
I am quite new at SQL.
Thanks to #Shadow, this is the query I needed, which works great:
SELECT
a.id,
a.title,
COALESCE(SUM(e.stock),0) AS 'stock',
COALESCE(SUM(e.sold),0) AS 'sold',
a.price,
c.category AS 'child_category',
d.category AS 'parent_category',
b.brand,
a.featured
FROM
products a
JOIN brand b
ON a.brand = b.id
JOIN categories c
ON a.category = c.id
LEFT JOIN categories d
ON c.parent = d.id
JOIN stock e
ON a.id = e.product_id
WHERE a.deleted = 0
GROUP BY a.id, a.title, a.price, c.category, d.category, b.brand, a.featured ORDER BY a.title ASC

how can i extract total forum_question and fourm_answer count as subject wise in mysql i have below mysql structure

forum_question :
CREATE TABLE IF NOT EXISTS `forum_question` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
`question` text NOT NULL,
`subject_id` int(11) NOT NULL,
`student_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `subject_id` (`subject_id`,`student_id`),
KEY `student_id` (`student_id`)
)
forum_answer table
CREATE TABLE IF NOT EXISTS `forum_answer` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`answer` text NOT NULL,
`question_id` int(11) NOT NULL,
`faculty_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `question_id` (`question_id`,`faculty_id`),
KEY `faculty_id` (`faculty_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ;
and subject table
CREATE TABLE IF NOT EXISTS `subject` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(150) NOT NULL
)
Try this edited query, I am sure this will work
SELECT
COUNT(que.id) AS totalQuestions,
(SELECT COUNT(DISTINCT(forum_answer.id))
FROM
forum_answer
WHERE
forum_answer.question_id = que.id
GROUP BY que.id)
AS totalAns
FROM
forum_question que
INNER JOIN
subject sub
ON (que.subject_id = sub.id)
WHERE
que.id > 0
GROUP BY sub.id
If by total you mean the total amount of rows, you can do the following:
`SELECT COUNT(*) FROM forum_question WHERE subject_id=YOURSUBJECT`
if you want just all rows, remove the WHERE clause.
Please you can try this Query
SELECT
subject.name AS subject_name,
count(forum_question.id) AS total_question,
(SELECT count(forum_answer.id)
FROM
forum_answer
WHERE
forum_answer.question_id = forum_question.id) AS total_answer
FROM
subject
Inner Join forum_question ON subject.id = forum_question.subject_id
GROUP BY subject.id
Sorry it was my mistake. now I edited the query and hope it will work for you

How can I query for rows with latest date and do an inner join on a second table?

All the examples I've seen show how to do an inner join using an alias to get rows with the latest date. I can do that with my data but I also want to do an inner join on another table and can't figure how to do both with the same query.
Here are the two tables:
CREATE TABLE `titles` (
`titleID` int(11) unsigned NOT NULL AUTO_INCREMENT,
`titlename` tinytext NOT NULL,
`url` varchar(255) DEFAULT '',
`category` int(2) unsigned NOT NULL,
`postdate` date NOT NULL,
PRIMARY KEY (`titleID`),
KEY `category` (`category`),
CONSTRAINT `titles_ibfk_1` FOREIGN KEY (`category`) REFERENCES `categories` (`catid`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1;
CREATE TABLE `stats` (
`statid` int(11) unsigned NOT NULL AUTO_INCREMENT,
`score` decimal(3,2) DEFAULT NULL,
`views` int(11) unsigned DEFAULT NULL,
`favs` int(11) DEFAULT NULL,
`comments` int(11) DEFAULT NULL,
`updatedate` date NOT NULL,
`title` int(11) unsigned NOT NULL,
PRIMARY KEY (`statid`),
KEY `title` (`title`),
CONSTRAINT `stats_ibfk_1` FOREIGN KEY (`title`) REFERENCES `titles` (`titleID`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=latin1;
My goals:
1) I want a query that gives me all the latest stats for each title.
2) I want to see the text name of the title (from the titles table).
I can use this query to get the latest score for each title.
select t.score, t.views, t.favs, t.comments, t.updatedate, t.title
from stats t
inner join (
select title, max(updatedate) as updatedate
from stats
GROUP BY title
) tm on t.title = tm.title and t.updatedate = tm.updatedate
But the problem with this query is that it displays the title column from stats which is an int. I want the text name of the title.
I can do this to get the title name and the score, but then I'm not getting the row with the latest date.
select titlename, score, updatedate
from stats
inner join titles
on titleid = title
How can I write a query that achieves both my goals?
You need to join the title table in this case as
select
s1.score,
s1.views,
s1.favs,
s1.comments,
s1.updatedate,
t.titlename
from titles t
join stats s1 on s1.title = t.titleID
join (
select title, max(updatedate) as updatedate
from stats
GROUP BY title
) s2 on s2.title = s1.title and s1.updatedate = s2.updatedate

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