2012-04-25 205 views
8

對於約會應用程序,我有幾張表,我需要查詢兩個查詢的LIMIT 10組合的單個輸出。目前看起來很難做到,儘管分開查詢它們並不是一個問題,但LIMIT 10將不起作用,因爲數字並不準確(例如,不是限制5和限制5,一個查詢可能返回0行,而另外10個,取決於情景)。MySQL - 將兩條select語句有效地組合到一個結果中

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 

想象一下,你是Karen和剛剛登錄,您應該看到這些2項:

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

在一個查詢與10 LIMIT相反這裏是需要結合兩個查詢:

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"; 

同樣,有時兩個表可以是空的,或1個表可以是空的,或兩者充分(其中在LIMIT 10個踢)和按時間排序。關於如何使查詢有效執行此任務的任何想法?想法,建議,編鐘,優化是值得歡迎的。

+1

如果這兩個查詢返回的列是相同的,請使用['UNION'](http://dev.mysql.com/doc/refman/5.6/en/union.html)加入它們並使整個對執行「LIMIT」的外部查詢提供子查詢。否則,可以通過(10減去第一個查詢返回的記錄數)確定必需的「LIMIT」,以應用於第二個查詢 - 可能最容易的方法是使用您用來調用查詢的任何語言。 – eggyal 2012-04-25 21:52:24

+0

用您的預期結果創建一個表格。你會在那裏看到問題。 – 2012-04-25 22:05:43

+0

無法將2個查詢與不同的選擇列表組合在一起。 – vyegorov 2012-04-25 22:08:56

回答

17

您可以將多個查詢與UNION結合使用,但前提是查詢的列數相同。理想情況下,列是相同的,不僅在數據類型中,而且在語義上也是如此;然而,MySQL並不關心語義,並會通過強制轉換爲更通用的方式來處理不同的數據類型 - 因此,如果需要,您可以將列重載以使每個表具有不同的含義,然後確定在更高版本中適當的含義(儘管我不建議這樣做)。

當列數不同時,或者當您希望從兩個查詢中獲得更好/更少重載的數據對齊時,可以將虛擬文字列插入到SELECT語句中。例如:

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

你可能甚至已經預留了第一臺和其他第二表中的某些列,使得它們NULL其他地方(但請記住,列名來自第一個查詢,所以你可能希望確保他們都命名爲那裏):

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; 

你可以嘗試對準以這種方式你兩個查詢,然後將它們與UNION操作相結合;通過應用LIMITUNION,你就接近實現你的目標:

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

剩下的是,如上所,從第一臺10點或更多的記錄將「推出」的任何記錄的唯一問題從第二個。但是,我們可以在外部查詢中使用ORDER BY來解決此問題。

全部放在一起:

(
    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; 

當然,現在它是由你來決定你處理,你在結果讀取每個記錄什麼類型的列(建議你測試request_id和/或者alert_id,NULL值;或者可以向結果添加額外的列,其明確指出每個記錄來自哪個表,但是它應該是等效的,只要這些列是NOT NULL)。

+0

感謝您的示例和解釋eggyal。行檢索背後的邏輯位於ORDER BY子句中,請求時間和警報時間依次插入不同的表中。所以從技術上講,很可能有第一張表中的3條記錄,然後是第二張表中的2條記錄,然後在表之間來回地記錄1條記錄,直到達到限制10。 – Wonka 2012-04-25 23:09:19

+0

@Wonka:聽起來好像你應該可以在外部查詢中用'ORDER BY'來實現這一點 - 讓我知道你是否無法弄清楚。 – eggyal 2012-04-25 23:10:05

+0

你的意思是ORDER BY [time_here] LIMIT 10?那裏面的查詢呢,只需要刪除ORDER BY dr.request_time LIMIT 5和ORDER BY da.alert_time LIMIT 5?你能告訴我什麼最終查詢將與我的查詢看起來像,所以我有信心? – Wonka 2012-04-25 23:23:22