mysql – Database query refactoring with multiples FIND_IN_SET()

Question:

I have the following query to find topics related by a set of ID's that are not the topic to be viewed:

SELECT
    press.image,
    press_i18n.title,
    press_i18n.slug
FROM press
INNER JOIN press_i18n ON (
    press.press_id = press_i18n.press_id
)
WHERE press.ocult_from_site = 'no'
AND press.status = 'active'
AND press_i18n.i18n_id = 'por'
AND (
    FIND_IN_SET (1326, REPLACE(press.tag_id, ";", ","))
    OR
    FIND_IN_SET (77, REPLACE(press.tag_id, ";", ","))
)
AND press_i18n.slug != 'bubu-tem-muito-sono'
ORDER by press.publish_date DESC

Showing rows 0 – 7 (8 total, Query took 0.0047 sec)

The query according to the example above is receiving two ID's, but if it receives twenty, it will become a little repetitive.

Also, the FIND_IN_SET() function was designed to work with , but is being used in this example to find values ​​separated by ; .

Question

How to optimize the query in order to guarantee its correct performance as the table fills and/or the number of ID's to be located increases?

-- ...
AND (
    FIND_IN_SET (1326, REPLACE(press.tag_id, ";", ","))
    OR
    FIND_IN_SET (77, REPLACE(press.tag_id, ";", ","))
    OR
    FIND_IN_SET (10545, REPLACE(press.tag_id, ";", ","))
    OR
    FIND_IN_SET (43256, REPLACE(press.tag_id, ";", ","))
    OR
    FIND_IN_SET (1234567, REPLACE(press.tag_id, ";", ","))
    OR
    FIND_IN_SET (7654321, REPLACE(press.tag_id, ";", ","))
)
-- ...

SQL Fiddle to help with testing, with minimal structure for the given example.

Answer:

It would be interesting for you to normalize the database, creating a relationship table between press and tags . The press.tag_id column would be press.tag_id , and the press_tags relationship table would look like this:

press_id   tag_id
------------------
       1     1326
       1       77
       1    10545

You make a JOIN with this table in the query body:

LEFT OUTER JOIN press_tags
ON press_tag.press_id = press.id

Then the entire block with the FIND_IN_SET can be replaced by:

AND press_tags.tag_id IN(1326, 77, 10545)

All together:

SELECT
    press.image,
    press_i18n.title,
    press_i18n.slug
FROM press
INNER JOIN press_i18n ON (
    press.press_id = press_i18n.press_id
)
LEFT OUTER JOIN press_tags ON (
    press_tag.press_id = press.id
    -- ou 
    -- press_tag.press_id = press.press_id
    -- (o que fizer mais sentido no seu banco)
)
WHERE press.ocult_from_site = 'no'
AND press.status = 'active'
AND press_i18n.i18n_id = 'por'
AND press_tags.tag_id IN(1326, 77, 10545)
AND press_i18n.slug != 'bubu-tem-muito-sono'
ORDER by press.publish_date DESC

You might also need an index on tag_id this relationship table. You may also want an id column (PK with auto increment), some frameworks require this to be able to delete rows.

Scroll to Top
AllEscort