简体   繁体   English

MySQL - 有效地将两个select语句组合成一个结果和LIMIT

[英]MySQL - Combining two select statements into one result with LIMIT efficiently

For a dating application, I have a few tables that I need to query for a single output with a LIMIT 10 of both queries combined. 对于约会应用程序,我有几个表,我需要查询单个输出,两个查询的LIMIT 10组合。 It seems difficult to do at the moment, even though it's not an issue to query them separately, but the LIMIT 10 won't work as the numbers are not exact (ex. not LIMIT 5 and LIMIT 5, one query may return 0 rows, while the other 10, depending on the scenario). 此刻似乎很难做到,即使单独查询它们也不是问题,但LIMIT 10将无法​​工作,因为数字不准确(例如,不是LIMIT 5和LIMIT 5,一个查询可能返回0行,而另外10个,视情况而定)。

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 

Imagine you are Karen and just logged in, you should see these 2 items: 想象一下你是凯伦并且刚刚登录,你应该看到这两个项目:

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

In one query with a LIMIT of 10. Instead here are two queries that need to be combined: 在一个LIMIT为10的查询中。相反,这里有两个需要组合的查询:

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

Again, sometimes both tables may be empty, or 1 table may be empty, or both full (where LIMIT 10 kicks in) and ordered by time. 同样,有时两个表可能都是空的,或者一个表可能是空的,或者两个表都是空的(LIMIT 10可以启动)并按时间排序。 Any ideas on how to get a query to perform this task efficiently? 有关如何有效执行此任务的查询的任何想法? Thoughts, advice, chimes, optimizations are welcome. 欢迎提出想法,建议,编钟和优化。

You can combine multiple queries with UNION , but only if the queries have the same number of columns. 您可以将多个查询与UNION ,但UNION是查询具有相同的列数。 Ideally the columns are the same, not only in data type, but also in their semantic meaning; 理想情况下,列不仅在数据类型中,而且在于它们的语义含义; however, MySQL doesn't care about the semantics and will handle differing datatypes by casting up to something more generic - so if necessary you could overload the columns to have different meanings from each table, then determine what meaning is appropriate in your higher level code (although I don't recommend doing it this way). 但是,MySQL并不关心语义,并且会通过强制转换为更通用的东西来处理不同的数据类型 - 因此,如果有必要,您可以重载列以使每个表具有不同的含义,然后确定更高级代码中的含义是什么(虽然我不建议这样做)。

When the number of columns differs, or when you want to achieve a better/less overloaded alignment of data from two queries, you can insert dummy literal columns into your SELECT statements. 当列数不同时,或者想要从两个查询中实现更好/更少重载的数据对齐时,可以在SELECT语句中插入虚拟文本列。 For example: 例如:

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

You could even have some columns reserved for the first table and others for the second table, such that they are NULL elsewhere (but remember that the column names come from the first query, so you may wish to ensure they're all named there): 您甚至可以为第一个表保留一些列,为第二个表保留其他列,这样它们在其他地方是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;

You could try aligning your two queries in this fashion, then combining them with a UNION operator; 您可以尝试以这种方式对齐您的两个查询,然后将它们与UNION运算符组合; by applying LIMIT to the UNION , you're close to achieving your goal: 通过将LIMIT应用于UNION ,您即将实现目标:

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

The only issue that remains is that, as presented above, 10 or more records from the first table will "push out" any records from the second. 剩下的唯一问题是,如上所述,来自第一个表的10个或更多记录将“推出”来自第二个表的任何记录。 However, we can utilise an ORDER BY in the outer query to solve this. 但是,我们可以在外部查询中使用ORDER BY来解决此问题。

Putting it all together: 把它们放在一起:

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

Of course, now it's up to you to determine what type of row you're dealing with as you read each record in the resultset (suggest you test request_id and/or alert_id for NULL values; alternatively one could add an additional column to the results that explicitly states from which table each record originated, but it should be equivalent provided those id columns are NOT NULL ). 当然,现在由您决定在读取结果集中的每条记录时确定要处理的行类型(建议您对NULL值测试request_id和/或alert_id ;或者可以在结果中添加一个额外的列它明确说明每个记录来自哪个表,但它应该是等效的,只要那些id列是NOT NULL )。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM