【问题标题】:Hierarchical tagging in SQLSQL 中的分层标记
【发布时间】:2010-09-20 09:31:33
【问题描述】:

我有一个 PHP Web 应用程序,它使用 MySQL 数据库进行对象标记,其中我使用了接受的标记结构作为this SO question 的答案。

我想实现一个标签层次结构,其中每个标签都可以有一个唯一的父标签。对父标签 T 的搜索将匹配 T 的所有后代(即 T、父标签为 T(T 的子代)、T 的孙子等)。

执行此操作的最简单方法似乎是将 ParentID 字段添加到标签表中,该字段包含标签的父标签的 ID,或者如果标签没有父标签,则包含一些幻数。然而,搜索后代需要对数据库进行重复的完整搜索以找到每个“世代”中的标签,我想避免这种情况。

一种(可能)更快但不太规范的方法是拥有一个包含每个标记的所有子级,甚至每个标记的所有后代的表。然而,这会带来数据库中数据不一致的风险(例如,标签是多个父级的子级)。

有没有一种好方法可以让查询快速找到后代,同时尽可能保持数据标准化?

【问题讨论】:

    标签: sql mysql database tags normalizing


    【解决方案1】:

    您可以构建 Kimball 所说的 Hierarchy Helper Table。

    假设您的层次结构如下所示:A -> B | B -> C | C -> D

    您可以将记录插入到如下所示的表中

    ParentID, ChildID, Depth, Highest Flag, Lowest Flag
    A, A, 0, Y, N
    A, B, 1, N, N
    A, C, 2, N, N
    A, D, 3, N, Y
    B, B, 0, N, N
    B, C, 1, N, N
    B, D, 2, N, Y
    C, C, 0, N, N
    C, D, 1, N, Y
    D, D, 0. N, Y
    

    我想我说得对....无论如何。关键是您仍然正确存储层次结构,您只需从适当的表构建此表。这个表查询就像一个女妖。假设您想知道 B 以下的所有第一级是什么。

    WHERE parentID = 'B' and Depth = 1
    

    【讨论】:

      【解决方案2】:

      我会使用某种数组来存储子标签,这应该比在自身上加入表格要快得多(特别是如果您有大量标签)。我看了看,我不知道 mysql 是否具有本机数组数据类型,但您可以通过使用文本列并在其中存储序列化数组来模拟这一点。如果你想进一步加快速度,你应该能够在该列上放置一个文本搜索索引来找出哪些标签是相关的。

      [编辑] 在阅读了 Ali 的文章后,我进行了一些搜索,并找到了 this 关于在 postgres 中实现层次结构的一系列方法的介绍。可能仍然有助于解释。

      【讨论】:

        【解决方案3】:

        Ali 的答案有一个指向 Joe Celko's Trees and Hierarchies in SQL for Smarties 的链接,这证实了我的怀疑 - 没有一个简单的数据库结构可以提供世界上最好的。最适合我的目的似乎是这本书中详述的“频繁插入树”,它类似于阿里链接的“嵌套集模型”,但具有非连续索引。这允许 O(1) 插入(a la 非结构化 BASIC 行编号),并在需要时偶尔进行索引重组。

        【讨论】:

          【解决方案4】:

          我使用两列来实现它。我在这里稍微简化一下,因为我必须将标签名称保存在单独的字段/表中,因为我必须针对不同的语言对其进行本地化:

          • 标签
          • 路径

          例如看看这些行:

          tag            path
          ---            ----
          database       database/
          mysql          database/mysql/
          mysql4         database/mysql/mysql4/
          mysql4-1       database/mysql/mysql4-1/
          oracle         database/oracle/
          sqlserver      database/sqlserver/
          sqlserver2005  database/sqlserver/sqlserver2005/
          sqlserver2005  database/sqlserver/sqlserver2008/
          

          等等

          在路径字段上使用like 运算符,您可以轻松获取所有需要的标记行:

          SELECT * FROM tags WHERE path LIKE 'database/%'
          

          有一些实现细节,例如当您在层次结构中移动节点时,您也必须更改所有子节点等,但这并不难。

          还要确保路径的长度足够长——在我的例子中,我没有使用路径的标签名称,而是使用另一个字段来确保我不会得到太长的路径。

          【讨论】:

          • 我几乎肯定会在未来的某个时候使用它。谢谢!
          • 如果一个标签可以有多个路径(n:m)怎么办?有没有我现在看不到的路径解决方案?
          【解决方案5】:

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-05-12
            • 1970-01-01
            • 2021-02-12
            • 1970-01-01
            • 2023-03-27
            • 2017-06-04
            相关资源
            最近更新 更多