mysql products in category select - mysql

I have a table named product_cats, the relevant columns are
product_id,
cat_id
Say we have categories 1, 2, 3, 4, and 5.
I need all the product_id's in cat_id 1 and 2 and (3 or 4 or 5).
I have a few working methods at this point, but they're all terribly slow.
Can someone shed some mysql magic on me?
Edit:
So the product needs to be in both category 1 and 2, and in either 3, 4, or 5.
Here's one of my attempts to get the idea better
SELECT
p1.product_id
FROM
product_cats p1 JOIN product_cats p2 ON ( p2.product_id = p1.product_id )
JOIN product_cats p3 ON ( p3.product_id = p1.product_id )
JOIN product_cats p4 ON ( p4.product_id = p1.product_id )
WHERE
(p1.category_id=? AND p2.category_id=? AND p3.category_id=?)
OR
(p1.category_id=? AND p2.category_id=? AND p4.category_id=?)

To select the products that are both in category 1 and 2 you can use a query like this:
select
product_id
from
product_cats
where
cat_id in (1,2)
group by
product_id
having
count(distinct cat_id)=2;
an index on both product_id and cat_id should help improve performances:
alter table product_cats add index idx_product_cats (product_id, cat_id);

Using Mysql you can implement your logic by using group by and sum function with having clause
select product_id
from product_cats
group by product_id
having sum(cat_id = 1) > 0
and sum(cat_id = 2) > 0
and sum(cat_id in(3,4,5)) > 0
Above query will return product_ids which has cat_id 1 and cat_id 2 and cat_id can be 3,4,5

Related

How to exclude row in one-to-many relationship between tables (SQL query)

I have two tables, Products and ProductTags (I also have table Tags which is not part of the problem)
My query is
SELECT
product.id
FROM
Products JOIN ProductTags ON Products.id = ProductTags.product_id
WHERE ProductTags.tag_id = 10 and ProductTags.tag_id <> 20
Table ProductTags is one-to-many connection between product_id and tag_id, if it's called like that. Let's say that whole ProductTags table is:
product_id
tag_id
777
10
777
20
888
10
888
30
what I get as an output from my query is 777 and 888. But I want to exclude products which have tag #20.
In real query I also join with other tables (also I need to have access to other fields of Products table), so I CAN'T get proper result using only ProductTags table! I am aware that I could do it like
SELECT product_id ProductTags WHERE tag_id = 10
but this WON'T be proper solution!
One way is to use aggregation.
SELECT product_id id
FROM producttags
WHERE tag_id IN (10,
20)
GROUP BY product_id
HAVING max(tag_id) = 10;
Another one uses NOT EXISTS and a correlated subquery.
SELECT pt1.product_id
FROM producttag pt1
WHERE pt1.tag_id = 10
AND NOT EXISTS (SELECT *
FROM producttag pt2
WHERE pt2.product_id = pt1.product_id
AND pt2.tag_id = 20);
Note that the join of product isn't needed to only get the product IDs, unless there's no proper foreign key constraint on producttags.product_id.
SELECT product.id
FROM Products
JOIN ProductTags ON Products.id = ProductTags.product_id
GROUP BY product.id
HAVING SUM(ProductTags.tag_id = 10) > 0 -- at least one
AND SUM(ProductTags.tag_id = 20) = 0 -- none
This form allows any amount of simple or complex conditions.
For example:
-- strictly one row with tag_id = 30
AND SUM(ProductTags.tag_id = 30) = 1
-- at least one tag_id 40 or 50
AND SUM(ProductTags.tag_id IN (40, 50)) > 0
-- ... and so on

Complex SQL Query Syntax

I want make a query to mysql database that gets me the products that are available, have sale_id of anything1 or anything2 or both and belong to categories of Ids a or b or both from table category_product which has (category_product table) 2 columns product_id and category_id since my products can belong to multiple categories.
HERE IS MY QUERY:
SELECT *
FROM products
WHERE available = 1
AND sale_id = 3
OR sale_id = 4
AND id IN (
SELECT product_id
FROM category_product
WHERE category_id = 2
OR category_id = 3
)
But It didn't work!!!
The Result was that it got the first query correct the one with available = 1 and sale_id = 3 or 4 or both.. But the products it got didn't belong to categories 2 or 3 or both
Any Help Please!!!
I think you just need parentheses or IN:
SELECT p.*
FROM products p
WHERE p.available = 1 AND
p.sale_id IN (3, 4) AND
p.id IN (SELECT product_id
FROM category_product cp
WHERE category_id IN (2, 3)
);
You seem to be learning SQL -- or at least boolean expressions. AND and OR don't automatically do what you want. When you are mixing them, use parentheses until you get the hang of it.
Your condition parses as:
WHERE (p.available = 1 AND p.sale_id = 3) OR
(p.sale_id = 4 AND
p.id IN (SELECT product_id
FROM category_product cp
WHERE category_id IN (2, 3)
)
)

MYSQL Query search in relationship

For the sake of clarity and this question i will rename the tables so it is a bit clearer for everybody and explain what i want to achieve:
There is an input form with options that return categories ID's. If a 'Product' has 'Category', i want to return/find the 'Product' which lets say has multiple categories(or just 1) and all of its categories are inside the array that is passed from the form.
Products table
ID Title
1 Pizza
2 Ice Cream
Categories table
ID Title
1 Baked food
2 Hot food
ProductsCategories table
ID ProductId CategoryId
1 1 1
2 1 2
So if i pass [1,2] the query should return Product with id 1 since all ProductsCategories are inside the requested array, but if i pass only 1 or 2, the query should return no results.
Currently i have the following query which works, but for some reason if i create a second Product and create a ProductCategory that has a CategoryId same as the first product, the query returns nulll...
SELECT products.*
FROM products
JOIN products_categories
ON products_categories.product_id= products.id
WHERE products_categories.category_id IN (1, 2)
HAVING COUNT(*) = (select count(*) from products_categories pc
WHERE pc .product_id = products.id)
All help is deeply appretiated! Cheers!
In order to match all values in IN clause, you just need to know in addition the number of passed categories which you must use it in HAVING clause:
SELECT
p.*,
GROUP_CONCAT(c.title) AS categories
FROM
Products p
INNER JOIN ProductsCategories pc ON pc.productId = p.ID
INNER JOIN Categories c ON c.ID = pc.categoryId
WHERE
pc.categoryId IN (1,2)
GROUP BY
p.id
HAVING
COUNT(DISTINCT pc.categoryId) = 2 -- this is # of unique categories in IN clause
So in case IN (1,2) result is:
+----+-------+---------------------+
| id | title | categories |
+----+-------+---------------------+
| 1 | Pizza | Baked Food,Hot Food |
+----+-------+---------------------+
1 row in set
In case IN (1,3) result is Empty set (no results).
#mitkosoft, thanks for your answer, but sadly the query is not producing the needed results. If the product's categories are partially in the passed categories the product is still returned. Additionally i might not know how many parameters are sent by the form.
Luckily I managed to create the query that does the trick and works perfectly fine (at least so far)
SELECT products.*,
COUNT(*) as resultsCount,
(SELECT COUNT(*) FROM products_categories pc WHERE pc.product_id = products.id) as categoriesCount
FROM products
JOIN products_categories AS productsCategories
ON productsCategories.product_id= products.id
WHERE productsCategories.category_id IN (7, 15, 8, 1, 50)
GROUP BY products.id
HAVING resultsCount = categoriesCount
ORDER BY amount DESC #optional
That way the query is flexible and gives me exactly what I needed! - Only those products that have all their categories inside the search parameters(not partially).
Cheers! :)

What is the wrong with this query?

I have 3 sql tables calls category, movies, category_movies. category and movies tables have many to many relationship. Thats why I use category_movies table. This is tables' structure...
Category : cat_id, cat_name,
movies : mov_id, mov_name,
category_movies : cat_id, mov_id
Now I have got 3 category IDs dynamically and now I want to select movies' names alone with category names which belongs to 3 category_id have already got.
This is the query that I tried so far..
SELECT c.cat_name AS cn, m.mov_name AS mn, m.mov_id
FROM category AS c
INNER JOIN category_movies AS cm ON cm.cat_id = c.cat_id
INNER JOIN movies AS m ON m.mov_id = cs.mov_id
WHERE c.cat_id IN (2, 5, 7)
GROUP BY c.cat_name, m.mov_name, m.mov_id
HAVING COUNT(*) >= 3
but this is now working.. can anybody tell me what is wrong with this query?
use IN clause on this
SELECT..
FROM..
WHERE cat_id IN (2, 5, 7)
and is the same with
SELECT..
FROM..
WHERE cat_id = 2 OR
cat_id = 5 OR
cat_id = 7
Please also take note that it is INNER JOIN not INNOR JOIN
but I guess, you want to perform RELATIONAL Division (you want to search for a movie that has all the category you want to find)
SELECT c.cat_name, m.mov_name, m.mov_id
FROM category AS c
INNER JOIN movies AS m ON m.cat_id = c.cat_id
INNER JOIN category_movies AS cm ON cm.mov_id = m.mov_id
WHERE cat_id IN (2, 5, 7)
GROUP BY c.cat_name, m.mov_name, m.mov_id
HAVING COUNT(*) >= 3
Relational Division
INNOR -> INNER
WHERE cat_id = 2 AND 5 AND 7
that hardly can be right it probably should be OR
1 code
SELECT Id, name, YEAR(BillingYar) AS Year
FROM Records
WHERE Year ≥ 2010
2 code
SELECT id, name
FROM students
WHERE grades = (SELECT MAX(grades)
FROM students
GROUP BY subject_id);
I can see only in first code wrong this symbol ( ≥ ), need to be >= . Something else?

How to select records who match criteria defined in a join table?

I have this three tables:
products TABLE:
id:integer
name:string
features TABLE:
id:integer
name:string
features_products TABLE:
product_id:integer
feature_id:integer
The features_products TABLE tells me which features have each product. For example:
product_id feature_id
1 3
1 5
3 4
tells me the product 1 has features 3 and 5 and product 3 have feature 4, also, product 2 (if exists) doesn't have features.
My question is, how can I SELECT all the products from the products TABLE wich have determinated features? For example, SELECT products which have features 3 AND 5, or SELECT products which have feature 2
To select all the product ids for products which have both features 3 and 5:
SELECT product_id
FROM features_products
WHERE feature_id IN (3, 5)
GROUP BY product_id
HAVING COUNT(*) = 2
This assumes that there is a uniqueness contraint on (product_id, feature_id). If you want the entire row from the product table then use this in a subquery:
SELECT *
FROM products
WHERE product_id IN (
SELECT product_id
FROM features_products
WHERE feature_id IN (3, 5)
GROUP BY product_id
HAVING COUNT(*) = 2
)
Something similar to this:
select * from product
where id in ( select product_id from feature_products where feature id in ( 1,3 ) )
swap out the (1,3) for the features you want to include.
You could do the following the join those tables and get the data you need:
SELECT *
FROM products p JOIN features_products fp ON p.id = fp.product_id
JOIN features f ON f.id = fp.feature_id
WHERE f.id = 3 OR f.id = 5