mysql count subtables and its subtables for its main table - mysql

Table 1 - Category
id | name
Table 2 - subcat
id | cid(category.id) | name
Table 3 - products
id | cid(category.id) | sid(subcat.id) | name
select a.* , count(b.id) as total
from category a left join
subcategory b on a.id=b.cid
group by a.id order by a.name
this gives the count of sub categories for each category
and I can run seperately for products so that i will get count of products for each category
what I want is the count of subcategories and the count of products for each category. How to form the query?
It should be like catename, count of (sub categories) and count of (products)

Because you may have sub-categories for a given category, but no products actually listed for such sub-category, you might not get the results you expect... This should catch both for you
select a.id,
a.name,
BySubCat.AvailableSubCategories,
ByProduct.ActualSubCats,
ByProduct.ProductCount
from
category a
left join
( select cid, count(*) as AvailableSubCategories
from subcat
group by cid ) BySubCat
on a.id = BySubCat.cid
left join
( select cid,
count( distinct sid ) as ActualSubCats,
count(*) ProductCount
from
products
group by
cid ) ByProduct
on a.id = ByProduct.cid
order by
a.name

Try this:
select c.name,count(sc.id),count(sub.pcount)
from subcat sc
inner join
(
select p.sid as subid, count(p.id) as pcount from Products p
inner join subcat sc on sc.id = p.sid
group by p.sid
) sub
on sub.subid = sc.id
inner join Category c on c.id = sc.cid
group by sc.id,sub.pcount

Related

SQL Product Categories with Product Count and Parent-Categories

I am trying to write a SQL query to fetch:
Category_ID, Category_Name, Category_Parent, Category, ProductCount
from two tables Product_Category and Product
Product_Category has columns: id, name, parent_id, status
Product has columns: id, category_id, name
I want to get the result from the query:
Category_ID | Category_Name | Category_Parent | ProductCount
Using over you can do the same group by work in aggregate functions.
SELECT
DISTINCT Product_Category.id AS Category_ID,
Product_Category.name AS Category_Name,
Product_Category.parent_id AS Category_Parent,
COUNT(Product.id) OVER (partition BY Product_Category.id) AS ProductCount
FROM Product JOIN Product_Category ON Product.category_id = Product_Category.id
result in mysql : https://dbfiddle.uk
So, I found the solution to the problem.
SELECT
X.id,
X.category,
X.parent,
X.status,
Y.product_count
FROM
(
SELECT
c.id AS id,
c.name AS Category,
p.name AS Parent,
c.status AS
STATUS
FROM
product_category c
LEFT JOIN product_category p ON
c.parent_id = p.id
) X
LEFT JOIN(
SELECT
c.id,
COUNT(p.id) AS product_count
FROM
product_category c
LEFT JOIN product p ON
c.id = p.category_id
GROUP BY
p.category_id
) AS Y
ON
X.id = Y.id

php - Check same column twice in mysqli query

I have 3 tables in my php based system.
Those tables are product, category, product_categories.
Product
pid | product_name | price
1 | Nike T-Shirt | 23
Category
cid | category_name
1 | Men
2 | Women
Product_categories
pcid | cid | pid
1 | 1 | 1
2 | 2 | 1
That means, 1 product may be in both multiple categories.
Now I am developing the product search section with filter.
If a user select both categories, all the products in selected categories should display.
Example : If a user select both Men & Women, Nike T-Shirt should be displayed.
The query I used:
select p.*
from products p
left join product_categories pc on pc.pid=p.pid
WHERE pc.cid ='1' AND pc.cid = '2'
But it not returning correct products.
Where is the error?
You want conditions that span over multiple rows, which suggests aggregation. You can join the tables, group by product, and use the having clause for filtering:
select p.id, p.product_name, p.price
from product p
inner join product_category pc on pc.pid = p.pid
inner join category c on c.cid = pc.cid
where c.category_name in ('Men', 'Women')
group by p.pid, p.product_name, p.price
having max(c.category_name = 'Men') = 1 and max(c.category_name = 'Women') = 1
If you can filter by category id rather than by category name, then you need one less join:
select p.id, p.product_name, p.price
from product p
inner join product_category pc on pc.pid = p.pid
where pc.cid in (1, 2)
group by p.pid, p.product_name, p.price
having max(pc.cid= 1) = 1 and max(pc.cid = 2) = 1
Here are few alternatives:
-- All products that are at lest in one of the desired categories
select * from product where product.pid = any (select product_category.pid from product_category where cid = 1 or cid = 2);
-- Products that are in both categories
select * from product where product.pid = any (
select product_category.pid from product_category where cid = 1 or cid = 2 group by pid having count(cid) = 2
);
-- Products with additional information "in how many desired categories they are"
-- You can order by it and/or filter on it
with product_category_matches as (
select pid, count(*) as category_count from product_category where cid in (1, 2) group by pid
)
select *, product_category_matches.category_count from
product inner join product_category_matches on product.pid = product_category_matches.pid
where product_category_matches.category_count > 0 -- Product must be at least in one desired category
order by product_category_matches.category_count desc

SQL query which return counted results from many tables

I have two tables 'shops' and 'products'. Shop has many products and product belongs to one shop. Moreover product belongs to the only one category. I have id's of 3 categories (for example 1,2,3). How can I get all shops having products which belongs to all 3 categories?
I tried
SELECT distinct s.*
from shops s
left join products p on p.shop_id = s.id
where p.category_id in (1,2,3)
but this returns shops with products which belongs to the category 1 OR 2 OR 3 But I want the products which belongs to the all 3 categories like 1 AND 2 AND 3, so every shop have to have at least 3 products
You could check the s.id having count(distinct p.category_id) = 3
SELECT s.id
from shops s
inner join products p on p.shop_id = s.id
where p.category_id in (1,2,3)
group by s.id
having count(distinct p.category_id) = 3
SELECT s.*
from shops s
join products p
on p.shop_id = s.id
where p.category_id in (1,2,3)
Group
by s.id
Having count(distinct p.category_id) = 3
Or something like that
Try this?
SELECT DISTINCT A.ID FROM SHOPS A, PRODUCTS B
WHERE A.ID = B.SHOP_ID AND EXISTS (SELECT COUNT(1) FROM PRODUCTS C, PRODUCTS D, PRODUCTS E WHERE B.ID = C.ID AND C.ID = D.ID AND D.ID = E.ID AND C.CATEGORY_ID = 1 AND D.CATEGORY_ID = 2 AND E.CATEGORY_ID = 3)
My T-SQL is rusty and this assumes I got the relations correct such that Shops 1 - M Products and, I'm unsure if Category is an entity but Category 1 - M Product. Also, just for future work, I've found it helps when your entities are names singular nouns but that's just my preference. Good luck.
Use group by will return by each shop
SELECT distinct s.*
from shops s
left join products p on p.shop_id = s.id
where p.category_id in (1,2,3)
group by s.id
having count(distinct p.category_id) = 3

Get categories which do not have any product from a particular category

I am trying to write a sql query to get the categories which does not have any of the products from particular category. Let say I have a,b,c,d,e categories and each category have some products. Now I need to get all the categories which done not include products of category a.
Categories table:
id name
1 A
2 B
3 C
4 D
5 E
category_products table:
product_id category_id
1 1
1 2
2 3
2 1
4 3
3 2
3 4
3 5
4 5
Query I used is below which gives B,C,D,E (not as expected)
SELECT DISTINCT c.name FROM category_products AS p
LEFT JOIN categories AS c ON c.id = p.category_id
WHERE p.product_id NOT IN (SELECT DISTINCT product_id FROM category_products where category_id = 1)
ORDER BY c.name
But I need results to be categories D,E which don't have any products from category A.
You need to do one more inner query, e.g.:
SELECT name
FROM categories
where id NOT IN (
SELECT DISTINCT category_id
FROM category_products WHERE
product_id IN (
SELECT product_id FROM category_products WHERE category_id = 1
)
);
This would return D and E.
Here's the SQL Fiddle.
You should use inner join
select distinct t2.name
from category_products t1
inner join Categories t2 on t2.id = t1.category_id
where t1.product_id not in
(select p.product_id
from category_products p
inner join Categories c on c.id = p.category_id
where c.name ='A')
I would be inclined to do this using group by and having:
select pc.product_id
from category_products pc join
categories c
on c.id = pc.category_id
group by pc.product_id
having sum(c.name = 'A') = 0;

MySQL One-To-Many SUM of COUNTs

After 2 days of searching and trying similar questions, it's got to the point where I need to ask the question!
I have the following database structure (simplified)..
mt_product | mt_sku | mt_price
========== | ====== | ========
id | brand_id | mpn | id | product_id | retailer_id | sku | id | sku_id | price | date
For instance...
* A can of Coca-Cola is ONE product.
* It can be sold in many different retailers, who will all have a SKU for it.
* This SKU will have a price, which can change day-by-day.
I want to list the total number of prices for the product.
To list this I currently have the following query which nearly works...
SELECT
p.id AS pid, p.title AS p_title, p.cat, p.mpn,
b.id AS bid, b.name AS brand,
(SELECT COUNT(s.id) FROM mt_sku AS s WHERE s.pid = p.id) AS num_sku,
(SELECT COUNT(gbp.id) FROM mt_price AS gbp INNER JOIN mt_sku ON mt_sku.id = gbp.sid ) AS num_price
FROM mt_product AS p
INNER JOIN mt_brand b ON p.bid = b.id
INNER JOIN mt_sku s ON p.id = s.pid
num_sku returns as expected, however when I introduce the second sub query for num_price (and I have revised this many times) I either get...
* no duplications of the pid but the num_price is the total number of prices to SKUs, not the amount of prices for this product_id (as query above) eg1_img
* the correct number of num_price, but instead of totalling up the total num_price, the pid is duplicated in the table (as query below) - therefore as the pid is duplicated, this does not give me the result I want. I added DISTINCT as it helped an earlier version of the query, it now makes no difference. eg2_img
SELECT
DISTINCT(p.id) AS pid, p.title AS p_title, p.cat, p.mpn,
b.id AS bid, b.name AS brand,
(SELECT COUNT(s.id) FROM mt_sku AS s WHERE s.pid = p.id) AS num_sku,
(SELECT COUNT(gbp.id) FROM mt_price AS gbp WHERE s.id = gbp.sid) AS num_price
FROM mt_product AS p
INNER JOIN mt_brand b ON p.bid = b.id
INNER JOIN mt_sku s ON p.id = s.pid
I'm pretty sure the key to this is that
product can have multiple SKUs, of which a SKU has multiple price history.
Any help or ideas of the schema would be superb.
Try this:
SELECT
p.id AS pid, p.title AS p_title, p.cat, p.mpn,
b.id AS bid, b.name AS brand,
COUNT(DISTINCT s.id) AS num_sku,
COUNT(gbp.id) AS num_price
FROM mt_product AS p
INNER JOIN mt_brand b ON p.brand_id = b.id
INNER JOIN mt_sku s ON p.id = s.product_id
INNER JOIN mt_price gbp ON s.id = gbp.sku_id
GROUP BY b.id, p.id
The products that don't have SKUs defined will not appear in the result set. Use LEFT JOIN mt_sku to make them appear in the result set (having 0 for num_sku and num_price):
LEFT JOIN mt_sku s ON p.id = s.product_id
In both variants of the query, the products that do not have prices defined will not appear in the result set. Use LEFT JOIN mt_price to include them into the result set (having 0 for num_price):
LEFT JOIN mt_price gbp ON s.id = gbp.sku_id
Take a look at the MySQL documentation for JOINs, GROUP BY and GROUP BY aggregate functions.
If you want to list the total prices then you need correlations.
Your first count is fine, because it is correlated to the outer query. The second has no correlation, so that seems strange. The following fixes the num_price subquery:
SELECT p.id AS pid, p.title AS p_title, p.cat, p.mpn,
b.id AS bid, b.name AS brand,
(SELECT COUNT(s2.id) FROM mt_sku s2 WHERE s2.pid = p.id) AS num_sku,
(SELECT COUNT(gbp.id) FROM mt_price gbp WHERE s.id = gbp.sid ) AS num_price
FROM mt_product p INNER JOIN
mt_brand b
ON p.bid = b.id INNER JOIN
mt_sku s
ON p.id = s.pid;
I'm also not sure why you have all the joins in the outer query. I assume that a given product is going to have multiple rows, and you want the multiple rows to have the same num_sku and num_price values.