我有一个事件结果表,我需要为给定的玩家列表获取每个玩家最近的n个事件。
这是在iOS上所以需要快速。我看过很多使用子查询或连接的top-n-group-group解决方案,但即使在macbook pro上,我的100k行数据集运行速度也很慢。到目前为止,我的愚蠢解决方案,因为我只运行最多6个玩家,是要做6个单独的查询。它不是非常慢,但必须有更好的方法,对吧?这是我现在正在做的事情的要点:
results_by_pid = {}
player_ids = [1,2,3,4,5,6]
n_results = 6
for pid in player_ids:
results_by_pid[pid] = exec_sql("SELECT *
FROM results
WHERE player_id = #{pid}
ORDER BY event_date DESC
LIMIT n_events")
然后我继续我的快乐方式。但是,如何将其转换为单个快速查询?
答案 0 :(得分:1)
这不是一个很好的答案,但是这里......
我发现制作东西非常快,可能涉及数据和模式本身的思想。例如,搜索有序列表比搜索无序列表要快,但您必须预先支付成本 - 无论是在设计还是执行中。
请问问自己,您的数据是否有任何自然分区可能会减少SQLite必须搜索的记录数量。您可能会询问最新的n个事件是否属于特定时间段。这些都是过去七天吗?最后一个月?如果是这样,那么您可以构建查询以在执行更复杂的搜索之前排除整个数据块。
此外,如果你无法快速完成工作,你可以考虑用户体验! Soooooo很多工程师都没有聪明的用户体验。您的查询是否会作为视图控制器推送的结果运行?然后在PREVIOUS视图控制器中设置后台线程中的内容,并在iOS动画时让它工作。推动画需要多长时间? .2秒?您的用户在什么时候向应用程序(通过某些UX控件)指示哪些playerids
将被查询?只要他触摸该按钮或TVCell,您就可以预取一些数据。因此,如果您需要做的总工作是 O(n log n),这意味着您可以将其分解为 O(n)和 O( log n)件。
在我避免做自己的努力工作的时候,我只想了一些想法。
更多想法
包含前n个插入的id的单独表怎么样?如果表的大小超过n,您可以添加触发器来删除旧ID。说..
CREATE TABLE IF NOT EXISTS recent_results
(result_id INTEGER PRIMARY KEY, event_date DATE);
// is DATE a type? I don't know. you get the point
CREATE TRIGGER IF NOT EXISTS optimizer
AFTER INSERT ON recent_results
WHEN (SELECT COUNT(*) FROM recent_results) > N
BEGIN
DELETE FROM recent_results
WHERE result_id = (SELECT result_id
FROM recent_results
WHERE event_date = MIN(event_date));
// or something like that. I have no idea if this will work,
// I just threw it together.
或者您可以创建一个临时的基于内存的表,您可以在应用程序加载时填充该表,并在应用程序执行期间执行事务时保持最新。这样你只需支付一次陡峭的价格!
再多几点想法。要有创意,并记住您通常可以定义您想要的数据结构和算法。祝你好运!
答案 1 :(得分:1)
没有更好的方法。 可能有帮助的SQL窗口函数未在SQLite中实现。
SQLite被设计为嵌入式数据库,其中大部分逻辑都保留在应用程序中。 与应避免网络通信的客户端/服务器数据库相比,混合SQL命令和程序逻辑没有性能劣势。
一个不那么愚蠢的解决方案要求你事先做一些SELECT player_id FROM somewhere
,这应该没问题。
要使各个查询有效,请确保在player_id
和event_date
两列上有一个索引。