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.
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.
I would like to search a keyword from the result of the subquery. In my subquery I already filtered the grants I need from different category required. Now I need to search the keyword in title for those results. Or is there efficient way to do this query because my work is not working after days stuck?
I tried working on the IN but still did not get it right.
SELECT DISTINCT gt.grant_id, gt.*,
infra.infra_name,
infrasub.infrasub_name,
lga.lga_name,
stream.stream_id,
stream.stream_n,
stream.stream_dept,
stream.stream_desc
FROM
grant_tbl AS gt
LEFT JOIN grant_details AS gd
ON (
gt.grant_id = gd.grant_id
)
LEFT JOIN infra_sub_tbl AS infrasub
ON (
infrasub.infra_sub_id = gd.infrasub_id
)
LEFT JOIN infra_tbl AS infra
ON (
infra.infra_id = gd.infra_id
)
LEFT JOIN lga_tbl AS lga
ON (
lga.lga_id = gd.lga_id
)
LEFT JOIN streams_tbl AS stream
ON (
stream.stream_id = gd.stream_id
)
WHERE gt.grant_id IN
(
SELECT DISTINCT gd.grant_id, CONCAT(gt.grant_name,"|",gt.grant_desc,"|",gt.keywords)
FROM grant_details AS gd
LEFT JOIN grant_tbl AS gt
ON gt.grant_id = gd.grant_id
WHERE gd.lga_id = 1
OR gd.lga_id = 2
AND gd.stream_id = 1
OR gd.stream_id = 2
GROUP BY gt.grant_id
)
my result should narrow down from my subquery. display only with matching keyworkds
You can use EXISTS instead of IN,
SELECT DISTINCT gt.grant_id, gt.*,
infra.infra_name,
infrasub.infrasub_name,
lga.lga_name,
stream.stream_id,
stream.stream_n,
stream.stream_dept,
stream.stream_desc
FROM grant_tbl AS gt
LEFT JOIN grant_details AS gd ON gt.grant_id = gd.grant_id
LEFT JOIN infrasub_tbl AS infrasub ON infrasub.infrasub_id = gd.infrasub_id
LEFT JOIN infra_tbl AS infra ON infra.infra_id = gd.infra_id
LEFT JOIN lga_tbl AS lga ON lga.lga_id = gd.lga_id
LEFT JOIN stream_tbl AS stream ON stream.stream_id = gd.stream_id
WHERE exists
(
SELECT DISTINCT gd.grant_id,CONCAT(gt2.grant_name,"|",gt2.grant_desc,"|",gt2.keywords)
FROM grant_details AS gd
LEFT JOIN grant_tbl AS gt2 ON gt2.grant_id = gd.grant_id
WHERE (gd.lga_id = 10 OR gd.lga_id = 20) AND (gd.stream_id = 100 OR gd.stream_id = 200)
and gt.grant_id=gt2.grant_id
GROUP BY gt.grant_id
)
Hope this helps.
I'm not sure how to make the following SQL query more efficient. Right now, the query is taking 8 - 12 seconds on a pretty fast server, but that's not close to fast enough for a Website when users are trying to load a page with this code on it. It's looking through tables with many rows, for instance the "Post" table has 717,873 rows. Basically, the query lists all Posts related to what the user is following (newest to oldest).
Is there a way to make it faster by only getting the last 20 results total based on PostTimeOrder?
Any help would be much appreciated or insight on anything that can be done to improve this situation. Thank you.
Here's the full SQL query (lots of nesting):
SELECT DISTINCT p.Id, UNIX_TIMESTAMP(p.PostCreationTime) AS PostCreationTime, p.Content AS Content, p.Bu AS Bu, p.Se AS Se, UNIX_TIMESTAMP(p.PostCreationTime) AS PostTimeOrder
FROM Post p
WHERE (p.Id IN (SELECT pc.PostId
FROM PostCreator pc
WHERE (pc.UserId IN (SELECT uf.FollowedId
FROM UserFollowing uf
WHERE uf.FollowingId = '100')
OR pc.UserId = '100')
))
OR (p.Id IN (SELECT pum.PostId
FROM PostUserMentions pum
WHERE (pum.UserId IN (SELECT uf.FollowedId
FROM UserFollowing uf
WHERE uf.FollowingId = '100')
OR pum.UserId = '100')
))
OR (p.Id IN (SELECT ssp.PostId
FROM SStreamPost ssp
WHERE (ssp.SStreamId IN (SELECT ssf.SStreamId
FROM SStreamFollowing ssf
WHERE ssf.UserId = '100'))
))
OR (p.Id IN (SELECT psm.PostId
FROM PostSMentions psm
WHERE (psm.StockId IN (SELECT sf.StockId
FROM StockFollowing sf
WHERE sf.UserId = '100' ))
))
UNION ALL
SELECT DISTINCT p.Id AS Id, UNIX_TIMESTAMP(p.PostCreationTime) AS PostCreationTime, p.Content AS Content, p.Bu AS Bu, p.Se AS Se, UNIX_TIMESTAMP(upe.PostEchoTime) AS PostTimeOrder
FROM Post p
INNER JOIN UserPostE upe
on p.Id = upe.PostId
INNER JOIN UserFollowing uf
on (upe.UserId = uf.FollowedId AND (uf.FollowingId = '100' OR upe.UserId = '100'))
ORDER BY PostTimeOrder DESC;
Changing your p.ID in (...) predicates to existence predicates with correlated subqueries may help. Also since both halves of your union all query are pulling from the Post table and possibly returning nearly identical records you might be able to combine the two into one query by left outer joining to UserPostE and adding upe.PostID is not null as an OR condition in the WHERE clause. UserFollowing will still inner join to UPE. If you want the same Post record twice once with upe.PostEchoTime and once with p.PostCreationTime as the PostTimeOrder you'll need keep the UNION ALL
SELECT
DISTINCT -- <<=- May not be needed
p.Id
, UNIX_TIMESTAMP(p.PostCreationTime) AS PostCreationTime
, p.Content AS Content
, p.Bu AS Bu
, p.Se AS Se
, UNIX_TIMESTAMP(coalesce( upe.PostEchoTime
, p.PostCreationTime)) AS PostTimeOrder
FROM Post p
LEFT JOIN UserPostE upe
INNER JOIN UserFollowing uf
on (upe.UserId = uf.FollowedId AND
(uf.FollowingId = '100' OR
upe.UserId = '100'))
on p.Id = upe.PostId
WHERE upe.PostID is not null
or exists (SELECT 1
FROM PostCreator pc
WHERE pc.PostId = p.ID
and pc.UserId = '100'
or exists (SELECT 1
FROM UserFollowing uf
WHERE uf.FollowedId = pc.UserID
and uf.FollowingId = '100')
)
OR exists (SELECT 1
FROM PostUserMentions pum
WHERE pum.PostId = p.ID
and pum.UserId = '100'
or exists (SELECT 1
FROM UserFollowing uf
WHERE uf.FollowedId = pum.UserId
and uf.FollowingId = '100')
)
OR exists (SELECT 1
FROM SStreamPost ssp
WHERE ssp.PostId = p.ID
and exists (SELECT 1
FROM SStreamFollowing ssf
WHERE ssf.SStreamId = ssp.SStreamId
and ssf.UserId = '100')
)
OR exists (SELECT 1
FROM PostSMentions psm
WHERE psm.PostId = p.ID
and exists (SELECT
FROM StockFollowing sf
WHERE sf.StockId = psm.StockId
and sf.UserId = '100' )
)
ORDER BY PostTimeOrder DESC
The from section could alternatively be rewritten to also use an existence clause with a correlated sub query:
FROM Post p
LEFT JOIN UserPostE upe
on p.Id = upe.PostId
and ( upe.UserId = '100'
or exists (select 1
from UserFollowing uf
where uf.FollwedID = upe.UserID
and uf.FollowingId = '100'))
Turn IN ( SELECT ... ) into a JOIN .. ON ... (see below)
Turn OR into UNION (see below)
Some the tables are many:many mappings? Such as SStreamFollowing? Follow the tips in http://mysql.rjweb.org/doc.php/index_cookbook_mysql#many_to_many_mapping_table
Example of IN:
SELECT ssp.PostId
FROM SStreamPost ssp
WHERE (ssp.SStreamId IN (
SELECT ssf.SStreamId
FROM SStreamFollowing ssf
WHERE ssf.UserId = '100' ))
-->
SELECT ssp.PostId
FROM SStreamPost ssp
JOIN SStreamFollowing ssf ON ssp.SStreamId = ssf.SStreamId
WHERE ssf.UserId = '100'
The big WHERE with all the INs becomes something like
JOIN ( ( SELECT pc.PostId AS id ... )
UNION ( SELECT pum.PostId ... )
UNION ( SELECT ssp.PostId ... )
UNION ( SELECT psm.PostId ... ) )
Get what you can done of that those suggestions, then come back for more advice if you still need it. And bring SHOW CREATE TABLE with you.
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.
How do I link across and get the variables of three tables, I want to get the linked facilities for each room but currently my record seems to be returning nothing.
I would like to return a new column header for facilities as well: http://sqlfiddle.com/#!2/8d6ca/25
The fiddle can be found here
SELECT *
FROM ts_room rm
WHERE
NOT EXISTS (
SELECT 1
FROM ts_roompref rp
JOIN ts_request rq ON rp.request_id = rq.id AND day_id = 1 AND period_id = 1
WHERE rm.id = rp.room_id)
AND NOT EXISTS (
SELECT 1
FROM ts_roompref rp
JOIN ts_allocation a ON rp.request_id = a.request_id AND a.status = "Allocated"
WHERE rm.id = rp.room_id)
AND EXISTS (
SELECT 1
FROM ts_roomfacilities f
JOIN ts_room b ON f.room_id = b.id
WHERE rm.id = f.room_id AND
f.facilities_id=2);
AND EXISTS (
SELECT 1
FROM ts_facilities f1
JOIN ts_roomfacilities c ON f2.id = c.id
WHERE rm.id = f.room_id);
There's a ; in the middle of your query which you should remove