Count subcategories with a condition - php

I have got 2 tables: categories and products.
Categories have parent-child relationship structure and the data is fetched by joining the same table.
When fetching the data, I have to count:
how many products each category contains which have stock >= 1
how many subcategories a category contains which contain at least 1 product with stock >= 1
SELECT c. * , count( DISTINCT s.cat_id ) AS number_of_subcategories, count( DISTINCT p.id ) AS number_of_products FROM categories c
LEFT JOIN categories s
ON s.parent_id = c.cat_id
LEFT JOIN products p
ON p.cat_id = c.cat_id AND p.stock >= 1
GROUP BY c.cat_name
ORDER BY number_of_products ASC
At the first glance all goes well, but unfortunately I get total number of all subcategories.
Do I miss one more join or what is my problem so far?
Here is the code: SQLFiddle

You could alter your query to use a subquery to get the number of subcategories similar to this:
SELECT c.cat_id,
c.parent_id,
c.cat_name,
count(sp.cat_id) AS number_of_subcategories,
count(DISTINCT p.id) AS number_of_products
FROM `categories` c
LEFT JOIN
(
select distinct s.cat_id, s.parent_id
from categories s
inner join products p
on s.cat_id = p.cat_id
where p.stock > 1
) sp
ON sp.parent_id = c.cat_id
LEFT JOIN products p
ON p.cat_id = c.cat_id
AND p.stock >= 1
GROUP BY c.cat_id, c.parent_id, c.cat_name;
See SQL Fiddle with Demo

Try changing AND for WHERE. Does it work?
Francisco

Related

Improve this query with use of union

This query gives an error if subquery return more than 1 row. I separated the queries and use mysqli_multi_query(), but both queries data is displayed in two tables.
So I decided to make the one query.
SELECT DISTINCT category ,
(SELECT COUNT(products.name)
FROM products
where category_id=categories.id
) AS total_products,
(
SELECT SUM(quantity) FROM productstock a
LEFT JOIN products b ON a.product_id=b.id
LEFT JOIN categories c ON b.category_id=c.id
where c.deleted=0
GROUP BY category_id
) AS available_stock,
SUM(product_qty*orignalCost) AS SaleWise_cost,
SUM(product_qty*saleprice) AS SaleWise_price,
SUM(product_qty*saleprice) AS total_sale ,
SUM((product_qty*saleprice)-(product_qty*orignalCost)) AS profit
FROM categories
INNER JOIN products ON categories.id = products.category_id
INNER JOIN sales ON sales.product_id = products.id
INNER JOIN productstock ON productstock.product_id = products.id
WHERE categories.deleted=0
GROUP BY category_id
As available stock is corelated subquery so joining condition must be added in where clause inside subquery. please check this pseudocode
(
SELECT SUM(quantity) FROM productstock a
LEFT JOIN products b ON a.product_id=b.id
LEFT JOIN categories c ON b.category_id=c.id
where c.deleted=0 AND b.category_id = categories.id
) AS available_stock
Another way
(SELECT SUM(quantity)
FROM products b
INNER JOIN productstock a
ON b.id = a.product_id
AND b.id = products.id
AND b.category_id = categories.id) AS available_stock

MySQL query search using joins

I have the following tables:
products table
-------------
id
name
...
categories table
----------------
id
name
product_categories table
------------------------
product_id
category_id
product_ratings
---------------
product_id
user_id
rating (1 to 5 INT)
How can I select (search) products and rating (average) by Category (name or id) and order them by Title or by rating.
I have tried some queries but im having some trouble on how to join tables and how to use where clause
Do you mean something like that?
select
products.name, categories.name, product_ratings.rating
from products
join product_categories on products.id = product_categories.product_id
join categories on categories.id = product_categories.category_id
join product_ratings on product_ratings.product_id = products.id
where
categories.name = 'CATEGORY NAME'
order by
product_ratings.rating
select p.name, r.rating
from products as p
inner join product_ratings as r on p.id = r.product_id
inner join categories as c on c.id = pc.category_id
inner join product_categories as pc = pc.product_id = p.id
where c.name = 'search_name'
order by r.rating;
Method 1
Rating by product and user id
SELECT
P.name, PR.user_id , IFNULL(PR.rating,0) as rating
FROM products P
JOIN product_categories PC ON P.id = PC.product_id
JOIN categories C ON C.id = PC.category_id
JOIN product_ratings PR ON PR.product_id = P.id
WHERE C.name = 'Category A'
ORDER BY P.name
Refer http://sqlfiddle.com/#!9/014af/6
Method 2
Rating by product id
Different users will rate the same product, so we have to consider the average rating of each product.
SELECT
P.name, GROUP_CONCAT(PR.user_id) as user_ids, AVG(IFNULL(PR.rating,0)) as rating
FROM products P
JOIN product_categories PC ON P.id = PC.product_id
JOIN categories C ON C.id = PC.category_id
JOIN product_ratings PR ON PR.product_id = P.id
WHERE C.name = 'Category A'
GROUP BY P.id
ORDER BY P.name
Refer http://sqlfiddle.com/#!9/014af/7

MySQL - WHERE / HAVING count

I searched lots of similar topics about this but can't find the answer to this specific probem.
So I have a table with categories and I have another table with products, so what I want is select all the categories that contain at least 1 product, seems very easy but the following code dont give me what I expect.
SELECT *
FROM categories
INNER JOIN products on (categories.id = products.cat_id)
HAVING count(products.cat_id) > 0
All help is appreciated.
Thank you!
You can actually get what you want with just a join because any categories without products will not match.
You should also add a DISTINCT or GROUP BY to remove the duplicate category records from the results:
SELECT DISTINCT c.*
FROM categories c
JOIN products p
ON c.id = p.cat_id
OR:
SELECT c.*
FROM categories c
JOIN products p
ON c.id = p.cat_id
GROUP BY c.id
If you want something fancy like categories that have 2 or more products then you can use GROUP BY and HAVING:
SELECT c.*
FROM categories c
JOIN products p
ON c.id = p.cat_id
GROUP BY c.id
HAVING count(*) >= 2
Try this select in this select all categories that have at least one product
select categories.* from categories
left join products on (categories.id = products.cat_id)
where products.cat_id IS NOT NULL
group by categories.id
In this case you're not using "HAVING" but will have the same result

Only get active products from table related to category

I got 2 tables:
1 Category
- cID
- Name
- Active
2 Products
- pID
- Name
- category_id
- active
This is want i want:
I want a list from categories that ONLY have ACTIVE products.
Thanks in advance
SOLUTION:
SELECT DISTINCT category.* FROM category INNER JOIN products ON category.id = products.c_id WHERE products.active = 0 ORDER BY category.id DESC
Assume Products.Active = 1 means active state.
SELECT c.*
FROM Category c
INNER JOIN Products p ON c.CID = p.category_id
GROUP BY c.CID
HAVING COUNT(*) = SUM(IF(p.Active = 1, 1, 0))
I'd suggest construction like this
IMHO, it's syntax shows what you really want to see - all categories that have active products
select C.cID, C.Name, C.Active
from Category as C
where C.cID in (select distinct T.category_id from Products as T where T.Active = 1)
SELECT * FROM Products
Join Category on Category.ciD = Products.category_id
WHERE Category.Active = 1 AND Products.Active =1
This is assuming your active columns are just 1 or 0. Otherwise adjust according to what values you're storing there.

How to query 3 tables in a single query?

I'm really sorry for the first post as i didn't explain everything.
Basically i have 3 tables, One for posts, One for Categories, & Another to link categories with posts.
I want in a single MySQL query to select posts that are under a specific category.
posts(id,title,body)
---------------------
125,Some title,Blah blah
categories(id,name)
---------------------
1,politic
2,entertainment
linker(categoryid,postid)
---------------------
2,125
I want in single query to fetch posts in the entertainment category by example, what to do?
Thanks
select
p.*
from
posts p
inner join linker l on l.postid = p.id
inner join categories c on c.categoryid = l.categoryid
where
c.name = 'entertainment'
The following SQL statement should provide you with a basis for what you are trying to do.
select p.*
from posts p
inner join linker l
on l.postid = p.id
inner join categories c
on l.categoryid = c.id
where c.name = 'entertainment'
If a post belongs to 2 categories, you can still use pinkfloydx33's query with DISTINCT in the select statement:
select
DISTINCT p.*
from
posts p
inner join linker l on l.postid = p.id
inner join categories c on c.categoryid = l.categoryid
where
c.name = 'entertainment'
The result set will show only one record.
It's the same exact thing, you just have to join 3 tables intead of 2 :
SELECT P.id post_id,
P.title,
P.body,
C.id category_id,
C.name
FROM posts P
INNER JOIN linker L
ON P.id = L.postid
INNER JOIN categories C
ON L.categoryid = C.id
WHERE C.name = 'Category'
Don't be afraid to do your own tests. If you understand how to join two tables, you should understand how to join three, four and more.
If you are specifying only one category in the WHERE clause, then the result will be a single row for each post ID.
Either way you can use DISTINCT or GROUP BY when the result could be more than one row per ID, but in that case i prefer the second one (GROUP BY).

Categories