MySQL медленно пытается найти «пропущенные» записи в одной таблице на основе строк во второй таблице - PullRequest
1 голос
/ 18 мая 2011

Справочная информация:

Веб-приложение для электронной коммерции, но с «участниками», которые заказывают друг у друга.Когда участник выполняет различные действия (такие как поиск), записи вставляются в таблицу «Действия».Если / когда участник отправляет заказ, запись вносится в таблицу «Заказ».Цель состоит в том, чтобы найти случаи, когда участник выполнял действие, но НЕ размещал заказ в течение некоторого периода времени (скажем, часа) после времени действия.

ПРИМЕЧАНИЕ. Код, который создает записи заказане может быть измененоЕсли бы это было возможно, я мог бы просто «запомнить» действия и включить эту информацию в записи заказа.Затем найти случаи, когда член выполнил действие, но не разместил заказ, было бы просто: просто найдите значение NULL (или другое значение по умолчанию) в этом столбце таблицы заказов.Опять же, увы, в моей ситуации это невозможно ...

Таблицы:

  • Order (id, ts / * timestamp * /, send_member_id, receive_member_id,…)
  • Member (идентификатор, имя,…)
  • Activity_Type (идентификатор, имя,…)
  • Activity_Log (идентификатор, ts, member_id, type_id,extra_info)

Индексы:

All appropriate indexes are in place. Specifically, an index on order.ts does exist.

Я пробовал эти три запроса:

ПОДХОД 1

SELECT …
  FROM activity_log, 
       Member
 WHERE activity_log.member_id = member.id
   AND activity_log.type_id = 1 /* Search */
   AND activity_log.ts > [start time]
   AND activity_log.ts < [end time]
   AND NOT EXISTS (SELECT ‘x’
                     FROM order
                    WHERE order.ts >= activity_log.ts
                      AND order.ts <= activity_log.ts + 3600
                      AND order.sending_member_id = activity_log.member_id)
ORDER BY activity_log.member_id, activity_log.ts desc

ПОДХОД 2

SELECT …
  FROM activity_log, member
 WHERE activity_log.member_id = member.id
   AND activity_log.type_id = 1 /* Search */
   AND activity_log.ts > [start time]
   AND activity_log.ts < [end time]
   AND activity_log.member_id NOT IN (SELECT order.sending_member_id
                                        FROM order
                                       WHERE order.ts >= activity_log.ts
                                         AND order.ts <= activity_log.ts + 3600)
ORDER BY activity_log.member_id, activity_log.ts desc

APPROACH 3

   SELECT …
     FROM activity_log
     JOIN member ON activity_log.member_id = member.id
LEFT JOIN order ON order.ts >= activity_log.ts 
               AND order.ts <= activity_log.ts + 3600 
               AND activity_log.member_id = order.sending_member_id
    WHERE activity_log.type_id = 1 /* Search */
      AND activity_log.ts > [start time]
      AND activity_log.ts < [end time]
      AND order.sending_member_id IS NULL
 ORDER BY activity_log.member_id, activity_log.ts desc

Даже при подходе 3 запрос выполняется в течение 20-30 секунд и не использует индекс для order.ts.

Ответы [ 3 ]

0 голосов
/ 18 мая 2011

Для MySQL выбор зависит от сравниваемых столбцов:

Индексы будут бесполезны, если вы измените данные для сравнения:

AND order.ts <= activity_log.ts + 3600 

В этом примере индекс на ACTIVITY_LOG.ts не имеет значения. Рассмотрим составные индексы (один индекс, более одного столбца).

0 голосов
/ 23 мая 2011

К вашему сведению, я изменил третий запрос выше, как показано ниже, и это значительно ускоряет мой запрос, когда окно [время начала] - [время окончания] мало по сравнению с общим временным интервалом, представленным строками в Activity_log и порядке столы. По сути, MySQL решает, использовать ли индексы (ts) для каждой таблицы, основываясь на том, насколько они полезны ... Если я спрашиваю "все время", MySQL совершенно справедливо не использует индексы. Но если я прошу небольшую продолжительность, MySQL наконец использует индексы.

SELECT …      
FROM activity_log JOIN member 
  ON activity_log.member_id = member.id 
LEFT JOIN (select * from order where order.ts > [start time] and order.ts < [end time + 3600]) orders
  ON order.ts >= activity_log.ts
    AND order.ts <= activity_log.ts + 3600
    AND activity_log.member_id = order.sending_member_id
WHERE activity_log.type_id = 1 /* Search */
AND activity_log.ts > [start time]
AND activity_log.ts < [end time]
AND order.sending_member_id IS NULL
ORDER BY activity_log.member_id, activity_log.ts desc 

Мне не нужен новый столбец индексированных концовок.

Большое спасибо ответившим людям, особенно Денису, чей комментарий привел меня к этому решению. -М

0 голосов
/ 18 мая 2011

Тогда найти случаи, когда член выполнял действие, но не упорядочивал, было бы просто: просто найдите значение NULL (или какое-либо другое значение по умолчанию) в этом столбце таблицы заказов. Опять же, увы, это невозможно в моей ситуации ...

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

Если не пересматривать вашу схему (что, как вы правильно диагностировали, является правильным способом продолжения), вашим лучшим вариантом будет анти-объединение (т. Е. Левое соединение ... где ноль).

Без каких-либо ограничений (скажем, для подмножества недавних дат заказа / активности) вы в основном объединяете две таблицы (в вашем случае три, но я подозреваю, что вы можете отбросить одну на участниках).

Лучшее, что вы можете сделать, это ограничить себя подмножеством временных меток в журналах заказов и операций. Это должно позволить вам уменьшить размер соединения. Чтобы это произошло, вам может потребоваться ввести индексы с несколькими столбцами с ограничением слева и предложением соединения справа, например, Activity_log (ts, member_id) и заказы (ts, member_id) или со столбцами наоборот в зависимости от ваших данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...