【问题标题】:How to efficiently fetch n most recent rows with GROUP BY in sqlite?如何在sqlite中使用GROUP BY有效地获取n个最近的行?
【发布时间】:2013-03-14 16:25:59
【问题描述】:

我有一个事件结果表,我需要为给定的玩家列表获取每个玩家最近的 n 个事件。

这是在 iOS 上,所以它需要很快。我查看了许多使用子查询或连接的 top-n-per-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")

然后我继续我的快乐之路。但是我怎样才能把它变成一个单一的快速查询呢?

【问题讨论】:

  • 没有更好的办法。 SQLite 被设计为一个嵌入式 数据库,其中大部分逻辑都保留在应用程序中。为什么你希望它是一个单一的查询?
  • 对 sqlite 进行单独调用会产生 io 开销,所以一般来说,尽可能减少总查询不是最好的吗?
  • 一般来说,是的。但是,如果您必须经历使最终结果明显更糟的扭曲,则不会。
  • 很公平。我的 sql-fu 并不是那么强大,所以我希望一些我无法构建的复杂查询可以更有效地完成此任务。
  • @CL 顺便说一句,请随意添加它作为答案——如果没有人提出其他建议,我会在稍后将其标记为接受。

标签: ios sql sqlite


【解决方案1】:

没有更好的方法。 SQLite 中没有实现可能有帮助的 SQL 窗口函数。

SQLite 被设计为嵌入式数据库,其中大部分逻辑都保留在应用程序中。 与应避免网络通信的客户端/服务器数据库相比,混合 SQL 命令和程序逻辑不会降低性能。

一个不那么愚蠢的解决方案需要你事先做一些SELECT player_id FROM somewhere,这应该没有问题。

为了提高单个查询的效率,请确保在 player_idevent_date 两列上有一个索引。

【讨论】:

    【解决方案2】:

    这不会是一个很大的答案,但这里是......

    我发现让事情变得真正快速可能涉及来自数据和架构本身性质的想法。例如,搜索有序列表比搜索无序列表要快,但您必须预先支付设计和执行成本。

    所以问问你自己,你的数据上是否有任何自然分区可能会减少 SQLite 必须搜索的记录数量。您可能会问最近的 n 个事件是否属于特定时间段。它们都来自过去 7 天吗?最后一个月?如果是这样,那么您可以构建查询以在执行更复杂的搜索之前排除整个数据块。

    另外,如果你不能让事情快速工作,你可以考虑 UX 诡计!很多工程师对他们的用户体验并不聪明。您的查询会作为视图控制器推送的结果运行吗?然后将事情从 PREVIOUS 视图控制器设置在后台线程中,并让它在 iOS 动画时工作。推送动画需要多长时间? .2 秒?您的用户在什么时候(通过一些 UX 控件)向应用程序指示要查询哪个 playerids?只要他触摸那个按钮或 TVCell,您就可以预取一些数据。所以如果你要做的总工作是O(n log n),那意味着你可以把它分解成O(n)O(记录 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.
    

    或者您可以创建一个基于内存的临时表,在应用加载时填充该表,并在应用执行期间执行事务时保持最新状态。这样您只需支付一次高昂的价格!

    还有一些想法给你。要有创意,记住你通常可以定义你想要的数据结构和算法。祝你好运!

    【讨论】:

    • 感谢您的想法!虽然我想不出在数据库架构方面我能做多少(最后 n 个事件可能在任何时间段内发生),但在流程中肯定有一个点我可以更快地开始预取数据以改善用户体验. Otoh,如果我能得到一个快速的查询,我就不需要费心了:P
    猜你喜欢
    • 2016-04-27
    • 1970-01-01
    • 2011-08-06
    • 1970-01-01
    • 2021-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-23
    相关资源
    最近更新 更多