如何在sqlite中使用GROUP BY有效地获取n个最近的行?

时间:2013-03-14 16:25:59

标签: ios sql sqlite

我有一个事件结果表,我需要为给定的玩家列表获取每个玩家最近的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")

然后我继续我的快乐方式。但是,如何将其转换为单个快速查询?

2 个答案:

答案 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_idevent_date两列上有一个索引。