The naughty LIKE
Maybe...
If you have a "category" and an optional "subcategory", then consider the following approach:
- Any c_id starting with "[111]" belongs to category 111.
- c_id = "[111][2]" refers to subcategory 2 in category 111.
- That could be carried further with sub-subcategories, etc.
THEN, these become very efficient; note the lack of a leading wildcard:
WHERE c_id LIKE '[111]%'
WHERE c_id = '[111][2]' -- if you knew it was two levels only, or
WHERE c_id LIKE '[111][2]%' -- if you allow 'two or more' levels
However, asking for items with subcategory '[2]', would still be inefficient due to the leading wildcard:
WHERE c_id LIKE '%[2]%'
FYI, a LIKE
without any wildcards optimizes as a simple =
.
(You might consider the more traditional notation: "111/2".)
Why the JOIN helps in this case
As for why this can be better:
SELECT p.*
FROM ( SELECT id ... FROM p WHERE ... ORDER BY ... LIMIT 3 ) AS a
JOIN p USING(id)
Note the LIMIT
. (or GROUP BY
.) The "derived table" (that subquery) may be optimizable and it delivers only 3 ids. Then the only 3 lookups into p
for the outer SELECT
is much less effort.
Without this contorted formulation, it may involve fetching p.*
for lots of rows before filtering and sorting down to just 3 rows. That can be bulky and inefficient.
Without the subquery, it may have fetched all the LONGTEXT
rows then kept only 3 of them. That is I/O was the villain. (Need to see the actual queries -- with and without join -- plus SHOW CREATE TABLE
, to further explain my point.
Many-to-many
Yes, since many products map to many categories, do use a 'linking' table. A discussion of such a schema: http://mysql.rjweb.org/doc.php/index_cookbook_mysql#many_to_many_mapping_table