SQL Query - put multiple condition on one product - mysql

Please help me with this:
I have a table like:
id_feature id_product id_feature_value
1 1 50
2 1 54
5 1 67
And I want to select from this table like this:
select count(id_product) from table where (id_feature = 1 AND id_feature_value = 50) AND (id_feature = 2 AND id_feature_value = 54) AND (id_feature = 5 AND id_feature_value = 67)
my query must meet the conditions. Like having count(condition) = 3
I don't know how to write this!
Please help me! and sorry my english!
SELECT count( pf.id_product ) AS nr_product, value, fv.id_feature_value, filter
FROM `nk_category_features` cat_f
INNER JOIN `nk_feature_value` fv ON fv.id_feature = '11'
AND fv.value IS NOT NULL
INNER JOIN `nk_product_features` pf ON pf.id_feature = '11'
AND pf.id_feature_value = fv.id_feature_value
INNER JOIN `nk_product` p ON p.id_product = pf.id_product
AND p.product_active = '1'
INNER JOIN `nk_product_features` pf1 ON ( pf1.id_feature = '14'
AND (
pf1.id_feature_value = '21'
) )
WHERE cat_f.id_feature = '11'
AND filter >0
GROUP BY pf.id_feature, pf.id_product
ORDER BY abs( fv.value ) ASC
this is my query that i used now, but i don't like the solution width inner join, inner join on the same table

try this:
select id_product,count(*)
from table
where (id_feature = 1 AND id_feature_value = 50)
OR (id_feature = 2 AND id_feature_value = 54)
OR (id_feature = 5 AND id_feature_value = 67)
group by id_product
having count(*) = 3
To get just the total of products meeting the where clause
select count(*)
from
( select id_product
from table
where (id_feature = 1 AND id_feature_value = 50)
OR (id_feature = 2 AND id_feature_value = 54)
OR (id_feature = 5 AND id_feature_value = 67)
group by id_product
having count(*) = 3
) tmp

Related

Slow Mysql Inner joins with multiple OR

I'm helping a friend with an e-commerce site. He has options for users to select different colours, styles, use and type of the products he's selling. The query the adds the following to the query:
INNER JOIN tbl_coloursProducts col ON ( p.product_id = col.productID AND (col.colourID = 2 OR col.colourID = 3 OR col.colourID = 5 OR col.colourID = 8 OR col.colourID = 10))
INNER JOIN tbl_useProducts tbluse ON ( p.product_id = tbluse.productID AND (tbluse.useID = 15 OR tbluse.useID = 16 OR tbluse.useID = 17 OR tbluse.useID = 18))
INNER JOIN tbl_styleProducts style ON ( p.product_id = style.productID AND (style.styleID = 39 OR style.styleID = 44))
INNER JOIN tbl_typeProducts type ON ( p.product_id = type.productID AND (type.typeID = 46 OR type.typeID = 48 OR type.typeID = 50))
The query loads fast enough when only a few options are selecting, but some users are selecting multiple or each which is causing the query to run in excess of 30 seconds and timing out.
Without altering the table structure is there a better way to optimise the query?
This is the full query:
SELECT *,
p.product_id,
Coalesce((SELECT p2sp.price
FROM ab_product_specials p2sp
WHERE p2sp.product_id = p.product_id
AND p2sp.customer_group_id = '1'
AND ( ( p2sp.date_start = '0000-00-00'
OR p2sp.date_start < Now() )
AND ( p2sp.date_end = '0000-00-00'
OR p2sp.date_end > Now() ) )
ORDER BY p2sp.priority ASC,
p2sp.price ASC
LIMIT 1), p.price) AS final_price,
pd.name AS name,
m.name AS manufacturer,
ss.name AS stock,
(SELECT Avg(r.rating)
FROM ab_reviews r
WHERE p.product_id = r.product_id
GROUP BY r.product_id) AS rating,
(SELECT Count(rw.review_id)
FROM ab_reviews rw
WHERE p.product_id = rw.product_id
GROUP BY rw.product_id) AS review
FROM ab_products p
LEFT JOIN ab_product_descriptions pd
ON ( p.product_id = pd.product_id
AND pd.language_id = '1' )
LEFT JOIN ab_products_to_stores p2s
ON ( p.product_id = p2s.product_id )
LEFT JOIN ab_manufacturers m
ON ( p.manufacturer_id = m.manufacturer_id )
LEFT JOIN ab_stock_statuses ss
ON ( p.stock_status_id = ss.stock_status_id
AND ss.language_id = '1' )
LEFT JOIN ab_products_to_categories p2c
ON ( p.product_id = p2c.product_id )
INNER JOIN tbl_coloursproducts col
ON ( p.product_id = col.productid
AND ( col.colourid = 2
OR col.colourid = 3
OR col.colourid = 5
OR col.colourid = 8
OR col.colourid = 10 ) )
INNER JOIN tbl_useproducts tbluse
ON ( p.product_id = tbluse.productid
AND ( tbluse.useid = 15
OR tbluse.useid = 16
OR tbluse.useid = 17
OR tbluse.useid = 18 ) )
INNER JOIN tbl_styleproducts style
ON ( p.product_id = style.productid
AND ( style.styleid = 39
OR style.styleid = 44 ) )
INNER JOIN tbl_typeproducts type
ON ( p.product_id = type.productid
AND ( type.typeid = 46
OR type.typeid = 48
OR type.typeid = 50 ) )
WHERE p.status = '1'
AND p.date_available <= Now()
AND p2s.store_id = 0
AND p2c.category_id = 131
GROUP BY p.product_id
ORDER BY p.product_id DESC
LIMIT 0, 8
Without the custom bits the query runs fine.
Looking at that query, not sure the ORs are the problem themselves (although you could possibly make the code more compact by using and IN clause for each one). Rather I suspect that selecting more and more options results in more rows being returned. And this is causing problems with the sub queries in the SELECT clause.
Can you try the query with the sub queries removed from the SELECT clause and see the effect that has.
You can remove the sub queries quite easily.
SELECT *,
p.product_id,
Coalesce(sub1.price, p.price) AS final_price,
pd.name AS name,
m.name AS manufacturer,
ss.name AS stock,
sub0.rating,
sub0.review
FROM ab_products p
INNER JOIN
(
SELECT r.product_id,
Avg(r.rating) AS rating,
Count(rw.review_id) AS review
FROM ab_reviews r
GROUP BY r.product_id
) sub0
ON p.product_id = sub0.product_id
LEFT OUTER JOIN
(
SELECT p2sp.product_id,
SUBSTRING_INDEX(GROUP_CONCAT(p2sp.price ORDER BY p2sp.priority ASC, p2sp.price ASC ), ',', 1) AS price
FROM ab_product_specials p2sp
WHERE p2sp.customer_group_id = '1'
AND ( p2sp.date_start = '0000-00-00' OR p2sp.date_start < NOW() )
AND ( p2sp.date_end = '0000-00-00' OR p2sp.date_end > NOW() )
GROUP BY p2sp.product_id
) sub1
ON p.product_id = sub1.product_id
LEFT JOIN ab_product_descriptions pd
ON ( p.product_id = pd.product_id
AND pd.language_id = '1' )
LEFT JOIN ab_products_to_stores p2s
ON ( p.product_id = p2s.product_id )
LEFT JOIN ab_manufacturers m
ON ( p.manufacturer_id = m.manufacturer_id )
LEFT JOIN ab_stock_statuses ss
ON ( p.stock_status_id = ss.stock_status_id
AND ss.language_id = '1' )
LEFT JOIN ab_products_to_categories p2c
ON ( p.product_id = p2c.product_id )
INNER JOIN tbl_coloursproducts col
ON ( p.product_id = col.productid
AND ( col.colourid = 2
OR col.colourid = 3
OR col.colourid = 5
OR col.colourid = 8
OR col.colourid = 10 ) )
INNER JOIN tbl_useproducts tbluse
ON ( p.product_id = tbluse.productid
AND ( tbluse.useid = 15
OR tbluse.useid = 16
OR tbluse.useid = 17
OR tbluse.useid = 18 ) )
INNER JOIN tbl_styleproducts style
ON ( p.product_id = style.productid
AND ( style.styleid = 39
OR style.styleid = 44 ) )
INNER JOIN tbl_typeproducts type
ON ( p.product_id = type.productid
AND ( type.typeid = 46
OR type.typeid = 48
OR type.typeid = 50 ) )
WHERE p.status = '1'
AND p.date_available <= Now()
AND p2s.store_id = 0
AND p2c.category_id = 131
GROUP BY p.product_id
ORDER BY p.product_id DESC
LIMIT 0, 8
As an aside, when you read from ab_product_specials you are checking for the date_start and date_end to be 0000-00-00 (ie, dates), but also comparing them with NOW() which returns a date / time field. Are those fields date or date / time fields?
My first though was to use IN to make the query eaier to read:
INNER JOIN tbl_coloursProducts col
ON p.product_id = col.productID AND col.colourID IN ( 2, 3, 5, 8, 10 )
Then I thought, I wonder if they are dynamically building SQL text to squirt into the database logic?! The optimizer is unlikely to do well at optimizing queries when they are constantly mutating in this way.
Consider a scratch table (pseudo code):
-- One time:
CREATE TABLE SratchColours ( colourID INT NOT NULL UNQIUE );
-- For each query:
DELETE FROM SratchColours;
INSERT INTO SratchColours VALUES ( 2 ), ( 3 ), ( 5 ), ( 8 ), ( 10 );
Now you dynamic list of values simply becomes just another join:
tbl_coloursProducts NATURAL JOIN SratchColours
(or you could use an inner join if you must!)
Now, having one base table for every concurrent user is probably not a great way to scale a system. Therefore, consider how to pass a bag of colourID values to the database logic (say, a stored proc), put them into a table (say, a temporary table), then join from there to your base tables.

using joins together with aggregates, and retrieving rows when no aggregate exists

The following query on my MySQL tables returns rows from the purchaseorder table that have corresponding entries in the deliveryorder table. How do I construct this query so that I get rows from the purchaseorder table even if no corresponding rows exist in the deliveryorder table? If the users want to see sql table CREATE statements, I can post those, but I'm not posting now as it really makes the question too big.
SELECT
`purchaseorder`.`id` AS `po_id`,
`purchaseorder`.`order_quantity` AS `po_order_quantity`,
`purchaseorder`.`applicable_approved_unit_rate` AS `po_unit_rate`,
`purchaseorder`.`applicable_sales_tax_rate` AS `po_tax_rate`,
`purchaseorder`.`order_date` AS `po_order_date`,
`purchaseorder`.`remarks` AS `po_remarks`,
`purchaseorder`.`is_open` AS `po_is_open`,
`purchaseorder`.`is_active` AS `po_is_active`,
`purchaseorder`.`approved_rate_id` AS `po_app_rate_id`,
`supplier`.`name` AS `sup_name`,
SUM(`deliveryorder`.`quantity`) AS `total_ordered`
FROM `purchaseorder`
LEFT JOIN `deliveryorder` ON (`deliveryorder`.`purchase_order_id` = `purchaseorder`.`id`)
INNER JOIN `approvedrate` ON (`purchaseorder`.`approved_rate_id` = `approvedrate`.`id`)
INNER JOIN `supplier` ON (`approvedrate`.`supplier_id` = `supplier`.`id`)
WHERE (
`purchaseorder`.`is_active` = 1
AND `purchaseorder`.`is_open` = 1
AND `deliveryorder`.`is_active` = 1
AND `approvedrate`.`material_id` = 2
)
HAVING `purchaseorder`.`order_quantity` >= `total_ordered` + 1
You have an aggregating function but no GROUP BY clause, which is wierd, but anyway - something like this? Oops - edited...
SELECT po.id po_id
, po.order_quantity po_order_quantity
, po.applicable_approved_unit_rate po_unit_rate
, po.applicable_sales_tax_rate po_tax_rate
, po.order_date po_order_date
, po.remarks po_remarks
, po.is_open po_is_open
, po.is_active po_is_active
, po.approved_rate_id po_app_rate_id
, s.name sup_name
, SUM(do.quantity) total_ordered
FROM purchaseorder po
LEFT
JOIN deliveryorder do
ON do.purchase_order_id = po.
AND do.is_active = 1
LEFT
JOIN approvedrate ar
ON ar.id = po.approved_rate_id
AND ar.material_id = 2
LEFT
JOIN supplier s
ON s.id = ar.supplier_id
WHERE po.is_active = 1
AND po.is_open = 1
HAVING po.order_quantity >= total_ordered + 1
I couldn't work out how to get the desired results all in one query, but ended up using the following two queries to fulfill my requirements: -
1st query
SELECT
pot.`id` AS `po_id`,
pot.`order_quantity` AS `po_order_quantity`,
pot.`applicable_approved_unit_rate` AS `po_unit_rate`,
pot.`applicable_sales_tax_rate` AS `po_tax_rate`,
pot.`is_open` AS `po_is_open`,
pot.`is_active` AS `po_is_active`,
st.`id` AS `sup_id`,
st.`name` AS `sup_name`,
SUM(dot.`quantity`) AS `total_ordered`
FROM `purchaseorder` pot
INNER JOIN `deliveryorder` dot ON (dot.`purchase_order_id` = pot.`id`)
INNER JOIN `approvedrate` art ON (pot.`approved_rate_id` = art.`id`)
INNER JOIN `supplier` st ON (art.`supplier_id` = st.`id`)
WHERE (
pot.`is_active` = 1
AND pot.`is_open` = 1
AND art.`material_id` = #materialid
AND art.`in_effect` = 1
AND art.`is_active` = 1
AND dot.`is_active` = 1
AND st.`is_active` = 1
)
HAVING pot.`order_quantity` >= `total_ordered` + #materialquantity
2nd query
SELECT
pot.`id` AS `po_id`,
pot.`order_quantity` AS `po_order_quantity`,
pot.`applicable_approved_unit_rate` AS `po_unit_rate`,
pot.`applicable_sales_tax_rate` AS `po_tax_rate`,
pot.`is_open` AS `po_is_open`,
pot.`is_active` AS `po_is_active`,
st.`id` AS `sup_id`,
st.`name` AS `sup_name`,
0 AS `total_ordered`
FROM `purchaseorder` pot
INNER JOIN `approvedrate` art ON (pot.`approved_rate_id` = art.`id`)
INNER JOIN `supplier` st ON (art.`supplier_id` = st.`id`)
WHERE (
pot.`is_active` = 1
AND pot.`is_open` = 1
AND art.`material_id` = #materialid
AND art.`in_effect` = 1
AND art.`is_active` = 1
AND st.`is_active` = 1
AND pot.`order_quantity` >= #materialquantity
AND pot.`id` NOT IN
(
SELECT dot.`purchase_order_id`
FROM `deliveryorder` dot
WHERE dot.is_active = 1
)
)

MySQL query returning 0 rows while it should return one

Something is wrong with my MySQL query below but I can't find the problem. It's not returning any errors but the query below should return 1 row, but it returns none.
The table 'fws_product' contains all products. The table 'webits_product_has_kenmerken' contains the product specifications.
SELECT fws_product.*
FROM webits_product_has_kenmerken
LEFT JOIN fws_product ON webits_product_has_kenmerken.product_id = fws_product.ID
WHERE fws_product.CATID = 11
AND (
(webits_product_has_kenmerken.kenmerk_id = 8 AND webits_product_has_kenmerken.kenmerk_value = 'Buddha to Buddha')
AND
(webits_product_has_kenmerken.kenmerk_id = 19 AND webits_product_has_kenmerken.kenmerk_value = '10 mm')
)
Thanks in advance!
It looks a bit nasty, but the following should do as you have requested
SELECT
p.*
FROM fws_product AS p
INNER JOIN webits_product_has_kenmerken AS ps8
ON ps8.product_id = p.ID
AND ps8.kenmerk_id = 8
AND ps8.kenmark_value = 'Buddha to Buddha'
INNER JOIN webits_product_has_kenmerken AS ps19
ON ps19.product_id = p.ID
AND ps19.kenmerk_id = 19
AND ps19.kenmark_value = '10 mm'
WHERE p.CATID = 11
This is another potential option which may do the job, but still feels very nasty
SELECT
p.*
FROM fws_product AS p
INNER JOIN (
SELECT
product_id,
COUNT(*) AS numMatches
FROM webits_product_has_kenmerken
WHERE (kenmerk_id,kenmerk_value) IN (
(8,'Buddha to Buddha'),
(19,'10 mm')
)
GROUP BY product_id
HAVING numMatches = 2
) AS ps
ON ps.product_id = p.ID
WHERE p.CATID = 11
i think you need the following:
SELECT fws_product.*
FROM webits_product_has_kenmerken
LEFT JOIN fws_product ON webits_product_has_kenmerken.product_id = fws_product.ID
WHERE fws_product.CATID = 11
AND (
(webits_product_has_kenmerken.kenmerk_id = 8 AND webits_product_has_kenmerken.kenmerk_value = 'Buddha to Buddha')
OR
(webits_product_has_kenmerken.kenmerk_id = 19 AND webits_product_has_kenmerken.kenmerk_value = '10 mm')
)
checke these columns for NULL values:
fws_product.CATID
webits_product_has_kenmerken.kenmerk_id
webits_product_has_kenmerken.kenmerk_value
every comparison with NULL will exclude the row from the reult

Complicated MySQL Query Problem

I have a problem with a MySQL Query:
I have two tables:
- clustercategories
- domains
Now I have a SQL Query which lists all Domains of a specific category with category name - this is my Query:
SELECT domains.*, clustercategories.clustercategoryname
FROM (domains, clustercategories)
WHERE ((clustercategories.id = 3 AND (domains.cluster1id = 3 OR domains.cluster2id = 3))
OR (clustercategories.id = 10 AND (domains.cluster1id = 10 OR domains.cluster2id = 10)))
AND domains.status = '1'
GROUP BY domains.name
ORDER BY domains.name
The Problem is now, that I also have a third table "subpages" where I want to count all entries of a specific domain with status = '1' and I don't know how to modify my query to work - I have tried this query, but I do not get any results:
SELECT domains.*, clustercategories.clustercategoryname
FROM (domains, clustercategories)
WHERE ((clustercategories.id = 3 AND (domains.cluster1id = 3 OR domains.cluster2id = 3) AND (SELECT COUNT(*) AS total FROM subpages WHERE subpages.domainid = domains.id AND subpages.status = '1'))
OR (clustercategories.id = 10 AND (domains.cluster1id = 10 OR domains.cluster2id = 10) AND (SELECT COUNT(*) AS total FROM subpages WHERE subpages.domainid = domains.id AND subpages.status = '1')))
AND domains.status = '1'
GROUP BY domains.name
ORDER BY domains.name
Has anyone any ideas?
I think that you'll want to put your subquery into your projection, like this:
SELECT domains.*, clustercategories.clustercategoryname,
(SELECT COUNT(*) FROM subpages WHERE subpages.domainid = domains.id AND subpages.status = '1') AS total
FROM domains, clustercategories
WHERE ((clustercategories.id = 3 AND (domains.cluster1id = 3 OR domains.cluster2id = 3))
OR (clustercategories.id = 10 AND (domains.cluster1id = 10 OR domains.cluster2id = 10)))
AND domains.status = '1'
GROUP BY domains.name
ORDER BY domains.name
It looks to me your first query can be rewritten like this
SELECT d.*
, cc.clustercategoryname
FROM domains d
INNER JOIN clustercategories cc
ON cc.id = d.cluster1id
OR cc.id = d.cluster2id
WHERE cc.id IN (3, 10)
AND d.status = '1'
GROUP BY
d.name
ORDER BY
d.name
thus adding the count of subpages can be done like this
SELECT d.*
, cc.clustercategoryname
, sp.total
FROM domains d
INNER JOIN clustercategories cc
ON cc.id = d.cluster1id
OR cc.id = d.cluster2id
LEFT OUTER JOIN (
SELECT COUNT(*) AS total
, domainid
FROM subpages
WHERE subpages.status = '1'
GROUP BY
domainid
) sp ON sp.domainid = d.domainid
WHERE cc.id IN (3, 10)
AND d.status = '1'
GROUP BY
d.name
ORDER BY
d.name

When doing a UNION in mysql how can I do a where on the results

Hi I am doing a union over several tables. It's a little long but works!
(SELECT user_id,added_date,group_id,'joined',0,0,'' FROM group_members WHERE status = 1)
UNION
(SELECT user_id,added_date,object_id,'made a comment',0,0,'' FROM comments WHERE object_type = 11 AND status = 1)
UNION
(SELECT user_id,added_date,group_id,'made the event',1,group_calendar_id,title FROM group_calendars WHERE status = 1)
UNION
(SELECT comments.user_id,comments.added_date,group_calendars.group_id,'made a comment on the event',1,group_calendar_id,'' FROM group_calendars
INNER JOIN comments ON group_calendars.group_calendar_id = comments.object_id WHERE group_calendars.status = 1 AND comments.status = 1 AND object_type = 10
)
UNION
(SELECT user_id,pd.added_date,pd.object_id,'uploaded a photo',2,pd.photo_data_id,
(SELECT varchar_val FROM photo_data WHERE data_id = 1 AND photo_data.photo_id = photos.photos_id AND object_type = 3 AND object_id = pd.object_id)
FROM photo_data pd
INNER JOIN photos ON photos.photos_id = pd.photo_id
WHERE photos.photo_status = 1 AND pd.status = 1 AND pd.data_id = 0 AND pd.object_type = 3
)
UNION
(SELECT cp.user_id,cp.added_date,cp.object_id,'made a comment on the photo',2,pd.photo_data_id,
(SELECT varchar_val FROM photo_data WHERE data_id = 1 AND photo_data.photo_id = photos.photos_id AND object_type = 3 AND object_id = pd.object_id)
FROM comments cp
INNER JOIN photo_data pd ON pd.photo_data_id = cp.object_id
INNER JOIN photos ON photos.photos_id = pd.photo_id
WHERE cp.object_type = 8 AND cp.status = 1 AND pd.status = 1 AND pd.data_id = 0 AND photos.photo_status = 1 AND pd.object_type = 3
)
UNION
(SELECT user_id,added_date,group_id,'made a topic',3,forum_topic_id,title FROM forum_topics WHERE forum_categories_id = ".GROUP_FORUM_CATEGORY." AND group_id > 0 AND status = 1)
UNION
(SELECT forum_comments.user_id,forum_comments.added_date,group_id,'made a comment on the topic',3,forum_comments.forum_topic_id,title FROM forum_comments
INNER JOIN forum_topics ON forum_comments.forum_topic_id = forum_topics.forum_topic_id
WHERE forum_topics.forum_categories_id = 16 AND forum_topics.group_id > 0 AND forum_topics.status = 1 AND forum_comments.status = 1
)
This gets all the activity from a set of groups. My question is at the end I want to make sure that the group is active.
So at the end want to do something like WHERE (SELECT COUNT(1) FROM groups g WHERE g.group_id = group_id AND status = 1) = 1
Is there any way of doing that?
i'd suggest to store it to a view or temporary table and query the view then. i know you will have two calls then, but it's actually faster in mysql that way.