I'm quite a begginer and I have two tables: "product" and "product attributes".
Here's some imaginary data (the actual stuff involves more tables )
Products Table:
product_id | product_name
10 | aaa
11 | bbb
12 | ccc
Product Attributes Table:
attribute_id | product_id
21 | 10
23 | 10
24 | 10
21 | 11
24 | 11
21 | 12
25 | 12
Where each product has more than one possible attribute. I have a list of attribute ids like (21,10,25) and I need to select all products whose attributes are a subset of that list.
Is it possible to do this in one query?
When I filter for (21,24) desired output is to return only product 11 (bbb)
When I filter for (21,23,24) desired output is to return products 10 and 11.
When I filter for (21) desired output is to return none (because all products have at least one other attribute).
If you pretend that your filter is in a table:
select *
from product p
where not exists (
select 1
from attributes a
where a.product_id = p.product_id
and not exists(
select 1
from filter f
where f.id_attribute = a.id_attribute))
If it was in a constructed query:
select *
from product p
where not exists (
select 1
from attributes a
where a.product_id = p.product_id
and attribute_id not in (<list>))
This is off the top of my head, so may have typos.
Assuming your product table is called Product and the ID column in that table is just called Id:
SELECT * from Product p where p.Id IN
(Select id_product from ProductAttributes where id_attribute in (21, 23, 24))
This should return only those id's where all attributes for each id are completely contained within the list:
select attribute_match.id_product from
(select id_product, count(*) c from attributes
where id_attribute in (21, 10, 25)
group by id_product) attribute_match,
(select id_product, count(*) c_count from attributes
group by id_product) attribute_total
where attribute_match.id_product = attribute_total.id_product
and attribute_match.c = attribute_total.c
select
P.id,
P.name,
count(P.id) as matched_attr_count,
count(PA.a_id) as total_attr_count
from
product_attributes PA
left join product P on P.id = PA.p_id and PA.a_id in (21,23,24)
group by
PA.p_id
having
matched_attr_count = total_attr_count;
Until MySQL supports the EXCEPT query combination,
SELECT product_id
FROM attributes
WHERE product_id NOT IN (
SELECT product_id
FROM attributes
WHERE attribute_id NOT IN (21, 23, 24)
)
GROUP BY product_id
UNION
SELECT id
FROM products AS p
LEFT JOIN attributes AS a
ON p.id = a.product_id
WHERE a.product_id IS NULL
If you wish to have only the products with all the given attributes, add a HAVING COUNT(*)=n clause to the first outer query, where 'n' is the length of the attribute list.
Based on your insight guys, I optimized it even further and used only 1 COUNT statement like this:
SELECT * ,COUNT(p.product_id) AS c FROM product_attribute pa
LEFT JOIN products p ON pa.product_id = p.product_id AND pa.attribute_id NOT IN ($filter_list)
GROUP BY pa.product_id
HAVING c=0
Does it work ? :)
Edit:
That code doesn't return the product's name or other fields it might have. This is the correct one:
SELECT * ,COUNT(pa.product_id ) AS c FROM products p
LEFT JOIN product_attribute pa ON pa.product_id = p.product_id AND pa.attribute_id NOT IN ($filter)
GROUP BY p.product_id
HAVING c=0
let me post simple imaginary data ( the actual stuff involves more tables )
table products
product_id | product_name
10 | aaa
11 | bbb
table product_attribute
attribute_id | product_id <br>
21 | 10
23 | 10
24 | 10
21 | 11
24 | 11
i want that:
when I filter for (21,24) to be returned only product 11 (bbb)
when I filter for (21,23,24) to be returned both products
when I filter for (21) only to be returned none ( because no product has only that attribute )
Related
I have 2 tables
-- Purchases --
Id | IdProvider | Date | Observations
and
-- PurchasesDetails --
Id | IdProduct | Quantity | Price
where Purchases.Id = PurchasesDetails.Id
I want to make a SQL query where it returns all the purchases between a range of price (The price info is on PurchasesDetails table -> Quantity*Price)
For example: Get all the purchases that costed more than 0$ but less than 500$
I've tried this:
SELECT * FROM Purchases INNER JOIN PurchasesDetails ON Purchases.Id = PurchasesDetails.Id WHERE Purchases .Id sum(PurchasesDetails.Price*PurchasesDetails.Quantity) BETWEEN 0 AND 500
But it doesn't work. Seems like I'm missing a condition to link up all the PurchasesDetails with the same Id
I think it's a really easy task but I'm stuck there for few hours so all the help are welcome!!
Is this what you want?
SELECT p.*,
SUM(pd.Price * pd.Quantity) as purchase_total
FROM Purchases p INNER JOIN
PurchasesDetails pd
ON p.Id = pd.Id
GROUP BY p.Id
HAVING purchase_total BETWEEN 0 AND 500;
Note that SELECT p.* is fine with GROUP BY p.id, assuming that id is unique in Purchases.
Im having a bit of difficulty with one equation I can't seem to figure out or find on here. I am trying to do the following;
I have 2 tables, products and teamproducts. There foreign key is productid. I am trying to select the products from the products table that have not been registered to the teamproducts table. I am also using a teamcode that in teamproducts. An example below;
products Table (productid | productname)
1001 | product 1
1002 | product 2
1003 | product 3
1004 | product 4
1005 | product 5
etc
teamproducts Table (teamcode | productid)
teamcode1 | 1001
teamcode1 | 1002
What I want to do is select all the products that aren't in the teamproducts page (so in this example product 3 and on)
I have tried the following;
SELECT productname FROM products p, teamproducts tp WHERE teamcode = teamcode1 AND p.productid != tp.productid
and other variations I have seen but have not come up with the right line. Any help please.
Untested, but I think this should do the job.
SELECT products.productname
FROM products
LEFT JOIN teamproducts
ON teamproducts.teamcode = 'teamcode1'
AND teamproducts.productid = products.productid
WHERE teamproducts.productid IS NULL;
I think you have to write teamcode1 between quotes.
Something like this:
SELECT productname FROM products p, teamproducts tp WHERE teamcode = 'teamcode1' AND p.productid != tp.productid
You can use a not in clause
select * from products
where id not in (select productid from teamproduct);
What you are looking for is LEFT JOIN, with an extra condition that eliminates the null results from the right table:
SELECT * FROM products
LEFT JOIN teamproducts USING (productid)
WHERE teamproducts.productid IS NULL; -- Add other conditions as needed
EDIT Adding 'teamcode' condition:
SELECT * FROM products
LEFT JOIN teamproducts USING (productid)
WHERE teamproducts.productid IS NULL
AND teamproducts.teamcode = 'teamcode1'; -- Add other conditions as needed
More about JOIN, and different types of it: http://dev.mysql.com/doc/refman/5.7/en/join.html
I'm trying to get something done, basically it comes down to this:
I want to retrieve all products and show all categories this product is in. But then, I want to filter out only the products which exists in categories x and y.
So, this is my query:
SELECT p.id, p.name,GROUP_CONCAT(distinct(pc.category_id) SEPARATOR ", ") as category
FROM products p
LEFT JOIN product_category pc ON p.id = pc.productid
GROUP BY p.id;
This works great, I get result like this:
p.id | p.name | category
10 | example| 15,16,17
11 | example| 15,20
12 | example| 39,40
Obviously the '15,16,16' are the categories the product is in. However, now I want to filter the resultset on products only containing category 15 or 16. So the resultset I want to get is:
p.id | p.name | category
10 | example| 15,16,17
11 | example| 15,20
So, what I tried is adding a WHERE to my MySQL statement like this:
WHERE category IN (15,16)
This works as for the filtering, but the problem is, in the resultset I don`t see which other categories the product is also in. So the result I see is:
p.id | p.name | category
10 | example| 15,16
11 | example| 15
Note the difference with the desired result is I just see the filtered cats and not all the cats.
I do get why this is behaving as it is, since obviously the 'category' column in my resultset is based on the values after filtering. However, I don`t know how to work around this or if what I want is even possible.
PS: this query will run on huge databases so the faster the query, the better.
One (admittedly weird) option is to use find_in_set on the aggregate result:
SELECT p.id,
p.name,
GROUP_CONCAT(DISTINCT(pc.category_id) SEPARATOR ", ") AS category
FROM products p
LEFT JOIN product_category pc ON p.id = pc.productid
GROUP BY p.id
HAVING FIND_IN_SET('15', GROUP_CONCAT (pc.category_id)) > 0 OR
FIND_IN_SET('16', GROUP_CONCAT (pc.category_id)) > 0
Try this untested query:
select * p.id, p.name,GROUP_CONCAT(distinct(pc.category_id) SEPARATOR ", ") as category
from FROM products p
LEFT JOIN product_category pc ON p.id = pc.productid
where p.id in (
SELECT pc.productid
product_category pc ON p.id = pc.productid
where category IN (15,16)
GROUP BY pc.productid
)
GROUP BY p.id
I was just wondering how would I resolved this task:
SET #needle = '15,16';
SELECT p.id, p.name, GROUP_CONCAT(pc.category_id SEPARATOR ', ') AS 'category' FROM products p
LEFT JOIN product_category pc ON p.id = pc.productid GROUP BY id
HAVING REGEXP_INSTR(GROUP_CONCAT(pc.category_id), CONCAT('([[:<:]])(',REPLACE(#needle,',','|'),')([[:>:]])'));
The groups before passing the HAVING clause:
p.id | p.name | category
10 | example| 15,16,17
11 | example| 15,20
12 | example| 39,40
Then we check the groups with the HAVING clause and REGEXP:
HAVING REGEXP_INSTR(t3.category, CONCAT('[[:<:]](15|16)[[:>:]]'))
/* REGEX: START_WORD_BOUNDARY(15 OR 16)END_WORD_BOUNDARY */
And here it is:
p.id | p.name | category
10 | example| 15,16,17
11 | example| 15,20
I have two tables, the first is the products table, and it has 2 columns: id and product_name.
The second table is the filters table, and it has 2 columns: filter_id, product_id.
For example i have this in the products table:
id | product_name
1 | test product
and this in the filters table:
filter_id | product_id
1 | 1
2 | 1
3 | 1
As you can see, the product with id '1' has 3 filters.
My goal is to get the products by filters.
For example, i need every product where the product has the 2 and 3 filter_id. I tried to use something like that:
SELECT * FROM products p LEFT JOIN filters f ON (p.id = f.product_id) WHERE (filter_id = '2' AND filter_id = '3')
I can't do that because the filter_id can't be equal to 2 and 3 at the same time.
The main problem is that i should use only one query to get the products.
Group by the product and select only those having both filter_ids
SELECT p.id, p.product_name
FROM products p
JOIN filters f ON p.id = f.product_id
WHERE f.filter_id in (2,3)
group by p.id, p.product_name
having count(distinct f.filter_id) = 2
I would like to run a query on two table "products" and "category", There are 30 record in my product table. product table has a column name category_ids varchar(255),which storing the ids of category in the format like(10,11,12,130,..) for each record of products table. In sort a product can be many categories. category table having a column name parent_id which is the parent category of that category.
I want to list the record with all category of that product.
For example look at one record of the products table having id = 7.
product_Id = 7,
category_ids = '213,215,216',
product_name = 'Ponds',
.....
Means product ponds has three category = category.id = 213, category.id = 215 and category.id = 216.
I want to list here all three records of ponds like in this format :=
product_Id | product_name | category_name | parent_category_name
7 ponds cream chemical
7 ponds medicine chemical
7 ponds powder Ayurvedic
I am trying with this query :-
select
p.id as product_id,
p.product_name,
child.name as category_name,
parent.name as parent_category_name
from category child
left join products p on child.id in(p.category_ids)
left join category parent on parent.id = child.parentid and parent.parentid = 0
where p.id = 7
The above query getting only one record not all three records as above.
What condition and joining in this query will be applied to get result as above described.
Sorry for spending your valuable time.
Any suggestions and ideas would be greatly appreciated.
Thanks a lot.
Try to change ON condition -
LEFT JOIN products p ON child.id IN(p.category_ids)
->
LEFT JOIN products p ON FIND_IN_SET(child.id, p.category_ids)
...because:
SELECT 1 IN ('1,2,3') find_1, 2 IN ('1,2,3') find_2;
+--------+--------+
| find_1 | find_2 |
+--------+--------+
| 1 | 0 | -- 0 !
+--------+--------+
SELECT FIND_IN_SET(1, '1,2,3') find_1, FIND_IN_SET(2, '1,2,3') find_2;
+--------+--------+
| find_1 | find_2 |
+--------+--------+
| 1 | 2 |
+--------+--------+
GROUP_CONCAT function.
example:
SELECT p.product_Id, p.product_name, GROUP_CONCAT(c.category) FROM products p
JOIN category c
ON FIND_IN_SET(child.id, p.product_Id)
GROUP BY p.product_Id;