【问题标题】:Creating a flattened table/view of a hierarchically-defined set of data创建分层定义的数据集的平面表/视图
【发布时间】:2011-03-24 10:30:34
【问题描述】:

我有一个包含分层数据的表。此层次结构中目前有大约 8 个级别。

我真的很喜欢数据的结构方式,但是当我需要知道第 8 级的记录是否是第 1 级记录的子记录时,性能很差。

我有 PL/SQL 存储函数来为我进行这些查找,每个函数都有一个 select * from tbl start with ... connect by... 语句。当我查询少量记录时,这很好用,但我现在需要一次查询约 10k 条记录,并且为每个记录运行此函数。它需要 2-3 分钟,我需要它在几秒钟内运行。

根据我对当前数据的了解,使用一些启发式方法,我可以摆脱查找功能而只使用childrecord.key || '%' LIKE parentrecord.key,但这是一个非常肮脏的技巧,并不总是有效。

所以现在我在想,对于这个分层定义的表,我需要一个单独的父子表,其中将包含所有关系......对于从 1-8 级开始的层次结构,将有 8 个!记录,将 1 与 2、1 与 3、...、1 与 8 和 2 与 3、2 与 4、...、2 与 8 相关联。以此类推。

我的想法是,我需要有一个插入触发器,它基本上会运行connect by 查询,并且对于每个匹配层次结构的匹配,它都会在查找表中插入一条记录。为了处理旧数据,我只需使用级联删除为主表设置外键。

还有比这更好的选择吗?我是否错过了另一种可以更快地确定这些遥远的祖先/后代关系的方法?

编辑:这似乎正是我的想法:http://evolt.org/working_with_hierarchical_data_in_sql_using_ancestor_tables

【问题讨论】:

  • 您的主要兴趣是回答“谁是我的最终父母”这个问题?或“谁是我的第一个定义了某些属性的父母?”如果是这样,物化视图可能比触发器更容易、更安全。
  • @Adam Musch:我很想知道是否有两条记录相互关联。您指的是触发器的哪些安全问题?未能锁定相应的记录并获取损坏的数据?
  • 主要是数据损坏,是的。通过表和物化视图维护传递闭包意味着可以独立更新——触发器可以被禁用,映射表可以针对它们编写 SQL。我已经写了物化视图来寻找“上游”来回答我在之前评论中提到的问题,而不是回答通用形式“谁是 B 的所有祖先”或“A 是 B 的祖先吗?”

标签: sql oracle database-design query-optimization hierarchical-data


【解决方案1】:

所以你想要的是实现传递闭包。也就是说,给定这个应用表...

 ID   | PARENT_ID
------+----------
    1 | 
    2 |         1
    3 |         2
    4 |         2
    5 |         4

...图表如下所示:

 PARENT_ID | CHILD_ID
-----------+----------
         1 |        2
         1 |        3
         1 |        4
         1 |        5
         2 |        3
         2 |        4
         2 |        5
         4 |        5

可以在 Oracle 中维护这样的表,尽管您需要为它推出自己的框架。问题是它是否值得开销。如果源表是不稳定的,那么保持图形数据新鲜可能会花费更多的周期,而不是您在查询上节省的时间。只有您知道您的数据的个人资料。

我认为您无法使用 CONNECT BY 查询和级联外键来维护这样的图表。太多的间接活动,太难做对了。还有一个物化视图,因为我们无法编写一个 SQL 查询,当我们删除 ID=4 的源记录时,它会删除 1->5 记录。

所以我建议你阅读 Dong、Libkin、Su 和 Wong 的一篇名为 Maintaining Transitive Closure of Graphs in SQL 的论文。这包含很多理论和一些粗糙的(Oracle)SQL,但它将为您提供构建维护图表所需的 PL/SQL 的基础。


"你能详细说明一下吗 太难维护 通过/级联 FK 连接?如果我控制 访问表和所有 插入/更新/删除通过 存储过程,有哪些 存在这样的情况 崩溃了?”

考虑记录1->5,它是1->2->4->5 的短路。现在,如果正如我之前所说,我们删除ID=4 的源记录会发生什么?级联外键可以删除2->44->5 的条目。但这会在图表中留下1->5(实际上是2->5),尽管它们不再代表图表中的有效边

可能有效的方法(我想,我还没有这样做)是在源表中使用额外的合成键,就像这样。

 ID   | PARENT_ID | NEW_KEY
------+-----------+---------
    1 |           | AAA
    2 |         1 | BBB
    3 |         2 | CCC
    4 |         2 | DDD
    5 |         4 | EEE

现在图表看起来像这样:

 PARENT_ID | CHILD_ID | NEW_KEY
-----------+----------+---------
         1 |        2 | BBB
         1 |        3 | CCC
         1 |        4 | DDD
         1 |        5 | DDD
         2 |        3 | CCC
         2 |        4 | DDD
         2 |        5 | DDD
         4 |        5 | DDD

所以图表有一个外键引用生成它的源表中的关系,而不是链接到 ID。然后删除ID=4 的记录将级联删除图表中NEW_KEY=DDD 所在的所有记录。

如果任何给定的 ID 只能有零个或一个父 ID,这将起作用。但如果允许这种情况发生,它就不会起作用:

 ID   | PARENT_ID
------+----------
    5 |         2
    5 |         4

换句话说,边1->5 代表1->2->4->51->2->5。因此,什么可能有效取决于数据的复杂性。

【讨论】:

  • 我有两张表我想将其应用到——一张是易失的,另一张在非工作时间仅由单线程进程更新一次。我希望首先在波动较小的表上实现它,然后在我解决所有问题后将框架移植到另一个表上。我还没有吸收你链接到的论文中的所有内容,但它似乎正是我正在寻找的。​​span>
  • 我不是在质疑您的判断,但您能否进一步说明使用 CONNECT BY/级联 FK 维护起来太难的部分?如果我控制对表的访问并且所有插入/更新/删除都通过存储过程进行,那么在哪些情况下会发生这种情况?即使其他人具有直接的插入/更新/删除访问权限,如果逻辑是在触发器/级联 FK 中实现的,我将面临的一些问题的示例是什么?
猜你喜欢
  • 2014-12-24
  • 2018-08-18
  • 2014-12-24
  • 2019-09-02
  • 1970-01-01
  • 1970-01-01
  • 2015-06-12
  • 2023-01-28
  • 1970-01-01
相关资源
最近更新 更多