我有一个包含一些用户数据的表:
user_id | guest_id | time_seen | action_performed | longitude | latitude
-------------------------------------------------------------------------
123 | NULL | Jan 10 | search | -127 | 35
152 | NULL | Dec 10 | login | -128 | 34
172 | NULL | Dec 15 | search | -125 | 35
123 | NULL | Jan 10 | login | -127 | 35
NULL | GUEST1 | Jan 10 | search | -127 | 35
NULL | GUEST1 | Dec 10 | search | -127 | 35
NULL | GUEST2 | Jan 10 | browse | -127 | 35
NULL | GUEST3 | Dec 10 | browse | -127 | 35
我需要获取给定时间跨度内唯一用户ID和来宾ID的列表。同一行永远不会同时拥有有效的用户ID和有效的访客ID。我目前使用的查询是:
SELECT *
FROM stats
WHERE time_seen >= "2011-12-1 00:00:00"
AND time_seen < "2012-1-1 00:00:00"
GROUP BY guest_id
UNION
SELECT *
FROM stats
WHERE time_seen >= "2011-12-1 00:00:00"
AND time_seen < "2012-1-1 00:00:00"
GROUP BY user_id;
所以我希望检索:
user_id | guest_id | time_seen | action_performed | longitude | latitude
-------------------------------------------------------------------------
152 | NULL | Dec 10 | login | -128 | 34
172 | NULL | Dec 15 | search | -125 | 35
NULL | GUEST1 | Dec 10 | search | -127 | 35
NULL | GUEST3 | Dec 10 | browse | -127 | 35
真正的表包含了大约1,100万个条目,并且每天都在增长,所以显然我有兴趣使查询尽可能高效。这个查询看起来有点不理想(除非有内部优化),因为我每次都执行相同的查询,之后只需按不同的方式对其进行分组。
有没有办法改善我的查询,或者这是我能做的最好的事情?
回答以下问题:
日期确实有时间戳值,我只是想简化帖子的目的。
user_id值与guest_id值之间没有重叠。
答案 0 :(得分:3)
如果user_id
值和guest_id
值之间没有重叠,那么您可以GROUP BY
这两列中的COALESCE
而不使用UNION
}
答案 1 :(得分:1)
这是我的建议:
SELECT `user_id`, `guest_id`, `time_seen`, `action_performed`, `longitude`, `latitude`
FROM stats
WHERE guest_id IS NOT NULL AND time_seen BETWEEN "2011-12-1 00:00:00" AND "2012-1-1 00:00:00"
GROUP BY guest_id
UNION
SELECT `user_id`, `guest_id`, `time_seen`, `action_performed`, `longitude`, `latitude`
FROM stats
WHERE user_id IS NOT NULL AND time_seen BETWEEN "2011-12-1 00:00:00" AND "2012-1-1 00:00:00"
GROUP BY user_id;
事情发生了变化:
1)明确列出要返回的字段
2)使用BETWEEN而不是2次比较
3)在WHERE子句中添加了guest_id IS NOT NULL
和user_id IS NOT NULL
。这样,在查看用户时,您最终不会将所有来宾分组在一起,反之亦然。
答案 2 :(得分:0)
如果您只需要一个访问者列表,那么:
SELECT DISTINCT COALESCE(user_id, guest_id), longitude, latitude
FROM stats
WHERE time_seen >= "2011-12-1 00:00:00" AND time_seen < "2012-1-1 00:00:00";