2012-04-25 100 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