2012-04-25 55 views
8

Đối với ứng dụng hẹn hò, tôi có một vài bảng cần truy vấn cho một đầu ra duy nhất với LIMIT 10 của cả hai truy vấn được kết hợp. Có vẻ như khó có thể thực hiện vào lúc này, mặc dù không phải là vấn đề để truy vấn chúng một cách riêng biệt, nhưng LIMIT 10 sẽ không hoạt động vì các con số không chính xác (ví dụ: không phải LIMIT 5 và LIMIT 5, một truy vấn có thể trả về 0 hàng , trong khi 10 khác, tùy thuộc vào kịch bản).MySQL - Kết hợp hai câu lệnh chọn thành một kết quả với LIMIT hiệu quả

members table 
member_id | member_name 
------------------------ 
    1   Herb 
    2   Karen 
    3   Megan 

dating_requests 
request_id | member1 | member2 | request_time 
---------------------------------------------------- 
    1   1   2  2012-12-21 12:51:45 

dating_alerts 
alert_id | alerter_id | alertee_id | type | alert_time 
------------------------------------------------------- 
    5   3   2  platonic 2012-12-21 10:25:32 

dating_alerts_status 
status_id | alert_id | alertee_id | viewed | viewed_time 
----------------------------------------------------------- 
    4   5   2   0  0000-00-00 00:00:00 

Hãy tưởng tượng bạn là Karen và chỉ cần đăng nhập, bạn sẽ thấy những 2 hạng mục:

1. Herb requested a date with you. 
2. Megan wants a platonic relationship with you. 

Trong một truy vấn với một LIMIT của 10 Thay vì đây là hai câu hỏi cần phải được kết hợp:

1. Herb requested a date with you. 
    -> query = "SELECT dr.request_id, dr.member1, dr.member2, m.member_name 
       FROM dating_requests dr 
       JOIN members m ON dr.member1=m.member_id 
       WHERE dr.member2=:loggedin_id 
       ORDER BY dr.request_time LIMIT 5"; 
2. Megan wants a platonic relationship with you. 
    -> query = "SELECT da.alert_id, da.alerter_id, da.alertee_id, da.type, 
         da.alert_time, m.member_name 
       FROM dating_alerts da 
       JOIN dating_alerts_status das ON da.alert_id=das.alert_id 
        AND da.alertee_id=das.alertee_id 
       JOIN members m ON da.alerter_id=m.member_id 
       WHERE da.alertee_id=:loggedin_id AND da.type='platonic' 
        AND das.viewed='0' AND das.viewed_time<da.alert_time 
       ORDER BY da.alert_time LIMIT 5"; 

Một lần nữa, đôi khi cả hai bảng có thể trống hoặc 1 bảng đầy đủ (trong đó LIMIT 10 bắt đầu) và sắp xếp theo thời gian. Bất kỳ ý tưởng về cách để có được một truy vấn để thực hiện nhiệm vụ này một cách hiệu quả? Suy nghĩ, lời khuyên, chuông, tối ưu hóa được hoan nghênh.

+1

Nếu các cột được trả về bởi hai truy vấn giống nhau, hãy nối chúng với ['UNION'] (http://dev.mysql.com/doc/refman/5.6/en/union.html) và tạo toàn bộ điều truy vấn con đến truy vấn bên ngoài thực hiện 'LIMIT'. Nếu không, bạn có thể xác định 'LIMIT' cần thiết để áp dụng cho truy vấn thứ hai từ (10 trừ số lượng bản ghi được trả về bởi truy vấn đầu tiên) - có lẽ dễ nhất để làm điều này bằng bất kỳ ngôn ngữ nào bạn đang sử dụng để gọi truy vấn. – eggyal

+0

Tạo bảng với kết quả mong đợi của bạn. Bạn sẽ thấy vấn đề ở đó. –

+0

Không thể kết hợp 2 truy vấn với các danh sách lựa chọn khác nhau. – vyegorov

Trả lời

17

Bạn có thể kết hợp nhiều truy vấn với UNION, nhưng chỉ khi các truy vấn có cùng số cột. Lý tưởng nhất là các cột giống nhau, không chỉ trong kiểu dữ liệu, mà còn theo nghĩa ngữ nghĩa của chúng; tuy nhiên, MySQL không quan tâm đến ngữ nghĩa và sẽ xử lý các kiểu dữ liệu khác nhau bằng cách đúc lên một cái gì đó chung chung hơn - vì vậy nếu cần bạn có thể quá tải các cột có ý nghĩa khác nhau từ mỗi bảng, sau đó xác định ý nghĩa nào là phù hợp cấp mã (mặc dù tôi không khuyên bạn nên làm theo cách này).

Khi số lượng cột khác nhau hoặc khi bạn muốn đạt được sự liên kết dữ liệu tốt hơn/ít bị quá tải từ hai truy vấn, bạn có thể chèn các cột chữ giả vào câu lệnh SELECT của mình. Ví dụ:

SELECT t.cola, t.colb, NULL, t.colc, NULL FROM t; 

Bạn có thể thậm chí có một số cột dành cho bảng đầu tiên và những người khác cho bảng thứ hai, chẳng hạn rằng họ là NULL nơi khác (nhưng hãy nhớ rằng các tên cột đến từ các truy vấn đầu tiên, vì vậy bạn có thể muốn đảm bảo tất cả họ đang được đặt tên có):

SELECT a, b, c, d, NULL AS e, NULL AS f, NULL AS g FROM t1 
UNION ALL -- specify ALL because default is DISTINCT, which is wasted here 
    SELECT NULL, NULL, NULL, NULL, a, b, c FROM t2; 

bạn có thể thử sắp xếp hai truy vấn của bạn trong thời trang này, sau đó kết hợp chúng với một nhà điều hành UNION; bằng cách áp dụng LIMIT đến UNION, bạn gần đạt được mục tiêu của bạn:

(SELECT ...) 
UNION 
    (SELECT ...) 
LIMIT 10; 

Vấn đề duy nhất còn lại là, như đã trình bày ở trên, 10 hoặc nhiều bản ghi từ bảng đầu tiên sẽ "đẩy ra" bất cứ hồ sơ từ giây. Tuy nhiên, chúng tôi có thể sử dụng một số ORDER BY trong truy vấn bên ngoài để giải quyết vấn đề này.

Đưa nó tất cả cùng nhau:

(
    SELECT 
    dr.request_time AS event_time, m.member_name,  -- shared columns 
    dr.request_id, dr.member1, dr.member2,    -- request-only columns 
    NULL AS alert_id, NULL AS alerter_id,    -- alert-only columns 
     NULL AS alertee_id, NULL AS type 
    FROM dating_requests dr JOIN members m ON dr.member1=m.member_id 
    WHERE dr.member2=:loggedin_id 
    ORDER BY event_time LIMIT 10 -- save ourselves performing excessive UNION 
) UNION ALL (
    SELECT 
    da.alert_time AS event_time, m.member_name,  -- shared columns 
    NULL, NULL, NULL,         -- request-only columns 
    da.alert_id, da.alerter_id, da.alertee_id, da.type -- alert-only columns 
    FROM 
    dating_alerts da 
    JOIN dating_alerts_status das USING (alert_id, alertee_id) 
    JOIN members m ON da.alerter_id=m.member_id 
    WHERE 
    da.alertee_id=:loggedin_id 
    AND da.type='platonic' 
    AND das.viewed='0' 
    AND das.viewed_time<da.alert_time 
    ORDER BY event_time LIMIT 10 -- save ourselves performing excessive UNION 
) 
ORDER BY event_time 
LIMIT 10; 

Tất nhiên, bây giờ nó thuộc vào bạn để xác định những loại hàng bạn đang làm việc với khi bạn đọc mỗi bản ghi trong resultset (đề nghị bạn kiểm tra request_id và/hoặc alert_id cho các giá trị NULL, một cách khác có thể thêm cột bổ sung vào kết quả nêu rõ từ bảng nào mỗi bản ghi bắt nguồn, nhưng nó tương đương với các cột idNOT NULL).

+0

Cảm ơn ví dụ và giải thích eggyal. Logic đằng sau việc truy xuất hàng nằm trong mệnh đề ORDER BY, thời gian yêu cầu và thời gian cảnh báo theo thứ tự khi chúng được chèn vào trong các bảng khác nhau. Vì vậy, về mặt kỹ thuật, rất có thể có 3 bản ghi từ bảng đầu tiên, sau đó 2 bản ghi từ bảng thứ hai, sau đó 1 bản ghi qua lại giữa các bảng cho đến khi đạt đến LIMIT 10. – Wonka

+0

@Wonka: Có vẻ như bạn sẽ có thể đạt được điều này với 'ORDER BY' trong truy vấn bên ngoài - hãy cho tôi biết nếu bạn không thể tìm ra. – eggyal

+0

Ý của bạn là ORDER BY [time_here] LIMIT 10? Điều gì về các truy vấn bên trong, chỉ cần thả ORDER BY dr.request_time LIMIT 5 và ORDER BY da.alert_time LIMIT 5? Bạn có thể cho tôi biết truy vấn cuối cùng sẽ trông như thế nào với các truy vấn của tôi vì vậy tôi tự tin? – Wonka

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