优化连接,求和,子查询

时间:2017-12-12 02:43:13

标签: mysql sql

我正在为一个研究项目构建一个Tinder克隆,我试图在概念上做一些非常简单的事情,但看起来我的请求实在太重了。

数据结构

我创建了this simple fiddle来可视化数据库结构 我试图将索引放在user.gender * user.orientation match.user1 match.user2 match.createdAt SELECT total_sum, userId FROM ( SELECT u.id as userId, u.orientation as userOrientation, u.gender as userGender, m1.sum1, m2.sum2, (m1.sum1 + m2.sum2) AS total_sum FROM user u INNER JOIN ( SELECT user1, COUNT(user1) as sum1 FROM `match` WHERE createdAt > DATE('2017-12-11 00:00:00') GROUP BY user1 ) m1 ON m1.user1 = u.id INNER JOIN ( SELECT user2, COUNT(user1) as sum2 FROM `match` WHERE createdAt > DATE('2017-12-11 00:00:00') GROUP BY user2 ) m2 ON m2.user2 = u.id WHERE u.gender IN ('female') AND u.orientation IN ('hetero', 'bi') AND u.lastLogin > 1512873464582 ) as total WHERE total_sum < 4 ORDER BY total_sum ASC LIMIT 8 上,但没有运气。

预期结果

我希望根据性别,方向,lastLogin和日期日期找到匹配次数较少的人。
用户在24小时内不会超过4场比赛,所以我会在过去24小时内找到&lt; = 3场比赛的用户。

下面的值是硬编码的,以便于编辑请求,因为我现在没有花时间做这部分。

匹配由2个用户(user1和user2)组成 同一天的4场比赛的限制是它们显示为user1和user2的总和。

user2

问题

使用小型表,请求需要几毫秒但是使用中型表(50k用户,200k匹配),请求需要很长时间(170秒)。

优化

根据 @Thorsten Kettner 响应,这是我在设置他建议的索引后运行到我的测试数据库中时的explain plan请求:

解决方案

我最终做得更轻松了 首先,我通过删除(SELECT u.id, mc.id as nb_match, u.gender, u.orientation FROM user u LEFT JOIN match_composition mc ON (mc.matchedUser = u.id AND mc.createdAt > DATE('2017-12-11 00:00:00')) WHERE u.lastLogin > 1512931740721 AND u.orientation IN ('bi', 'hetero') AND u.gender IN ('female') AND mc.id IS NULL ORDER BY u.lastLogin DESC) UNION ALL (SELECT u.id, count(mc.id) as nb_match, u.gender, u.orientation FROM match_composition mc JOIN user u ON u.id = matchedUser WHERE mc.createdAt > DATE('2017-12-11 00:00:00') AND u.lastLogin > 1512931740721 AND u.orientation IN ('bi', 'hetero') AND u.gender IN ('female') GROUP BY matchedUser ORDER BY nb_match ASC LIMIT 8) 列来提升我的匹配表。它的大小加倍,因为现在1个匹配变为2行但允许我使用适当的索引做一些非常简单且非常有效的事情 第一个查询是管理没有匹配的用户,第二个查询是用来处理匹配的用户。我不再将matchesLimit放入查询中,因为它为mysql添加了额外的工作,我只需要检查第一个结果,看看matchNumber是否为&lt; = 3。

{{1}}

感谢您的帮助

3 个答案:

答案 0 :(得分:2)

我猜你的SQL技能是正确的。这就是我想出的:

SELECT u.id as userId, 
       u.orientation as userOrientation, 
       u.gender as userGender, 
       count(m.user1) total_sum
FROM user u
LEFT JOIN `match` m on (u.id in (m.user1, m.user2) 
                        and m.createdAt > DATE('2017-12-11 00:00:00'))
WHERE u.gender IN ('female')
  AND u.orientation IN ('hetero', 'bi')
  AND u.lastLogin > 1512873464582
having count(m.user1) <=4
ORDER BY total_sum ASC
LIMIT 8;

编辑:还包括没有匹配的案例

尝试使用索引匹配表格列 user1,user1 以及您使用的用户表格列(或列组合)过滤器(例如性别),看看会带来更好的性能。

答案 1 :(得分:2)

用户可以匹配为user1或user2。我们可以使用UNION ALL为每位用户创建一条记录:

select user1 as userid from match union all select user2 as userid from match;

完整的查询:

select
  u.id as userid,
  coalesce(um.total, 0) as total
from user u
left join
(
  select userid, count(*) as total
  from 
  (
    select user1 as userid from match where createdat > date '2017-12-11'
    union all 
    select user2 as userid from match where createdat > date '2017-12-11'
  ) m
  group by userid
) um on um.userid = u.id
where u.gender IN ('female')
  and u.orientation in ('hetero', 'bi')
  and u.lastlogin > 1512873464582
  and coalesce(um.total, 0) < 4
order by coalesce(um.total, 0);

您可以使用以下索引:

create index idx_m1 on match (createdat, user1);
create index idx_m2 on match (createdat, user2);
create index idx_u on user (lastlogin, gender, orientation, id);

答案 2 :(得分:0)

根据您提供的内容,我将创建索引:
- match.user1
- match.user2
- match.createdAt
- user.id(唯一的,可能是PK) - user.lastLogin

我也会尝试用COUNT(*)替换COUNT(user1),但它可能不会产生很大的影响。

user.gender user.orientation 上的索引可能毫无用处:索引的效率在某种程度上与其基础值的方差成正比。因此,具有2-3个不同值的字段的索引比使用更昂贵。

对于DLL,请尝试以下操作。我尝试在user加入之前强制进行过滤,以防查询优化器无法正常工作(我对非MS数据库的经验不足)

match