I saw some articles but I can't implement it on my code,
I Have 3 tables
Category
ID
name
1
winter
2
summer
Product
ID
name
1
bikini
2
scarf
Category_product
id_category
id_product
1
1
2
2
Basically like that, with much more products and more records on category_product as well, so what I want is to select 10 categories with their products but limited by 10 as well...
So I have something like
---Category 1
Product 1
Product 2
Product 3
---Category 2
Product 1
Product 2
Product 3
10 category max and 10 products max
I tried this but only get one product
SELECT c.* FROM category c
INNER JOIN category_product cp ON cp.id_category = c.id
INNER JOIN
(SELECT id_category, MAX(id_product) test
FROM category_product
GROUP BY id_category) cp2 ON cp.id_category = cp2.id_category
AND cp.id_product = cp2.test
so what I want is to select 10 categories with their products but limited by 10 as well
You can use row_number() and limit:
select c.*, cp.product_id
from (select c.*
from categories c
limit 10 -- you might want order by to get particular categories
) c join
(select cp.*,
row_number() over (partition by cp.category_id order by cp.product_id) as seqnum
from category_product cp
) cp
on c.category_id = c.id
where seqnum <= 10;
You can join in products if you want additional information about the products.
Related
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
I have to 3 tables: product, product_to_store, store
product table
id quantity status
1 1 1
2 0 1
3 0 1
4 23 1
product_to_store table
store_id product_id
1 1
2 2
1 3
2 4
store table
id name
1 store1
2 store2
To find total products I can run query to fetch all products in table product where status of product is enabled.
select count(*) from product where status=1
total name
2 Store 1
2 store 2
To find total out of stock products I can run below query after joining all 3 tables and using group by store_id:
Select count(*) as outofproducts from product where quantity=0;
Result come like this:
outofproducts name
1 Store 1
1 store 2
But I want combination of above 2 results in single query like below:
outofproducts total name
1 2 Store 1
1 2 store 2
You'd use conditional aggregatiopn, i.e. sum/count over conditions:
select
s.name,
sum(p.quantity > 0) as in_stock,
sum(p.quantity = 0) as out_of_stock,
count(*) as total
from store s
join product_to_store ps on ps.store_id = s.id
join product p on p.id = ps.product_id
group by s.name
order by s.name;
This makes use of MySQL's true = 1, false = 0. If you don't like it, replace sum(p.quantity = 0) with sum(case when p.quantity = 0 then 1 else 0 end) or count(case when p.quantity = 0 then 1 end).
You can start query from store table so that we will get total rows as store table data.
Then use nested query for each store to get out of product and total product count
select
(Select count(*) as outofproducts from product_to_store ps inner join product p on p.id = ps.product_id where quantity=0 and ps.store_id = s.id ) as outofproducts ,
(Select count(*) as count from product_to_store ps inner join product p on p.id = ps.product_id where ps.store_id = s.id ) as totalCount,
s.name
from store s
You could join the related subquery for count
select t1.name, t1.outofproducts, t2.total
from(
select b.id, b.name , count(*) outofproducts
from product_to_store c
inner join product a on a.id = c.product_id
inner join store b on a.id = c.store_id
where a.quantity = 0
group by b.id, b.name
) t1
inner join (
select b.id, b.name , count(*) total
from product_to_store c
inner join product a on a.id = c.product_id
inner join store b on a.id = c.store_id
group by b.id, b.name
) t2 on t1.id = t2.id
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;
I have two tables one containg offer information and the other containg products
something like this:
OFFER PRODUCTS
ID Number Version poID offer_id Product how_many
========================== ========================================
1 123 1 1 1 Apple 1
2 123 2 2 1 Banana 2
3 124 1 3 1 Orange 1
4 2 Apple 1
5 2 Banana 2
6 2 Orange 2
7 2 Kiwi 1
8 3 Apple 2
9 3 Banana 3
I would like a list of how many products that are currently offered.
Since OFFER(id = 2) is an update of (id = 1) only (id = 2) should be counted.
How should I best query this?
First you need to get all the latests offers:
select o.id
from offer o
where version = (select max(version)
from offer o2
where o2.number = o.number);
Based on the above you can then get all the products:
select p.*
from products p
where offer_id in (select o.id
from offer o
where version = (select max(version)
from offer o2
where o2.number = o.number));
If id and version correlate:
select sum(how_many) from products p
join offer on p.offer_id=offer.id
join (
select number, max(version) version from offer group by number
) x
on offer.id=x.id and offer.version = x.version
SELECT *
FROM products
WHERE offer_id = (SELECT MAX(id) FROM offer)
or, if you prefer the join syntax
SELECT p.*
FROM products p
INNER JOIN (SELECT MAX(id) id FROM offer) o ON p.offer_id = o.id
Edit (still not completely sure this is what you want without seeing your desired results)
SELECT p.*
FROM products p
INNER JOIN offer o on p.offer_id = o.id
INNER JOIN
(SELECT number, max(version)
FROM offer
GROUP BY number
) oMax ON o.number = oMax.number AND o.version = oMax.version
Try this:
select [list columns here]
from products p
join (select offernumber, max(id) as ID from offer group by offernumber) a
on a.id = p.offer_id
If you need addtional columns from offer other than the offernumber and the id:
select [list columns here]
from products p
join (select offernumber, max(id) as ID from offer group by offernumber) a
on a.id = p.offer_id
join offer o on o.id = a.id
I have two tables:
parent-child 'categories':
id name parent_id
1 Food NULL
2 Pizza 1
3 Pasta 2
'transactions':
id amount category_id
1 100 1
2 50 2
3 25 2
I want to return all the Categories along with two total columns:
total = The sum of the amount for all transactions with this category_id
parentTotal = total + the total of all its child categories
Example (using the tables above):
id name parent_id total parentTotal
1 Food NULL 100 175
2 Pizza 1 0 0
3 Pasta 2 75 0
EDIT:
Code updated (based on code from Nedret Recep below) and works fine...
SELECT
tmp1.id, tmp1.name, tmp1.parent_id, tmp1.total, IFNULL(tmp1.total, 0) + IFNULL(tmp2.s, 0) AS parenttotal
FROM
(SELECT
ca.id, ca.name, ca.parent_id, SUM(tr.amount) as total
FROM
categories ca
LEFT JOIN
transactions tr
ON
tr.category_id = ca.id
GROUP BY
ca.id)
AS tmp1
LEFT JOIN
(SELECT
c.id, c.parent_id as categoryid, SUM(t.amount) AS s
FROM
transactions t
RIGHT JOIN
categories c
ON
t.category_id = c.id
GROUP BY
c.parent_id)
AS tmp2
ON tmp2.categoryid = tmp1.id
order by coalesce(tmp1.parent_id, tmp1.id), tmp1.parent_id
I'd really appreciate some help - thanks!
With one inner join we calculate the totals in category which is standard. Then with another inner join we calculate the sums but this time grouping by parent_id. Then we join the two result tables to have both sums in one row. This query will be slow with large tables so an alternative approach on application level would do better.
SELECT
tmp1.id, tmp1.name, tmp1.parent_id, tmp1.total, tmp1.total + tmp2.s AS parenttotal
FROM
(SELECT
ca.id, ca.name, ca.parent_id, SUM(tr.amount) as total
FROM
transactions tr
INNER JOIN
categories ca
ON
tr.categoru_id = ca.id
GROUP BY
ca.id)AS tmp1
LEFT OUTER JOIN
(
SELECT
c.parent_id as categoryid, SUM(t.amount) AS s
FROM
transactions t
INNER JOIN
categories c
ON
t.category_id = c.i
GROUP
BY c.id ) AS tmp2
ON
tmp2.categoryid = tmp.id