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.
Related
this is prestashop 1.7 version category get product query. if use random, it is very slow, how optimize it?
SELECT
cp.id_category,
p.*,
product_shop.*,
stock.out_of_stock,
IFNULL( stock.quantity, 0 ) AS quantity,
IFNULL( product_attribute_shop.id_product_attribute, 0 ) AS id_product_attribute,
product_attribute_shop.minimal_quantity AS product_attribute_minimal_quantity,
pl.`description`,
pl.`description_short`,
pl.`available_now`,
pl.`available_later`,
pl.`link_rewrite`,
pl.`meta_description`,
pl.`meta_keywords`,
pl.`meta_title`,
pl.`name`,
image_shop.`id_image` id_image,
il.`legend` AS legend,
m.`name` AS manufacturer_name,
cl.`name` AS category_default,
DATEDIFF(
product_shop.`date_add`,
DATE_SUB( "2019-11-30 00:00:00", INTERVAL 7 DAY )) > 0 AS new,
product_shop.price AS orderprice
FROM
`ps_category_product` cp
LEFT JOIN `ps_product` p ON p.`id_product` = cp.`id_product`
INNER JOIN ps_product_shop product_shop ON ( product_shop.id_product = p.id_product AND product_shop.id_shop = 1 )
LEFT JOIN `ps_product_attribute_shop` product_attribute_shop ON ( p.`id_product` = product_attribute_shop.`id_product` AND product_attribute_shop.`default_on` = 1 AND product_attribute_shop.id_shop = 1 )
LEFT JOIN ps_stock_available stock ON ( stock.id_product = `p`.id_product AND stock.id_product_attribute = 0 AND stock.id_shop = 1 AND stock.id_shop_group = 0 )
LEFT JOIN `ps_category_lang` cl ON ( product_shop.`id_category_default` = cl.`id_category` AND cl.`id_lang` = 11 AND cl.id_shop = 1 )
LEFT JOIN `ps_product_lang` pl ON ( p.`id_product` = pl.`id_product` AND pl.`id_lang` = 11 AND pl.id_shop = 1 )
LEFT JOIN `ps_image_shop` image_shop ON ( image_shop.`id_product` = p.`id_product` AND image_shop.cover = 1 AND image_shop.id_shop = 1 )
LEFT JOIN `ps_image_lang` il ON ( image_shop.`id_image` = il.`id_image` AND il.`id_lang` = 11 )
LEFT JOIN `ps_manufacturer` m ON m.`id_manufacturer` = p.`id_manufacturer`
WHERE
product_shop.`id_shop` = 1
AND cp.`id_category` = 12
AND product_shop.`active` = 1
AND product_shop.`visibility` IN ( "both", "catalog" )
ORDER BY
RAND()
LIMIT 50
Please provide SHOW CREATE TABLE for each table. Meanwhile, ...
Let's start by optimizing the joins.
LEFT JOIN `ps_product_lang` pl ON ( p.`id_product` = pl.`id_product`
AND pl.`id_lang` = 11
AND pl.id_shop = 1 )
That needs INDEX(id_product, id_lang, id_shop) (The columns may be in any order.)
Don't use LEFT unless you really need to fetch a row from the righthand table as NULLs when it does not exist. In particular,
LEFT JOIN `ps_product` p
is probably getting in the way of optimization.
WHERE product_shop.`id_shop` = 1
AND product_shop.`active` = 1
AND product_shop.`visibility` IN ( "both", "catalog" )
would probably benefit from these indexes
INDEX(id_shop, active, visibility, id_product)
INDEX(id_product, id_shop, active, visibility)
product_category needs
INDEX(id_category, id_product) -- in this order.
In general many-to-many mapping tables need to follow the tips here: http://mysql.rjweb.org/doc.php/index_cookbook_mysql#many_to_many_mapping_table
The query has the "explode-implode" syndrome. This is where it first does a JOINs, collecting a lot of data, then throws away much of it due, in your case, to the LIMIT 10. It can probably be cured by turning the query inside-out. The general ID is to start with a derived table that gets the 10 rows desired, then reaches into the other table for the rest of the desired columns. This "reaching" need happen only 10 times, not however many the JOINs currently require.
SELECT ...
FROM ( SELECT <<primary key columns from cp, p, and product_shop>>
FROM cp
JOIN p ON ...
JOIN product_shop ON ...
ORDER BY RAND()
LIMIT 10 ) AS x
JOIN <<p, product_shop ON their PKs>> -- to get p.*, product_shop.*>>
[LEFT] JOIN << each of the other tables>> -- to get the other tables
You should start by testing the subquery (a "derived" table) to verify that it is noticeably faster than the original query.
SELECT commerce_product_field_data_commerce_product__field_data_field_products.entity_id, field_products_commerce_product.nid FROM commerce_order o
join commerce_payment_transaction t on o.order_id = t.order_id
join commerce_line_item i on o.order_id = i.order_id
LEFT JOIN field_data_commerce_total s ON i.line_item_id = s.entity_id AND (s.entity_type = 'commerce_line_item' AND s.deleted = '0')
LEFT JOIN field_data_commerce_product field_data_commerce_product ON i.line_item_id = field_data_commerce_product.entity_id AND (field_data_commerce_product.entity_type = 'commerce_line_item' AND field_data_commerce_product.deleted = '0')
INNER JOIN commerce_product commerce_product_field_data_commerce_product ON field_data_commerce_product.commerce_product_product_id = commerce_product_field_data_commerce_product.product_id
LEFT JOIN
(select * from field_data_field_products)
commerce_product_field_data_commerce_product__field_data_field_products ON commerce_product_field_data_commerce_product.product_id = commerce_product_field_data_commerce_product__field_data_field_products.field_products_product_id
LEFT JOIN ( select nid as nid from node order by nid)
field_products_commerce_product
ON commerce_product_field_data_commerce_product__field_data_field_products.entity_id = field_products_commerce_product.nid LEFT JOIN (
select r.entity_id, r.field_ranges_value from field_data_field_ranges r
) r
on r.entity_id = field_products_commerce_product.nid
WHERE t.status = 'success' and i.type = 'product' and o.Uid <> 0
AND o.status IN ('completed') and o.created >= '1483228800' and o.created <= '1483315200' and r.field_ranges_value = 'Tasty Sticks'
Is my sql
It is giving me 5 results. I only need 4
One of the product id's belong to two Drupal nodes and I only want one of them
I tried changing LEFT JOIN ( select nid as nid from node order by nid) to
LEFT JOIN ( select nid as nid from node order by nid limit 1) but then I don't get any records at all. Any idea what needs changing please other than removing one of the duplicate nodes. Thanks
After migrating Prestashop instance from MySQL to MariaDB filtered search queries are gotten slow. Here's an example of a such slow query.
SELECT
fl.name feature_name,
fp.id_feature,
fv.id_feature_value,
fvl.value,
COUNT(DISTINCT p.id_product) nbr,
lifl.url_name name_url_name,
lifl.meta_title name_meta_title,
lifvl.url_name value_url_name,
lifvl.meta_title value_meta_title,
psi.price_min,
psi.price_max,
m.name
FROM
ps_feature_product fp
INNER JOIN
ps_product p
ON
(p.id_product = fp.id_product)
LEFT JOIN
ps_feature_lang fl
ON
(
fl.id_feature = fp.id_feature AND fl.id_lang = 2
)
INNER JOIN
ps_feature_value fv
ON
(
fv.id_feature_value = fp.id_feature_value AND(
fv.custom IS NULL OR fv.custom = 0
)
)
LEFT JOIN
ps_feature_value_lang fvl
ON
(
fvl.id_feature_value = fp.id_feature_value AND fvl.id_lang = 2
)
LEFT JOIN
ps_layered_indexable_feature_lang_value lifl
ON
(
lifl.id_feature = fp.id_feature AND lifl.id_lang = 2
)
LEFT JOIN
ps_layered_indexable_feature_value_lang_value lifvl
ON
(
lifvl.id_feature_value = fp.id_feature_value AND lifvl.id_lang = 2
)
INNER JOIN
ps_product_shop product_shop
ON
(
product_shop.id_product = p.id_product AND product_shop.id_shop = 1
)
INNER JOIN
`ps_layered_price_index` psi
ON
(
psi.id_product = p.id_product AND psi.id_currency = 2 AND psi.id_shop = 1
)
LEFT JOIN
`ps_manufacturer` m
ON
(
m.id_manufacturer = p.id_manufacturer
)
WHERE
product_shop.`active` = 1 AND product_shop.`visibility` IN("both", "catalog") AND fp.id_feature = 9 AND p.id_product IN(
SELECT
id_product
FROM
ps_category_product cp
INNER JOIN
ps_category c
ON
(
c.id_category = cp.id_category AND c.id_category = 13 AND c.active = 1
)
) AND p.id_product IN(
SELECT
id_product
FROM
ps_feature_product fp
WHERE
fp.`id_feature_value` = 39
) AND p.id_product IN(
SELECT
id_product
FROM
ps_feature_product fp
WHERE
fp.`id_feature_value` = 18
) AND p.id_product IN(
SELECT
id_product
FROM
ps_feature_product fp
WHERE
fp.`id_feature_value` = 13
) AND p.id_product IN(
SELECT
id_product
FROM
ps_feature_product fp
WHERE
fp.`id_feature_value` = 44
) AND p.id_product IN(
SELECT
id_product
FROM
ps_feature_product fp
WHERE
fp.`id_feature_value` = 3186
)
GROUP BY
fv.id_feature_value
ORDER BY
fv.position
What is interesting is that it gets a lot faster (4s vs 0.4s) if one of the multiple (nearly identical) subqueries in the WHERE clause is removed.
... AND
/*
-- removing any of the six product filters makes
-- the query a lot faster
p.id_product IN(
SELECT
id_product
FROM
ps_feature_product fp
WHERE
fp.`id_feature_value` = 18
)
*/
AND ...
Here's the EXPLAIN output for this query (without the modifications).
Is there's something obvious that needs to be changed somehow? All tables are already OPTIMIZEd with no improvement.
You have several of these:
AND p.id_product IN (
SELECT id_product
FROM ps_feature_product fp
WHERE fp.`id_feature_value` = 39 )
which could be turned into this
AND EXISTS ( SELECT * FROM ps_feature_product
WHERE id_product = p.id_product
AND id_feature_value = 39 )
IN ( SELECT ... ) is poorly optimized.
Be sure to have INDEX(id_product, id_feature_value).
The reason for the difference in performance between MySQL and MariaDB is that several optimization improvements diverged in about 5.6. They involve stuff relating to what you are doing.
Things like
LEFT JOIN ps_layered_indexable_feature_value_lang_value lifvl
ON ( lifvl.id_feature_value = fp.id_feature_value
AND lifvl.id_lang = 2
need a composite INDEX(id_feature_value, id_lang) (in either order). But I can guess from the EXPLAIN that you have such.
Please provide SHOW CREATE TABLE for each table, there may be more advice.
I can't address your specific question because I don't know which line of the EXPLAIN corresponds to the removed clause.
I have a really long select from my database with many joins. The problem is with counting SUM: without sum, select time is about 3s, but with SUM is about 15s.
Is it possible to optimize my select to obtain a shorter select time?
Here is my code:
SELECT
accomodation.id,
accomodation.aid,
accomodation.title_en,
accomodation.title_url_en,
accomodation.address,
accomodation.zip,
accomodation.stars,
accomodation.picture,
accomodation.valid_from,
accomodation.valid_to,
accomodation.latitude,
accomodation.longitude,
accomodation.city_id AS
accomodation_city_id,
db_cities.id AS city_id,
db_cities.title_en AS city,
db_cities.title_url AS city_url,
db_countries.title_en AS country_title,
db_countries.title_url_en AS country_url,
accomodation_type.class AS accomodation_type_class,
accomodation_review_value_total.value AS review_total,
MIN(accomodation_price.price) AS price_from,
accomodation_rooms.total_persons
FROM
(SELECT aid, MAX(info_date_add) AS max_info_date_add FROM accomodation GROUP BY aid) accomodation_max
INNER JOIN accomodation
ON
accomodation_max.aid = accomodation.aid AND
accomodation_max.max_info_date_add = accomodation.info_date_add
LEFT JOIN db_cities
ON (
db_cities.id = accomodation.city_id OR
(((acos(sin((db_cities.latitude*pi()/180)) * sin((accomodation.latitude*pi()/180)) + cos((db_cities.latitude*pi()/180)) * cos((accomodation.latitude*pi()/180)) * cos(((db_cities.longitude - accomodation.longitude)*pi()/180))))*180/pi())*60*1.1515*1.609344) < '20')
JOIN db_countries
ON db_countries.id = accomodation.country_id
LEFT JOIN accomodation_review_value_total
ON accomodation_review_value_total.accomodation_aid = accomodation.aid
LEFT JOIN accomodation_type_value
ON accomodation_type_value.accomodation_id = accomodation.id
LEFT JOIN accomodation_type
ON accomodation_type.id = accomodation_type_value.accomodation_type_id
JOIN accomodation_season
ON (
accomodation_season.accomodation_aid = accomodation.aid AND
( '2013-11-04' BETWEEN accomodation_season.start_date AND accomodation_season.end_date OR '2013-11-05' BETWEEN accomodation_season.start_date AND accomodation_season.end_date ) )
JOIN accomodation_price
ON
accomodation_price.accomodation_aid = accomodation.aid AND
accomodation_price.accomodation_price_type_id = '1' AND
accomodation_price.accomodation_price_cat_id = '1' AND
accomodation_price.price BETWEEN '20' AND '250' AND
accomodation_price.accomodation_season_id = accomodation_season.id
JOIN accomodation_theme_value
ON accomodation_theme_value.accomodation_id = accomodation.id
INNER JOIN
(SELECT
accomodation_id,
SUM(accomodation_rooms.rooms) AS total_rooms,
SUM(accomodation_rooms.beds * accomodation_rooms.rooms) AS total_persons
FROM accomodation_rooms
GROUP BY accomodation_id) accomodation_rooms
ON
accomodation_rooms.accomodation_id = accomodation.id AND
accomodation_rooms.total_persons >= '4'
WHERE
db_countries.title_url_en LIKE '%spain%' AND
db_cities.title_url LIKE '%barcelona%' AND
accomodation_type_value.accomodation_type_id IN (5,10) AND
total_rooms >= '2' AND
accomodation_theme_value.accomodation_theme_id IN (11,12,13) AND
accomodation.stars IN (3,4,5) AND
( accomodation_review_value_total.value >= '4.5' ) AND
db_cities.id = '2416'
GROUP BY accomodation.aid
ORDER BY
CASE
WHEN accomodation.valid_to>=NOW() AND accomodation.valid_from<=NOW() AND MIN(accomodation_price.price) IS NOT NULL THEN 0
WHEN NOW()>accomodation.valid_to AND accomodation.valid_to>'0000-00-00' AND MIN(accomodation_price.price) IS NOT NULL THEN 1
WHEN accomodation.valid_to>=NOW() AND accomodation.valid_from<=NOW() THEN 2
WHEN NOW()>accomodation.valid_to AND accomodation.valid_to>'0000-00-00' THEN 3
ELSE 4 END,
review_total DESC,
accomodation.title_en
LIMIT 10
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