【问题标题】:Selecting adjacent rows in an SQL query在 SQL 查询中选择相邻行
【发布时间】:2014-08-04 10:25:18
【问题描述】:

以下是一个不太适合 RDBMS 的问题,我认为,但这就是我要处理的问题。

我正在尝试编写一个工具来搜索存储在数据库中的日志。 有些行可能是:

Time             | ID | Object | Description 
2012-01-01 13:37 | 1  | 1      | Something happened
2012-01-01 13:39 | 2  | 2      | Something else happened
2012-01-01 13:50 | 3  | 2      | Bad
2012-01-01 14:08 | 4  | 1      | Good
2012-01-01 14:27 | 5  | 1      | Bad
2012-01-01 14:30 | 6  | 2      | Good

对象是外键。在实践中,时间会随着 ID 的增加而增加,但这不是一个实际的约束。实际上还有更多的领域。这是一个 Postgres 数据库 - 我也希望能够支持 SQLite,但我知道这很可能是不可能的。

现在,我希望能够针对对象 2 发生的所有不良事件运行查询:

SELECT * FROM table WHERE Object = 2 AND Description = 'Bad';

但是查看结果周围的一些上下文通常会很有用 - 正如grep-C 选项在搜索文本日志时非常有用。 对于上面的查询,如果我们想要任何一侧的上下文行,除了第 3 行之外,我们还需要第 2 行和第 6 行。

如果原始查询返回多行,则需要检索更多上下文。 请注意,上下文不是从与对象 1 关联的事件中检索到的;我们只消除对描述的限制。 此外,所涉及的顺序,以及由此决定什么与什么相邻的因素,是由时间字段引起的。

这指定了我想要实现的目标,但相关的数据库相当大,至少与运行它的机器的能力相比。

获取相邻行最常被引用的解决方案要求您在我称之为基本查询的每个结果中运行一个额外的查询;这不好,因为这可能是成千上万的查询。

我目前最糟糕的解决方案是运行一个查询来检索所有可能是上下文的行的 ID - 在上面的示例中,这将是搜索与对象 2 相关的所有行。然后我得到匹配的 ID基本查询,扩展(使用所有可能的 ID 的列表)到与基本查询或上下文匹配的行的 ID 列表,然后最终检索这些 ID 的数据。 这有效,但不优雅且缓慢。 从远程计算机上使用该工具时速度特别慢,因为初始 ID 列表可能非常大,检索它然后仅通过 Internet 传输它可能是过度的。

我尝试过的另一个解决方案是使用计算行的“缓冲区序列”的子查询或视图。 添加此字段后的表格如下所示:

Time             | ID | Sequence | Object | Description 
2012-01-01 13:37 | 1  | 1        | 1      | Something happened
2012-01-01 13:39 | 2  | 1        | 2      | Something else happened
2012-01-01 13:50 | 3  | 2        | 2      | Bad
2012-01-01 14:08 | 4  | 2        | 1      | Good
2012-01-01 14:27 | 5  | 3        | 1      | Bad
2012-01-01 14:30 | 6  | 3        | 2      | Good

在此表上运行基本查询然后允许您通过添加或减去序列值来生成所需的 ID 列表。 这消除了通过网络传输大量行的问题,但现在数据库必须运行这个复杂的子查询,而且速度慢得令人无法接受,尤其是在第一次运行时——考虑到用例,查询是零星的,缓存不是很有效.

如果我负责架构,我可能只是将这个字段存储在数据库中,但我不是,所以欢迎任何改进建议。谢谢!

【问题讨论】:

  • “邻接”是一个抽象结构……它需要特定的排序才能有意义。你的意思是“时间戳立即之前和之后?”你如何断绝关系?在任何情况下,您可能正在查看一个分配行号的函数,然后您想JOIN 该值 +/- 1
  • 您可能对this answer感兴趣
  • 您使用的是什么数据库? SQLite 还是 Postgres?
  • @GordonLinoff Postgres,但支持 SQLite 是一个奖励。
  • @paqogomez 如果我错了,请纠正我,但这个答案似乎不适用于 WHERE 子句 - 您只能获得该查询中行的下一个/上一个 ID。不过,也许它可以通过第二个查询来实现。

标签: sql database sqlite postgresql optimization


【解决方案1】:

你应该使用ROW_NUMBER窗口函数

http://www.postgresql.org/docs/current/static/functions-window.html

邻接是一种抽象结构,依赖于显式排序(或PARTITION OVER)...您是指具有前面时间戳的那个吗?

决定你如何决定你想要什么样的“相邻”,然后通过这个标准获得ROW_NUMBER

一旦你有了,你只需 JOIN 项目上的每一行都有 ROW_NUMBER +/- 1

【讨论】:

  • 不需要加入 LEAD 和 LAG 这样做(也在参考链接上)
  • @ConradFrix 好点。我没有意识到 LEADLAG 在 postgres 中。
【解决方案2】:

你可以用 sqlite 试试这个

SELECT DISTINCT t2.*
  FROM  (SELECT * FROM t WHERE object=2 AND description='Bad') t1
      JOIN
        (SELECT * FROM t WHERE object=2) t2
      ON t1.id = t2.id OR
        t2.id IN (SELECT id FROM t WHERE object=2 AND t.time<t1.time ORDER BY t.time DESC LIMIT 1) OR
        t2.id IN (SELECT id FROM t WHERE object=2 AND t.time>t1.time ORDER BY t.time ASC  LIMIT 1)
ORDER BY t2.time
;

通过更多上下文更改限制值

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-10
    • 1970-01-01
    • 2016-03-15
    • 1970-01-01
    • 2017-11-08
    相关资源
    最近更新 更多