MySQL - Поиск продукта по многим категориям (много строк ...) быстрее? - PullRequest
0 голосов
/ 23 сентября 2018

У меня есть большой набор данных (то есть по 20 миллионов строк в каждой таблице), который выглядит примерно так (я на самом деле не имею дело с продуктами и категориями, но такая же ситуация)

Таблица продуктов

|id|name|created_at|
--------------------
|1 |....|2018-06...|
|2 |....|2018-06...|
|3 |....|2018-06...|
|4 |....|2018-06...|
|5 |....|2018-06...|
...etc.

И таблица категорий

|id|product_id|category|description|
------------------------------------
|1 |1         | abc    | def       |
|2 |1         | ghi    | jkl       |
|3 |1         | mno    | pqr       |
|4 |2         | abc    | stu       |
|5 |2         | wvx    | yz        |
...etc

Я хочу выполнить поиск, чтобы найти продукты со многими категориями, т. Е.

SELECT DISTINCTROW * FROM product WHERE

product.id in (
    SELECT categories.product_id FROM categories WHERE category = 'abc'
)
AND
product.id in(
    SELECT categories.product_id FROM categories WHERE category = 'ghi'
)
AND
product.id in(
    SELECT categories.product_id FROM categories WHERE category= 'mno'
)
AND 'some extra where' ORDER BY product.created_au LIMIT 10 offset 0

Но это действительно медленно ... Я пробовал разные подходы к этому, но каждый занимает не менее 30 секунд.

Я составил индекс столбцов, используемых для объединения.

В общем, я хочу сделать поиск, когда товар должен соответствовать одной или нескольким категориям.Кроме того, позже я хочу выполнить поиск, чтобы товар соответствовал категории и описанию (может быть из разных строк в таблице категорий).

Есть идеи?Или, возможно, ссылки, где я могу прочитать больше о таких случаях, как это?

Ответы [ 3 ]

0 голосов
/ 23 сентября 2018

Временная таблица должна помочь ускорить процесс.

CREATE TEMPORARY TABLE tmpCategories( category_id INT)

Найдите способ вставить идентификаторы категории, которую вы хотите найти, в эту таблицу.

Затем напишите объединение, похожее на это:

SELECT p.* FROM product p
INNER JOIN categories c ON p.product_id = c.product_id
INNER JOIN tmpCategories tc ON tc.catrgory_id = c.category_id

Ваш запрос может быть не совсем таким.Но такой подход может ускорить процесс.

PS Я печатаю с телефона, так что извините за форматирование:)

0 голосов
/ 23 сентября 2018

Я бы просто использовал GROUP BY и HAVING для получения продуктов:

select c.product_id
from categories c
where c.category in ('abc', 'ghi', 'mno')
group by c.product_id
having count(*) = 3;

Вы можете использовать join, exists или in, чтобы получить остаток продуктаинформация:

select p.*
from products p join
     (select c.product_id
      from categories c
      where c.category in ('abc', 'ghi', 'mno')
      group by c.product_id
      having count(*) = 3
     ) cp
     on c.product_id = p.i
where . . .  -- other conditions on product
0 голосов
/ 23 сентября 2018

Первое предложение, которое вы могли бы использовать INNER JOIN в подзапросе вместо IN

    SELECT DISTINCTROW * 
    FROM product p 
    INNER JOIN  (
        SELECT categories.product_id FROM categories WHERE category = 'abc'
    ) t1 on p.id  = t1.product_id
    INNER JOIN (
        SELECT categories.product_id FROM categories WHERE category = 'ghi'
    ) t2 on p.id = t2.product_id
    INNER JOIN (
        SELECT categories.product_id FROM categories WHERE category= 'mno'
    ) t3 p.id = t3.product_id
    WHERE  'some extra where' 

    ORDER BY product.created_au LIMIT 10 offset 0

Вы также можете попробовать использовать один подзапрос для получения всех product_id с категорией 3

    SELECT DISTINCTROW * 
    FROM product p 
    INNER JOIN  (
       SELECT categories.product_id FROM categories WHERE category IN ( 'abc','ghi', 'mno')
       group by categories.product_id 
       having count(distinct category ) = 3
    ) t1 on p.id  = t1.product_id
    WHERE  'some extra where' 

    ORDER BY product.created_au LIMIT 10 offset 0

Если внутреннее объединение не ускоряется, как ожидалось, убедитесь, что у вас есть правильный составной индекс для таблицы категорий

 CREATE INDEX index_name  ON categories(category,product_id );
...