我正在为一个研究项目构建一个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}}
感谢您的帮助
答案 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