【问题标题】:How many SQL queries do I need?我需要多少 SQL 查询?
【发布时间】:2010-11-23 01:30:51
【问题描述】:

我有一个数据库表 (sqlite),其中包含构成树层次结构的项目。每个项目都有一个 id 字段(用于它自己)和一个 parentId 用于其父项。现在给定一个项目,我必须检索从根到项目的整个链。

伪代码中的算法基本上是这样的:

  1. 光标是项目
  2. 通过 parentId 检索光标的 parentItem
  3. 如果 parentItem 不是 rootItem,则 cursor = parentItem 并转到 2。

所以我必须对每个项目执行 SQL SELECT 查询。

是否可以通过仅执行一个 SQL 查询来检索整个链 rootItem -> ... -> 项目?

【问题讨论】:

    标签: sql database sqlite


    【解决方案1】:

    有很多创造性的方法可以在数据库中组织分层数据,但我始终发现最简单的方法是恢复非分层格式的数据,然后以编程方式匹配父记录和子记录。

    总工作量:1 次查询 + 1 次通过数据集以编程方式创建层次结构。


    替代方法:

    我过去曾使用过这种方法,但效果有限。您可以使用 varchar(max) 列将每个项目的路径存储在树中,如下所示:

    ID    ParentID    Path
    --    --------    ----
    1     null        1/
    2     1           1/2/
    3     null        3/
    4     2           1/2/4/
    5     4           1/2/4/5/
    6     null        6/
    7     5           1/2/4/5/7/
    9     5           1/2/4/5/9/
    

    从那时起,获取 ID = 5 下的所有节点非常简单:

    SELECT *
    FROM table
    WHERE Path like (SELECT Path FROM Table WHERE ID = 5) + '%'
    

    【讨论】:

    • 不错的技术,我会 +1,但我有几个小时没有投票了。你为什么说有限成功?
    • 为什么不只是 SELECT * FROM table WHERE Path LIKE '%/5/%'; ?
    • @eyze:当然,这也可以 :) 但是,在几乎所有数据库实现中,数据库不能对以通配符开头的类似表达式使用索引。请参阅 SQLite 文档 (sqlite.org/optoverview.html):“由 LIKE 或 GLOB 运算符组成的术语有时可用于约束索引。这种使用有很多条件:[...] LIKE 的右侧或 GLOB 必须是不以通配符开头的字符串文字”。我不确定我上面的代码是否会使用索引,因为它不是字符串文字。 YMMV。
    • @eyze:也许“有限的成功”这句话不是最好的措辞。我曾在小型个人项目中使用过该技术,但从未真正在 big 表或 deep 层次结构上尝试过。然而,仍然值得尝试更多:)
    【解决方案2】:

    不是 ANSI 标准 SQL,不是,不是。好吧,这并不完全正确。您可以进行左外连接并添加足够的内容以覆盖可能的最大深度,但除非您限制最大深度并包含那么多连接,否则它并不总是有效。

    如果您的行集足够小(比如少于 1000 行),只需将它们全部检索出来,然后再计算出来。它很可能比单次读取遍历更快。

    您可以批处理父遍历。有这样的查询:

    SELECT t1.id id1, t1.parent parent1,
           t2.id id2, t2.parent parent2,
           t3.id id3, t3.parent parent3,
           t4.id id4, t4.parent parent4,
           t5.id id5, t5.parent parent5
    FROM mytable t1
    LEFT OUTER JOIN mytable t2 ON t1.parent = t2.id
    LEFT OUTER JOIN mytable t3 ON t2.parent = t3.id
    LEFT OUTER JOIN mytable t4 ON t3.parent = t4.id
    LEFT OUTER JOIN mytable t5 ON t4.parent = t5.id
    WHERE t1.id = 1234
    

    并将其扩展到您想要的任何数字。如果最后检索到的父级不为空,那么您还没有位于树的顶部,因此再次运行查询。这样,您应该有望将其减少到 1-2 次往返。

    除此之外,您还可以查看在 ID 中编码该数据的方法。不建议这样做,但是如果您将每个节点限制为 100 个子节点,您可以说 ID 为 10030711 的节点的路径为 10 -> 03 -> 07 -> 11。这当然还有其他问题(例如 max ID 长度),当然它是 hacky。

    另外值得注意的是,SQL 中有两种用于分层数据的基本模型。邻接表和嵌套集。你的方式(这很常见)是一个邻接集。嵌套集在这种情况下并没有真正的帮助,而且在它们上进行插入很复杂。

    【讨论】:

    • 不幸的是,我的行集相当大,甚至还在不断增长。
    【解决方案3】:

    你能改变表结构吗?看起来存储左右节点比仅存储父节点更容易使用,因为这样就可以进行单个选择。请参阅以下链接:

    http://www.mail-archive.com/sqlite-users@sqlite.org/msg23867.html

    http://weblogs.asp.net/aghausman/archive/2009/03/16/storing-retrieving-hierarchical-data-in-sql-server-database.aspx(这是 SQLServer,但他们有一个图表可能会有所帮助。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-10-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多