2012-01-23 30 views
8

我試圖顯示成員記錄列表,並且我有幾張表格用於顯示我需要的內容。MySQL子查詢 - 在左加入中只查找第一條記錄

這是很容易的部分。我需要幫助的部分是對每個成員記錄都有許多記錄的表格:登錄歷史記錄

我想只顯示登錄歷史記錄表中存在的每個成員記錄的第一行。或者,我可能想翻動翻牌並在登錄歷史記錄表中顯示最後一條記錄。

這裏就是我這麼遠:

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 

所以返回要的是什麼。

tbl_members_login_history我想添加到返回的結果是:mh。 loggedtime,mh。 ipaddy

我知道加入tbl_members_login_history作爲LEFT JOIN將返回重複的,所以我想一定有這裏的子查詢的必要性,以只返回第一個記錄爲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 for 兩個對登錄歷史記錄表的引用表示即使沒有匹配的行,也會報告m行。

+0

我剛剛嘗試過您的解決方案,並將其與包含子查詢的查詢進行比較,該查詢使用帶有MAX(...)''GROUP BY',加入'ON date = maxdate'。使用'<'運算符的解決方案花費了2s(!),而子查詢解決方案花費了0.01s(當然結果表相同)。查詢的'EXPLAIN'看起來比帶有子查詢的更好。我不知道爲什麼我得到這個巨大的性能差異,但我想這是因爲'<'產生了巨大的中間結果。 – steffen

+0

@steffen:是的,因爲我回答了這個問題,所以我見過很多其他案例,並得出結論,上述解決方案或像您所描述的解決方案*可以更快,取決於有多少個不同的組和多少個數據具有的每組行數。所以最好用你的數據測試兩種方法,然後決定。 –

+0

是的。令我困惑的是巨大的性能差異。我在這裏找到了你的解決方案几次。並且'EXPLAIN'輸出看起來很棒,所以我期望查詢速度非常快。但相反,它太慢了。奇怪的。 – 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是僞列,你可以嘗試你的restictive列