2011-12-23 1 views
3

У меня есть две таблицы - одна называется customer_records, а другая - customer_actions.Эффективность запросов (несколько выборок)

customer_records имеет следующую схему:

CustomerID (auto increment, primary key) 
CustomerName 
...etc... 

customer_actions имеет следующую схему:

ActionID (auto increment, primary key) 
CustomerID (relates to customer_records) 
ActionType 
ActionTime (UNIX time stamp that the entry was made) 
Note (TEXT type) 

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

Что я хочу сделать, это отобразить список записей от customer_records, где последнее значение ActionType было определенным значением.

До сих пор я искал сеть/SO и придумать с этим монстром:

SELECT * FROM (
    SELECT * FROM (
     SELECT * FROM `customer_actions` ORDER BY `EntryID` DESC 
    ) list1 GROUP BY `CustomerID` 
) list2 WHERE `ActionType`='whatever' LIMIT 0,30 

который является большим - это перечисляет каждый идентификатор клиента и их последнее действие. Но запрос чрезвычайно медленный на случаи (примечание: есть около 20 000 записей в customer_records). Может ли кто-нибудь предложить какие-либо советы о том, как я могу сортировать этого монстра запроса или настроить мой стол, чтобы дать более быстрые результаты? Я использую MySQL. Любая помощь действительно ценится, спасибо.

Редактировать: Чтобы быть ясным, мне нужно увидеть список клиентов, последнее действие которого было «что угодно».

+0

Означает ли это, что, если последнее действие (по ActionDate) клиента не является «не», «то», то этот клиент не должен быть указан? И для клиентов, которые * указаны *, вы просто хотите, чтобы клиент, а также их последнее действие, а также все их действия? – MatBailie

+0

@ Dems Если последний «ActionType» для клиента не «все равно», то они не должны отображаться. Для тех, у кого есть последний «ActionType» из «любой», мне понадобится их информация из 'customer_records', но это не обязательно нужно рассматривать в одном запросе. – Jonathon

ответ

5

для фильтрации клиентов их последнего действия, вы могли бы использовать коррелированный подзапрос ...

SELECT 
    * 
FROM 
    customer_records 
INNER JOIN 
    customer_actions 
    ON customer_actions.CustomerID = customer_records.CustomerID 
    AND customer_actions.ActionDate = (
      SELECT 
      MAX(ActionDate) 
      FROM 
      customer_actions AS lookup 
      WHERE 
      CustomerID = customer_records.CustomerID 
     ) 
WHERE 
    customer_actions.ActionType = 'Whatever' 

вы можете найти его более эффективным, чтобы избежать корреляционную суб- запрос следующим образом:

SELECT 
    * 
FROM 
    customer_records 
INNER JOIN 
    (SELECT CustomerID, MAX(ActionDate) AS ActionDate FROM customer_actions GROUP BY CustomerID) AS last_action 
    ON customer_records.CustomerID = last_action.CustomerID 
INNER JOIN 
    customer_actions 
    ON customer_actions.CustomerID = last_action.CustomerID 
    AND customer_actions.ActionDate = last_action.ActionDate 
WHERE 
    customer_actions.ActionType = 'Whatever' 
+0

Спасибо - первый запрос вернул ожидаемые результаты (фантастически!), Но взял * 445 секунд *, чтобы сделать так: - | ! Второй запрос возвратил пустой набор. – Jonathon

+0

@JonnyKeogh - Два опечатанных там, теперь исправлены. * [У вас есть индекс покрытия для customer_actions (CustomerID, ActionDate)?] * – MatBailie

+0

Это сработало, спасибо огромное! – Jonathon

2

Я не уверен, что я понимаю требования, но мне кажется, что для этого будет достаточно JOIN.

SELECT cr.CustomerID, cr.CustomerName, ... 
FROM customer_records cr 
     INNER JOIN customer_actions ca ON ca.CustomerID = cr.CustomerID 
WHERE `ActionType` = 'whatever' 
ORDER BY 
     ca.EntryID 

Обратите внимание, что 20.000 записей не должны создавать проблемы с производительностью

+0

В дополнение к вышесказанному, если вы перефакторизуете это, я настоятельно рекомендую отказаться от * и использовать только те имена столбцов, которые вам нужны напрямую (выбор * - большой удар производительности) – Codingo

+0

@Codingo - Вы прямо противоположны. Запрос исправлен. –

+0

В этом запросе выбираются все клиенты, которые в какой-то момент выполняли «что угодно», но мне нужно увидеть клиентов, где последнее действие, выполненное в их записи, было «независимо». Извините, я был не совсем ясен в своем оригинальном посте. – Jonathon

2

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

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

select top 100 -- Change as required 
     cr.CustomerID , 
     cr.CustomerName, 
     cr.MoreDetail1, 
     cr.Etc 
from customer_records cr 
     inner join customer_actions ca 
      on ca.CustomerID = cr.CustomerID 
where ca.ActionType = 'x' 
order by cr.CustomerID 

Несколько замечаний:

  • В некоторых случаях я считаю левых внешних соединений, чтобы быть быстрее, внутренние соединения. Было бы полезно измерить производительность как для этого запроса.
  • Избегайте возврата * по возможности
  • Вам не нужно ссылаться на 'c р.x 'в первоначальном выборе, но это хорошая привычка вступать в дело, когда вы начинаете работу над большими запросами, которые могут иметь несколько объединений в них (это будет иметь большой смысл после того, как вы начнете делать это
  • При использовании объединений всегда присоединиться на первичный ключ
+0

'TOP 100' должен' Limit 100', но я бы предположил, что OP может это исправить :) Но для * всегда * присоединения к Первичному ключу? На самом деле я не согласен. Если таблица «actions» имеет суррогатный первичный ключ, функциональность, требуемая OP, по-прежнему потребует объединения полей, не входящих в основной ключ. Я бы сказал, что, поскольку MySQL делает основной ключ кластеризованным индексом, есть реальные преимущества в этом, но если ваша логика означает, что вы не можете, убедитесь, что у вас есть соответствующий индекс. – MatBailie

+0

Очень действительные баллы - теперь мы будем больше думать об этом. Большое спасибо. – Codingo

1

Возможно, мне что-то не хватает, но что не так с простым объединением и предложением where?

Select ActionType, ActionTime, Note 
FROM Customer_Records CR 
INNER JOIN customer_Actions CA 
    ON CR.CustomerID = CA.CustomerID 
Where ActionType = 'added case info' 
+0

OP хочет список клиентов, где их последняя запись в таблице действий является конкретным действием. «Последняя запись в таблице действий» - это та часть, которая здесь отсутствует. * [Но я должен был проверить с комментарием, чтобы быть уверенным.] * – MatBailie

+0

А, да, я перечитываю OP. «То, что я хочу сделать, - это отобразить список записей из customer_records, где последний ActionType был определенным значением». это подразумевало мой ответ: то, что я пропустил позже, «он перечисляет каждый идентификатор клиента и их последнее действие», – xQbert