【问题标题】:Checking inherited attributes in an 'ancestry' based SQL table检查基于“祖先”的 SQL 表中的继承属性
【发布时间】:2010-04-09 04:04:59
【问题描述】:

我正在使用祖先 gem 来帮助在数据库中组织我的应用程序的树结构。它基本上将孩子的祖先信息写入一个名为“祖先”的特殊列。特定孩子的祖先列可能看起来像“1/34/87”,其中该孩子的父母是 87,然后 87 的父母是 34,而 34 的父母是 1。

似乎我们可以从这个表中选择行,每个行都有一个子查询来检查所有的祖先,看看它是否设置了某个属性。例如。在我的应用程序中,您只需将父元素的可见性列设置为 0 即可隐藏项目及其子项。

我希望能够找到没有隐藏其祖先的所有项目。我尝试使用 REPLACE 命令将斜杠转换为逗号,但 IN 需要一组逗号分隔的整数,而不是一个带有逗号分隔的字符串编号的字符串。

这很有趣,因为我可以分两步完成这个查询,例如检索该行,然后获取其祖先列,拆分 id 并进行另一个查询,检查 id 是否在该组 id 中,并且可见性不是 0 和 whala!但是将这些加入到一个查询中似乎是一项艰巨的任务。大量搜索显示了一些答案,但没有一个真正符合我的要求。

SELECT * FROM t1 WHERE id = 99;

99 的祖先栏写着 '1/34/87'

SELECT * FROM t1 WHERE visibility = 0 AND id IN (1,34,87);

有点倒退,但如果这不返回任何行,则该项目是可见的。

有没有人遇到过这种情况并提出解决方案。我真的不想走存储过程路线。这是一个 Rails 应用程序。

【问题讨论】:

    标签: sql mysql ruby-on-rails tree


    【解决方案1】:

    您可能想要做的是创建一个拆分函数,如果您还没有一个 (Split a Delimited String in SQL ),然后将其用作 IN 选择。

    还有另一种方法,但它可能会降低大型表的性能。 类似的东西

    SELECT  *
    FROM    Table t INNER JOIN
            Table tParents      
            ON  (   t.Path LIKE CAST(tParents.ID AS VARCHAR(20)) + '/%'
                    OR  t.Path LIKE +'%/' + CAST(tParents.ID AS VARCHAR(20)) + '/%'
                    OR  t.Path LIKE +'%/' + CAST(tParents.ID AS VARCHAR(20)))
    
    WHERE   t.ID = 99
    AND     tParents.Visible = 0
    

    【讨论】:

    • 谢谢。我试图远离存储的函数,正如该页面所说“这个例子仅用于说明目的,因为这种事情最好留给过程语言。”我之前发现了一个:D
    • @Brendon - 如果您接受建议“......这种事情最好留给程序语言”,然后在应用程序方面进行。第二个最好的方法是在存储的函数中进行。在给定的示例中,这不是一种罪过 - 您不会循环遍历大型结果集的所有记录,不必要地一一更新它们。
    • 谢谢 Unreason,我会调查一下。我会接受这个答案,因为它似乎是目前最好的选择:)
    【解决方案2】:

    如果您坚持摆脱存储/过程,为什么不从您的物化路径方法切换到 nested sets

    否则,从应用程序端执行两个查询(或使用 asstander 建议的存储过程)。

    SQL 中层次结构的良好链接here

    编辑: 您似乎正在数据库中存储有关树控件状态的信息。

    假设这确实是合理的并且您需要将可见性存储在数据库中,您可能会调查以下场景(这些是想法,而不是即时解决方案):

    1. 打开一个游标/记录集/whatever-row-based-approach-is-call-in-your-framework 并将其传递给树控件,以便从数据库中更新和获取的次数与树上的操作(使分支可见、更新可见性、隐藏分支等)。在这种情况下(取决于框架),您不必预先选择正确的元素(也不必在每次用户关闭或打开分支时发出 select 语句)。

    2. 更新数据库中所有子项的可见性。似乎您仅在展开/折叠的节点上更新可见性(如果您需要保留折叠分支和叶子的可见性,那么您可能有两个字段;这并不优雅,但我也会测试这个选项)

    3. 再次调查嵌套集;使用嵌套集所需的查询可能会变得更快。编写 SQL 也变得更容易(这里的 SQL 返回所有父节点可见的节点,假设可见性是 tinyint(1);BIT_AND 将在单个查询中对所有父节点进行聚合 AND)

      选择节点名称作为名称
      FROM t1 AS 节点,
      t1 作为父级
      WHERE node.visibility = 1 AND node.lft BETWEEN parent.lft AND parent.rgt
      按节点名称分组
      HAVING BIT_AND(parent.visibility) = 1
      按节点排序.lft

    (这是经过测试的,我以here 为例,并增加了可见性)

    此外,当您对每个解决方案进行测试和基准测试时,不要忘记对所有操作进行基准测试(选择可见分支、打开隐藏分支、将节点标记为不可见等)。

    【讨论】:

    • 大声笑,只是想发表评论并失去了整件事。我从嵌套集迁移到物化路径:D 这需要在纯 SQL 中的原因是它是用于填充索引的 Sphinx 查询。我想消除不可见的后代。谢谢你的链接,我会好好看看:)
    • 一开始只获取可见元素有什么问题?是不是可能有一个节点在链中被标记为不可见,你想删除它下面的所有节点?
    • 嗨,Unreason,你说得对。我需要消除不可见的项目及其后代,即使后代没有明确标记为不可见:)
    • 我更新了答案,查看嵌套集 SQL(相同的方法可用于 asstander/物化路径结构,主要区别在于,在这种情况下,您必须使用 3 个 ORed LIKE 表达式来选择节点 -父对,而不是这里的两个整数比较;另一方面,嵌套集需要维护)。
    • 谢谢 Unreason,我会好好看看。当两个更新彼此太接近时,我们的嵌套集实现(使用 awesome_nested_set 用于 rails)一直被破坏。尽管有各种锁定等...我无法找到问题所在,所以我决定采用迄今为止效果很好的物化路径方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-07
    • 1970-01-01
    相关资源
    最近更新 更多