MYSQL query, joining 2 table's issue - mysql

I have this query -
SELECT interest_desc, categoryID, MAX(num_in_cat) AS num_in_cat
FROM
(
SELECT interest_desc, categoryID, COUNT(categoryID) AS num_in_cat
FROM interests
GROUP BY interest_desc, categoryID
) subsel
GROUP BY interest_desc, categoryID
I want to change it so that I can eventually display the category name from a separate table called categories. All I can display is the categoryID from interests with this sql
Both table structures are
#interests
CREATE TABLE `interests` (
`interestID` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`categoryID` int(11) NOT NULL,
`sessionID` int(11) NOT NULL,
`interest_desc` varchar(30) NOT NULL,
`timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`interestID`)
) ENGINE=MyISAM AUTO_INCREMENT=9 DEFAULT CHARSET=utf8
categories table structure
# categories
CREATE TABLE `categories` (
`categoryID` int(11) NOT NULL AUTO_INCREMENT,
`category_desc` varchar(100) NOT NULL,
PRIMARY KEY (`categoryID`)
) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
I know a join of some sort is needed but I have looked at examples and are struggling to get the exact syntax.
I have this in a php script - the echo statement is this
"{$result['interest_desc']} was the most popular in category {$result['categoryID']} with {$result['num_in_cat']} occurrences\n";
and its output is this -
"Adidas was the most popular in category 5 with 1 occurrences"
I want the output to be "Adidas was the most popular in Sport with 1 occurrences"
However my sql query does not feature category_desc.

This is more quick performance wise
SELECT subsel.interest_desc, subsel.categoryID, cat.category_desc, MAX(num_in_cat) AS num_in_cat
FROM
(
SELECT interest_desc, categoryID, COUNT(categoryID) AS num_in_cat
FROM interests
GROUP BY interest_desc, categoryID
) subsel
inner join categories as cat on subsel.categoryID = cat.categoryID
GROUP BY interest_desc, subsel.categoryID

Kindly check this , It will give you the required result.
SELECT subsel.interest_desc, cat.category_desc, MAX(num_in_cat) AS num_in_cat
FROM
(
SELECT interest_desc, categoryID, COUNT(categoryID) AS num_in_cat
FROM interests
GROUP BY interest_desc, categoryID
) subsel
inner join categories as cat on subsel.categoryID = cat.categoryID
GROUP BY interest_desc, subsel.categoryID

SELECT * FROM interests i LEFT JOIN categories c ON i.categoryID = c.categoryID
I haven't tested it. There might be syntax errors.

I do not know in what realistic scenarios the two similar queries as you posted make sense. I would say you can go with this straightaway:
SELECT i.interest_desc, c.category_desc, COUNT(i.categoryID) AS num_in_cat
FROM interests AS i
INNER JOIN categories AS c USING (categoryID)
GROUP BY i.interest_desc, i.categoryID

Related

Computed Column as Average from Other Data in Another Table

I am creating a simple movie website that allows users to browse through movie titles, and rate movies with a 5 point rating system. I am using XAMPP, and phpAdmin to store my database through the SQL language. I have the following table below that stores ratings:
CREATE TABLE `movie_ratings` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`movie_id` int(11) DEFAULT NULL,
`rating` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
I have the following table below that stores movies:
CREATE TABLE `movies` (
`movie_title` varchar(255) NOT NULL,
`movie_id` int(100) NOT NULL,
`genre` text NOT NULL,
`release_date` text NOT NULL,
`price` int(100) NOT NULL,
`year` year(4) NOT NULL,
`description` text NOT NULL,
`movie_image` text NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
I want to add a column, average_rating to my movies table that basically takes all rating values from movie_ratings from a specific movie_id and averages them and then stores the value into this new average_rating column within the movies table.
I believe I need a computed column, but I only know how to do this via columns from the same table, here I am using another table. I read that a view could be possible too... Would prefer to store the average rating in movies though as a new column.
So I created a VIEW but it's only displaying one movie and averaging.
CREATE VIEW movie_ratings_view AS
SELECT b.movie_id, b.movie_title, b.movie_image, b.price, AVG(br.rating)
AS avgRating FROM movies b INNER JOIN movie_ratings br ON
b.movie_id = br.movie_id;
You need a group by clause
CREATE VIEW movie_ratings_view
AS
SELECT
b.movie_id
, b.movie_title
, b.movie_image
, b.price
, AVG(br.rating) AS avgRating
FROM movies b
INNER JOIN movie_ratings br ON b.movie_id = br.movie_id
GROUP BY
b.movie_id
, b.movie_title
, b.movie_image
, b.price
;
An alternative (if/when subqueries are allowed in the from clause of a view by MySQL):
CREATE VIEW movie_ratings_view
AS
SELECT
m.movie_id
, m.movie_title
, m.movie_image
, m.price
, COALESCE(mr.numRatings, 0) AS numRatings
, COALESCE(mr.avgRating, 0) AS avgRating
FROM movies m
LEFT JOIN (
SELECT
movie_id
, COUNT(rating) AS numRatings
, AVG(rating) AS avgRating
FROM movie_ratings
GROUP BY movie_id) mr ON m.movie_id = mr.movie_id
;
Probably the most efficient code for a view is a correlated subquery:
CREATE VIEW movie_ratings_view AS
SELECT m.movie_id, m.movie_title, m.movie_image, m.price
(SELECT AVG(mr.rating)
FROM movie_ratings mr
WHERE mr.movie_id = m.movie_id
) as avgRating
FROM movies m;
This can make use of an index on movie_ratings(movie_id, rating). And it doesn't have the outer GROUP BY which can be expensive.
It is slightly different because this will return movies with no ratings -- but that might even be desirable.

How would I get articles from a certain category and its children?

I'm developing an article system that uses categories and child categories.
Basically, if the category has a parent_id value, it's a child of that category.
I would like to be able to get the most recent articles from a category and articles from its child categories.
For example: I have a category called "Gaming Articles" and several child categories under that called Xbox, PlayStation, Nintendo, and PC. My system makes it possible to post articles in the parent categories such as Gaming Articles as well as in the child categories.
So this would have to include articles that are in either the parent category or the child categories of that parent.
CREATE TABLE IF NOT EXISTS `articles` (
`article_id` int(15) NOT NULL AUTO_INCREMENT,
`author_id` int(15) NOT NULL,
`category_id` int(15) NOT NULL,
`modification_id` int(15) NOT NULL,
`title` varchar(125) NOT NULL,
`content` text NOT NULL,
`date_posted` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`status` tinyint(1) NOT NULL,
`attachment_id` int(15) NOT NULL,
PRIMARY KEY (`article_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS `article_categories` (
`category_id` int(15) NOT NULL AUTO_INCREMENT,
`parent_id` int(15) NOT NULL,
`title` varchar(50) NOT NULL,
`description` text NOT NULL,
`attachment_id` text NOT NULL,
`enable_comments` tinyint(1) NOT NULL,
`enable_ratings` tinyint(1) NOT NULL,
PRIMARY KEY (`category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
The query I have so far...
SELECT article_id, category_id
FROM articles
WHERE category_id = 1
ORDER BY article_id DESC
LIMIT 10
Of course, this only gets articles under that category, not from both the category and that category's child categories.
As your table structure stands, this query will work assuming only 1 level of nesting (ie children don't themselves have children):
SELECT a.*
FROM articles a
JOIN article_categories ac ON a.category_id = ac.category_id
WHERE 1 IN (a.category_id, ac.parent_id)
ORDER BY a.article_id DESC
LIMIT 10
Note the "reversed" style IN to neatly capture what is effectively an OR.
If your nesting is deeper, simply add another join for each level, for example if you have up to 4 levels (2 more than the above query):
SELECT a.*
FROM articles a
JOIN article_categories ac1 ON a.category_id = ac1.category_id
LEFT JOIN article_categories ac2 ON ac1.parent_id = ac2.category_id
LEFT JOIN article_categories ac3 ON ac2.parent_id = ac3.category_id
WHERE 1 IN (a.category_id, ac1.parent_id, ac2.parent_id, ac3.parent_id
ORDER BY a.article_id DESC
LIMIT 10
In the second case, the use of left joins is necessary to still return articles that don't have so many levels above.
In this structure is inposible in one query. (I assume that there are many levels of categories)
You can:
recursively search child categories (ex. in php) and then
SELECT * FROM articles WHERE category_id IN ( $categories );
change db structure and use tree structure
try: The Nested Set Model in article : http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/
add new column to categories table, and store full path to category, ex.:
category 1->2, category_path 1,2
category 1->2->3, category_path 1,2,3
category 1->4, new category_path 1,4
If You looking all data in category 1 and children, try:
SELECT
a.*
FROM articles a
INNER JOIN categories c
ON a.category_id = c.category_id
WHERE c.category_path LIKE '1,%'
As far as i understand, what you want is to get all the articles from a child of a category, if that is correct, try this out:
SELECT a.article_id, a.category_id FROM articles as a, article_categories c WHERE a.category_id = c.category_id AND c.parent_id = (SELECT c.parent_id WHERE c.category_id = 1) ORDER BY article_id DESC LIMIT 10
If this wasn't what you wanted, comment it, i'll try to answer you.

Filtering product records by criteria in MySQL

I have a database that contains products, beds to be specific. The details of each bed is stored in a products table, but then I have a separate table, product_bedding_sizes that holds records for each size a bed can in. So one bed may have five entries in this table (it’s a one-to-many relationship).
That bit is done. However, on the public website, visitors can search products and restrict their search to a particular bed size. So my question is, how can I take an array of sizes and select all bed products that may have one or more entries for the specified sizes?
A simplified schema:
CREATE TABLE `products` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB;
CREATE TABLE `product_bedding_sizes` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`product_id` int(10) unsigned NOT NULL,
`size` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `product_id` (`product_id`)
) ENGINE=InnoDB;
ALTER TABLE `product_bedding_sizes`
ADD CONSTRAINT `product_bedding_sizes_ibfk_1`
FOREIGN KEY (`product_id`)
REFERENCES `products` (`id`)
ON DELETE CASCADE
ON UPDATE CASCADE;
If the user selects say, 90cm and 120cm, the plain English explanation of the query would be:
Select all from the products table where it may have a sizein the product_bedding_sizes table that’s 90cm or 120cm.
I take your requirement to be that a row should be returned if the product has a size of 90 OR if it has a size of 120, or both. (The query would be slightly different to return rows where the product has both size of 90 AND a size of 120.)
Any of these queries return the specified result:
SELECT p.id
, p.name
FROM products p
JOIN product_bedding_sizes s
ON s.product_id = p.id
AND s.size IN (90,120)
GROUP
BY p.id
, p.name
--or--
SELECT p.id
, p.name
FROM products p
JOIN ( SELECT s.product_id
FROM product_bedding_sizes s
WHERE s.size IN (90,120)
GROUP BY s.product_id
) t
ON t.product_id = p.id
ORDER
BY p.id
, p.name
-- or --
SELECT p.id
, p.name
FROM products p
WHERE EXISTS
( SELECT 1
FROM product_beddings_sizes s
WHERE s.product_id = p.id
AND s.size IN (90,120)
)
ORDER
BY p.id
, p.name
-- or --
SELECT p.id
, p.name
FROM products p
WHERE p.id IN
( SELECT s.product_id
FROM product_bedding_sizes s
WHERE s.size IN (90,120)
)
ORDER
BY p.id
, p.name

sql join on two fields in one table

i have bookings table which has two people- i want to return person_1 as a row, person_2 as a new row but with the person's id related to the people table
This is as far as i got-but doesnt pull in booking info
SELECT people.* FROM (
(select booking.person_1 as id from booking)
union ALL
(select booking.person_2 as id from booking)
) as peopleids
join people on people.id = peopleids.id;
heres my structure
CREATE TABLE IF NOT EXISTS `booking` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`slot` enum('morning_drive','afternoon_loop','return_drive') NOT NULL,
`type` enum('911','vintage_911') NOT NULL,
`car` int(11) NOT NULL,
`person_1` int(11) DEFAULT NULL,
`person_2` int(11) DEFAULT NULL,
`dated` date NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ;
CREATE TABLE IF NOT EXISTS `people` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`first_name` varchar(50) NOT NULL,
`last_name` varchar(50) NOT NULL,
`organisation` varchar(100) NOT NULL,
`event_date` date NOT NULL,
`wave` varchar(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=9 ;
any ideas on how i could get a result set like- person.first_name, person.last_name, person.organisation, booking.dated, person.car, person.slot. im struggling with having two fields and having them to relate them into the one list
update for anyone interested in this and joining a 3rd table
heres my final query with php vars to pull in my certain dates and slots and also join a third table
SELECT peopleids.id,
peopleids.car,
cars.nr,
p.first_name,
p.last_name,
p.organisation,
p.event_date,
p.wave
FROM (SELECT booking.car, booking.person_1 as id FROM booking WHERE booking.dated = '".$date."' AND booking.`slot` = '".$slot."'
union ALL SELECT booking.car, booking.person_2 as id FROM booking WHERE booking.dated = '".$date."' AND booking.`slot` = '".$slot."'
) as peopleids
LEFT JOIN people p ON p.id = peopleids.id LEFT JOIN cars on cars.id = peopleids.car;
SELECT
ag.id,
p.first_name,
p.last_name,
p.organisation,
p.event_date,
p.wave
FROM (
SELECT booking.person_1 as id, booking.Car as car FROM booking
union ALL
SELECT booking.person_2 as id, booking.Car as car FROM booking
) as ag
JOIN people p ON people.id = ag.id;
INNER | LEFT JOIN Cars c ON c.ID = ag.car
select people.first_name as firstname,
people.last_name as lastname,
people.organisation,
booking.dated,
booking.car,
booking.slot from booking
left join people on booking.person_1 = people.id
OR
select people.first_name as firstname,
people.last_name as lastname,
people.organisation,
booking.dated,
booking.car,
booking.slot
from booking
left join people on booking.person_1 = people.id or booking.person_2 = people.id
check that...if you still need help will help you

how to get sub categories with category detail?

i have two tables.
tb_category table
CREATE TABLE IF NOT EXISTS `tb_category` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
)
tb_subcategory table
CREATE TABLE IF NOT EXISTS `tb_subcategory` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`category_id` int(11) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
)
i want to get subcategories for selected category with parent category details.
so i need to get specified category row and it subcategories in single query
thanks in advance
SELECT c.name catname,c.id cat, s.id subcat, s.name subname
FROM tb_category c LEFT JOIN tb_subcategory s ON c.id = s.category_id
WHERE c.id = <category>;
This will pull up a single category record, id=, and all the associated subcategory records.
The output will be something like:
catname cat subcat subname
Category-10 10 1 Cat-10 Subcat-1
Category-10 10 2 Cat-10 Subcat-2
Category-10 10 3 Cat-10 Subcat-3
Try to write a query using JOIN clause and bind rows by tb_category.id and tb_subcategory.category_id fields.