MySQL - 处理这种分层数据的最佳方法?

时间:2010-06-29 03:35:04

标签: mysql table-structure

这是后续:
MySQL - Is it possible to get all sub-items in a hierarchy?

我有一个任意深度的邻接列表模型表(我可以将其转换为嵌套集模型

我阅读了有关如何使用嵌套集模型的MySQL数据,尽管它似乎变得越来越复杂,并且非常复杂,无法进行插入,更新和删除等基本功能。

另一篇博客展示如何使用具有邻接列表模型的触发器系统来保存将每个对象与其祖先相关联的祖先表。


现在我需要能够返回给定节点的所有子节点的列表,以更改或删除它们。这种层次结构一旦创建就不会一直在变化,但会有大量的层次结构。

我看到的三种方法是:

  1. 创建了一个存储过程,它会执行一个返回所有子项的递归查询。

  2. 转换为嵌套集模型,这需要深入了解复杂性,并可能创建一个存储过程来添加,编辑和删除。

  3. 在插入/删除触发器上创建上述祖先表以处理所有数据。

  4. 如果还有其他方法我没有探索,请告诉我,我会更新此列表。

5 个答案:

答案 0 :(得分:4)

Quassnoi已对嵌套集模型和邻接列表模型进行了一些性能测试,并在其博客Adjacency list vs. nested sets: MySQL中记录了结果和建议。执行摘要是:

  • 嵌套集更快地获取所有子节点或所有父节点。
  • 如果您经常需要更新表,嵌套集是一个坏主意。

以下是他的文章的结论:

  

在MySQL中,如果对hierarhical结构的更新很少,并且在更新期间锁定表(在长表上可能需要几分钟),则应该首选嵌套集模型。

     

这意味着使用MyISAM存储引擎创建表,创建如上所述的GEOMETRY类型的边界框,使用SPATIAL索引对其进行索引并在表中保持级别。

     

如果对表的更新频繁或者在更新所暗示的长时间内锁定表是不合理的,则应使用邻接列表模型来存储分层数据。

     

这需要创建一个查询表的函数。

本文的其余部分将介绍如何定义表,实现查询以及如何进行性能测量。使用空间索引是一个聪明的想法,可以提高可能对您来说不熟悉的嵌套集模型的性能。


如果您还在考虑没有MySQL的方法,那么您可能需要查看PostgreSQL这是另一个免费的开源数据库。 PostgreSQL支持recursive common table expressions形式的递归查询,这使得查询分层数据比在MySQL中更容易,并且提供更好的性能。 Quassnoi还撰写了一篇文章Adjacency list vs. nested sets: PostgreSQL,其中显示了详细信息。

虽然我们正在讨论其他方法,但Oracle的数据库也值得一提。 Oracle还有一个自定义扩展CONNECT BY,可以非常简单快速地查询分层数据。 Quassnoi的文章Adjacency list vs. nested sets: Oracle再次涵盖了性能细节。在这种情况下,获得所有孩子所需的查询非常简单:

SELECT *
FROM yourtable
START WITH id = 42
CONNECT BY parent = PRIOR id

答案 1 :(得分:2)

我总是选择嵌套集来实现剪切简单性和便利性。我总是建议this article。它显示了使用这种层次数据工作所需的查询。我在这里看到的唯一缺点是,当层次结构达到一定程度的复杂性时,插入/更新新记录会变得更慢,但读数比我见过的许多其他解决方案更快。

只是给你一个上面文章的例子:

SELECT t1.name AS lev1, t2.name as lev2, t3.name as lev3, t4.name as lev4
FROM category AS t1
LEFT JOIN category AS t2 ON t2.parent = t1.category_id
LEFT JOIN category AS t3 ON t3.parent = t2.category_id
LEFT JOIN category AS t4 ON t4.parent = t3.category_id
WHERE t1.name = 'ELECTRONICS';

+-------------+----------------------+--------------+-------+
| lev1        | lev2                 | lev3         | lev4  |
+-------------+----------------------+--------------+-------+
| ELECTRONICS | TELEVISIONS          | TUBE         | NULL  |
| ELECTRONICS | TELEVISIONS          | LCD          | NULL  |
| ELECTRONICS | TELEVISIONS          | PLASMA       | NULL  |
| ELECTRONICS | PORTABLE ELECTRONICS | MP3 PLAYERS  | FLASH |
| ELECTRONICS | PORTABLE ELECTRONICS | CD PLAYERS   | NULL  |
| ELECTRONICS | PORTABLE ELECTRONICS | 2 WAY RADIOS | NULL  |
+-------------+----------------------+--------------+-------+
6 rows in set (0.00 sec)

SQL明智,我认为它不会变得更漂亮和更简单;)

我不知道存储过程方式。但是因为它涉及递归(在你的情况下),我不知道它是否会在层次结构中有很多级别。我想你可以尝试一下。

答案 2 :(得分:1)

也许你应该考虑使用像MongoDB这样的面向文档的数据库。它可以让你的生活更轻松。

答案 3 :(得分:1)

在处理分层数据集时,我发现最好在考虑缓存的情况下处理它。以这种方式处理这个问题的方法的主要好处之一是它不需要将数据库去规范化为可能更难变异的东西。

由于内存堆(memcache,redis等)查找比简单id -> data分辨率的SQL快得多,我会用它们来缓存每个节点的直接子节点的id列表。这样,您可以通过递归算法获得不错的性能,为任何节点构建完整的列表。

要添加/删除新节点,您只需要使其“直接父缓存O(1)无效。

如果速度不够快,可以在每个节点的节点的所有子节点列表中添加另一层缓存。为了使其能够使用体面可变的数据集,您应该记录每个节点的缓存性能(新鲜/缓存命中率),并设置何时存储缓存的容差级别。这也可以存储在内存堆中,因为它是非重要数据。

如果您使用此更高级的缓存模型,则需要注意这些完整的子节点列表在其任何子节点更改时需要失效O(log n)

获得子ID列表后,可以使用SQL的WHERE id IN( id1, id2, .... )语法查询所需内容。

答案 4 :(得分:-1)

我曾经不得不在一个类似SQL的数据库管理器中存储一个复杂的分层任意深度的物料清单系统,而这个系统并不是真正完成任务,它最终导致了凌乱和棘手的指标,数据定义,从头开始重新启动后,使用数据库管理器仅为简单索引键上的记录读取和写入提供API,并在外部代码中执行所有实际输入/操作/报告,最终结果更快实现,更容易理解,更易于维护和增强。所需的最复杂查询基本上是SELECT A FROM B。

因此,不要将逻辑和操作嵌入到MySQL的限制内,而是考虑敲打代码来做你想做的事情,并且只依赖于MySQL来获得最低级别的获取/放置。