Запрос работает слишком медленно, когда нет результатов. Как это улучшить? - PullRequest
0 голосов
/ 22 марта 2012

У меня есть три таблицы

filters (id, name)
items(item_id, name)
items_filters(item_id, filter_id, value_id)
values(id, filter_id, filter_value)

около 20000 записей в пунктах.около 80000 записей в items_filters.

SELECT i.*
    FROM items_filters itf INNER JOIN items i ON i.item_id = itf.item_id
     WHERE (itf.filter_id = 1 AND itf.value_id = '1') 
     OR (itf.filter_id = 2 AND itf.value_id = '7') 
    GROUP BY itf.item_id 
    WITH ROLLUP 
    HAVING COUNT(*) = 2
   LIMIT 0,10;

Это 0,008 раза, когда есть записи, которые соответствуют запросу, и 0,05, когда нет записей.

Я пробовал разные варианты раньше:

SELECT * FROM items WHERE item_id IN (
    SELECT `item_id` 
     FROM `items_filters` 
     WHERE (`filter_id`='1' AND `value_id`=1) 
     OR (`filter_id`='2' AND `value_id`=7)
    GROUP BY `item_id` 
    HAVING COUNT(*) = 2
   ) LIMIT 0,6;

Это полностью замораживает mysql, когда нет записей.

Чего я действительно не получаю, так это того, что SELECT i. * FROM items_filters itf ВНУТРЕННИЕ СОВМЕСТНЫЕ элементы i ON i.item_id = itf.item_id ГДЕ itf.filter_id = 1 AND itf.value_id = '1' LIMIT 0,1 занимает ~ 0,05, если записи не найдены, и ~ 0,008, когда есть

Объясните

| id | select_type | table | type | possible_keys | key     | key_len | ref                 | rows | Extra                           |
|  1 | SIMPLE      | i     | ALL  | PRIMARY       | NULL    | NULL    | NULL                |   10 | Using temporary; Using filesort |
|  1 | SIMPLE      | itf   | ref  | item_id       | item_id | 4       | ss_stylet.i.item_id |    1 | Using where; Using index        |

Ответы [ 2 ]

0 голосов
/ 22 марта 2012

Помимо обеспечения и индексации на items_filters для обоих (filter_id, value_id), я бы предварительно квалифицировал ваши идентификаторы элементов с помощью группы, затем присоединяюсь к таблице элементов. Похоже, вы пытаетесь найти предмет, отвечающий двум конкретным условиям, и для этого возьмите предметы ...

Я также оставил "group by with rollup" во внешнем интерфейсе, даже если для внутреннего идентификатора будет возвращен отдельный экземпляр. Но поскольку внутренний запрос уже применяет ограничение в 0,10 записей, он не выдает слишком много результатов для присоединения к вашей таблице элементов.

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

SELECT i.*
   FROM
      ( select itf.item_id
           from items_filters itf
          WHERE (itf.filter_id = 1 AND itf.value_id = '1') 
             OR (itf.filter_id = 2 AND itf.value_id = '7') 
          GROUP BY itf.item_id 
          HAVING COUNT(*) = 2
          LIMIT 0, 10 ) PreQualified
      JOIN items i 
         ON  PreQualified.item_id = i.item_id

Другим подходом может быть СОЕДИНЕНИЕ по внутреннему запросу, чтобы вам даже не нужно было применять группу с помощью и наличием. Поскольку вы явно ищете ровно два элемента, я бы попробовал следующее. Таким образом, первый квалификатор ДОЛЖЕН иметь запись с ID = 1 и значением = '1'. Если бы он даже не попал в эту запись, он никогда не позаботился бы о втором. Затем, применяя объединение к той же таблице (с псевдонимом itf2), он должен найти по тому же идентификатору - И условия для второго (id = 2 value = '7'). Это в основном заставляет выглядеть почти как один проход против одной записи ПЕРВЫЙ, и прежде всего перед рассмотрением чего-либо еще. Это все равно приведет к тому, что вы получите ограниченный набор из 10, прежде чем получите детали предмета.

SELECT i.*
   FROM
      ( select itf.item_id
           from items_filters itf
              join items_filters itf2
                 on itf.item_id = itf2.item_id
                AND itf2.filter_id = 2 
                AND itf2.value_id = '7'
          WHERE 
             itf.filter_id = 1 AND itf.value_id = '1'
          LIMIT 0, 10 ) PreQualified
      JOIN items i 
         ON  PreQualified.item_id = i.item_id

Я также удалил группу с / с накоплением согласно вашему комментарию дубликатов (что я и ожидал).

0 голосов
/ 22 марта 2012

Для меня это выглядит как четыре таблицы.

Сделайте EXPLAIN PLAN по запросу и найдите TABLE SCAN.Если вы видите один, добавьте индексы для столбцов в предложениях WHERE.Это, безусловно, поможет.

...