2012-01-23 1 views
8

Я пытаюсь отобразить список записей элементов, и у меня есть несколько таблиц, которые я использую для отображения того, что мне нужно.Подзапрос MySQL - найти только первую запись в LEFT JOIN

Это легкая часть. Часть, в которой мне нужна помощь, - это таблица с множеством записей для каждой записи участника: История входа

Я хочу отобразить только первую строку для каждой записи участника, которая существует в таблице История входа. В качестве альтернативы, я, возможно, захочу перевернуть флоп и отобразить последнюю запись в таблице «История входа».

вот что у меня до сих пор:

SELECT m.memberid, m.membername, m.gender, mp.phone 
FROM tbl_members m, 
    tbl_members_phones mp, 
    tbl_members_addresses ma 
WHERE m.defaultphoneid = mp.phoneid 
AND m.defaultaddressid = ma.addressid 

Так что возвращает то, что ожидается.

2 колонки из tbl_members_login_history Я хочу добавить к получившимся результатом: mh. loggedtime, mh. ipaddy

Я знаю, добавив tbl_members_login_history как LEFT JOIN будет возвращать дубликаты, поэтому я думаю, что должна быть Subquery необходимости здесь, чтобы вернуться только 1-й рекордом для этого memberid, которая существует в tbl_members_login_history.

Что меня беспокоит, так это отсутствие записи в таблице истории, я все же хочу отображать эту информацию о члене, но оставляю столбцы истории как NULL.

Будет ли это инцидентом с подзапросом? и если да, то как добавить такой тип LIMIT?

+0

Из любопытства, являются ли телефон и адрес обязательным для пользователя? Если нет, я не думаю, что ваш запрос вернет информацию о другом члене, если один из них не знает, как он написан. – Sam

+0

В этом случае да. Но вы правы, член не будет возвращен в результате без существующей записи. – coffeemonitor

ответ

20

Это проблема greatest-n-per-group, о которой часто спрашивают о переполнении стека.

Вот как я бы решить в вашем сценарии:

SELECT m.memberid, m.membername, m.gender, mp.phone, mh.loggedtime, mh.ipaddy 
FROM tbl_members m 
INNER JOIN tbl_members_phones mp ON m.defaultphoneid = mp.phoneid 
INNER JOIN tbl_members_addresses ma ON m.defaultaddressid = ma.addressid 
LEFT OUTER JOIN tbl_members_login_history mh ON m.memberid = mh.memberid 
LEFT OUTER JOIN tbl_members_login_history mh2 ON m.memberid = mh2.memberid 
    AND mh.pk < mh2.pk 
WHERE mh2.pk IS NULL; 

То есть, мы хотим mh быть самым последним в ряду tbl_member_login_history для данного MemberId. Поэтому мы ищем еще одну строку mh2, которая является еще более поздней. Если не найдено ни одной новой строки, кроме строки mh, то mh2.* будет NULL, поэтому mh должен быть самым последним.

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

Использование LEFT OUTER JOIN для как ссылки на таблицу истории Логина означает, что m строка будет сообщена, даже если нет соответствующей строки.

+0

Я просто попробовал ваше решение и сравнил его с запросом, содержащим подзапрос, используя 'GROUP BY' с' MAX (...) ', присоединяя' ON date = maxdate'.Ваше решение с оператором '<' заняло 2s (!), А решение подзапроса заняло 0,01 с (например, таблица результатов). 'EXPLAIN' вашего запроса выглядит намного лучше, чем тот, который находится в подзапросе. Я не знаю, почему я получаю эту огромную разницу в производительности, но я думаю, это потому, что '<' создает огромный промежуточный результат. – steffen

+0

@steffen: Да, поскольку я ответил на этот вопрос, я видел много других случаев и пришел к выводу, что либо решение выше, либо решение, как вы описываете *, может быть быстрее, в зависимости от того, сколько отдельных групп и сколько строк на группу данных. Поэтому лучше всего протестировать оба метода с вашими данными, а затем решить. –

+0

Правда. Что меня смутило, так это огромная разница в производительности. Я нашел ваше решение пару раз здесь, на SO. И вывод 'EXPLAIN' выглядит великолепно, поэтому я ожидал, что запрос будет очень быстрым. Но вместо этого это было намного медленнее. Weird. – steffen

0

добавить как этот

LEFT OUTER JOIN (SELECT member_id, MAX(LAST_LOGIN_DATE) from tbl_members_login_history) Last_Login ON Last_Login.memberid = m.memberid 

PS. LAST_LOGIN_DATE - псевдо-столбец, вы можете попробовать свой отзывный столбец