2012-01-23 52 views
8

Tôi đang cố gắng hiển thị danh sách các bản ghi thành viên và tôi có một vài bảng tôi đang sử dụng để hiển thị những gì tôi cần.Truy vấn con MySQL - Chỉ tìm bản ghi đầu tiên trong LEFT JOIN

Đó là phần dễ dàng. Phần tôi cần trợ giúp là với một bảng có nhiều bản ghi cho từng thành viên: Lịch sử đăng nhập

Tôi muốn chỉ hiển thị hàng đầu tiên cho mỗi bản ghi thành viên tồn tại trong bảng Lịch sử Đăng nhập. Ngoài ra, tôi có thể muốn lật flop và hiển thị bản ghi cuối cùng trong bảng Lịch sử Đăng nhập.

đây là những gì tôi đã có cho đến nay:

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 

Vì vậy mà trả về những gì đang mong đợi.

2 cột từ tbl_members_login_history Tôi muốn thêm vào kết quả trả về là: mh. loggedtime, mh. ipaddy

tôi biết thêm tbl_members_login_history như một LEFT JOIN sẽ quay trở lại bản sao, vì vậy tôi nghĩ phải có một điều cần thiết Subquery đây, để trở lại chỉ là kỷ lục 1 cho rằng memberid tồn tại trong tbl_members_login_history.

Điều tôi lo lắng là nếu không có bản ghi trong bảng lịch sử tồn tại, tôi vẫn muốn hiển thị thông tin thành viên đó, nhưng để cột lịch sử là NULL.

Đây có phải là sự cố truy vấn phụ không? và nếu có, làm cách nào để thêm loại LIMIT đó?

+0

Ngoài sự tò mò, điện thoại và địa chỉ có cần thiết cho thành viên không? Nếu không, tôi không nghĩ rằng truy vấn của bạn sẽ trả lại thông tin thành viên khác nếu một trong số họ thiếu cách viết. – Sam

+0

Trong trường hợp này, có. Nhưng bạn nói đúng, thành viên sẽ không được trả lại trong kết quả mà không có một bản ghi hiện có. – coffeemonitor

Trả lời

20

Đây là vấn đề greatest-n-per-group, được hỏi thường xuyên trên Stack Overflow.

Đây là cách tôi sẽ giải quyết nó trong kịch bản của bạn:

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; 

Đó là, chúng tôi muốn mh là hàng gần đây nhất trong tbl_member_login_history cho memberid nhất định. Vì vậy, chúng tôi tìm kiếm một hàng khác mh2 thậm chí còn mới hơn. Nếu không tìm thấy hàng nào gần đây hơn hàng mh thì mh2.* sẽ là NULL, vì vậy mh phải là hàng gần đây nhất.

Tôi giả định rằng bảng này có cột khóa chính chứa giá trị ngày càng tăng. Trong ví dụ này, tôi giả sử tên cột là pk.

Sử dụng LEFT OUTER JOIN cho cả hai tham chiếu vào bảng lịch sử đăng nhập có nghĩa là hàng m sẽ được báo cáo ngay cả khi không có hàng phù hợp.

+0

Tôi vừa thử giải pháp của bạn và so sánh nó với truy vấn chứa truy vấn con bằng cách sử dụng 'GROUP BY' với' MAX (...) ', tham gia' ON date = maxdate'.Giải pháp của bạn với toán tử '<' mất 2s (!), Trong khi giải pháp truy vấn con mất 0,01s (cùng một bảng kết quả của khóa học). 'EXPLAIN' của truy vấn của bạn trông đẹp hơn nhiều so với truy vấn con. Tôi không biết tại sao tôi lại có được sự khác biệt lớn về hiệu năng này, nhưng tôi đoán đó là vì '<' tạo ra một kết quả trung gian rất lớn. – steffen

+0

@steffen: Vâng, vì tôi đã trả lời câu hỏi này, tôi đã thấy rất nhiều trường hợp khác và kết luận rằng giải pháp trên hoặc giải pháp như bạn mô tả * có thể * nhanh hơn, tùy thuộc vào số lượng nhóm khác nhau và số lượng các hàng trên mỗi nhóm dữ liệu có. Vì vậy, tốt nhất là kiểm tra cả hai phương pháp với dữ liệu của bạn và sau đó quyết định. –

+0

True. Điều làm tôi bối rối là sự khác biệt về hiệu suất rất lớn. Tôi tìm thấy giải pháp của bạn một vài lần ở đây trên SO. Và đầu ra 'EXPLAIN' trông tuyệt vời nên tôi mong đợi truy vấn đó rất nhanh. Nhưng thay vào đó nó chậm hơn rất nhiều. Kỳ dị. – steffen

0

thêm như

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

PS này. LAST_LOGIN_DATE là cột giả, bạn có thể thử cột cố định

Các vấn đề liên quan