这将需要一段时间,所以要喝点咖啡。
有很多很好的资源可用于分层开发。大多数你会看到下面来自像this这样的网站,它指的是你很乐意推荐的Celko。
您需要做的第一件事就是删除关键字字段。开发,使用和维护方面的额外努力远不及所得到的好处。我会告诉你以后如何实现它。
在此设计中,将行看作节点。每个节点都有两个值,即左边界和右边界。这些形成了一个范围或影响范围。如果一个节点的边界为1:4,而另一个节点的比例为2:3,则第二个节点是第一个子节点,因为其跨度包含在第一个节点的范围内。另外,由于第二个节点的边界是连续的,因此它下面不能有节点,所以它必须是叶节点。这听起来可能听起来很复杂,特别是在考虑多层次的节点时,但您会看到SQL如何编写起来相对容易,而且表的维护工作量很小。
完整的脚本是here。
CREATE TABLE categories (
id INT not null auto_increment PRIMARY KEY,
name VARCHAR(50) NOT NULL,
lBound INT NOT NULL,
rBound INT NOT NULL,
-- MySQL does not implement check constraints. These are here for illustration.
-- The functionality will be implemented via trigger.
CONSTRAINT cat_ptr_incr_chk CHECK (lBound < rBound), -- basic integrity check
CONSTRAINT cat_ptr_root_chk CHECK (lBound >= 0) -- eliminates negative values
);
create unique index ndx_cat_lBound on categories(lBound);
create unique index ndx_cat_rBound on categories(rBound);
发现有什么在这里,说:“我是一个叶子结点”,“我是根”或“我的根节点是这样的,和这样的。”这些信息都包含在lBound和rBound(左边界,右边界)值中。我们来构建一些节点,以便我们可以看到这个样子。
INSERT INTO categories(name, lBound, rBound)
values('Categories', 0, 1);
ID name lBound rBound
== ========== ====== ======
1 Categories 0 1
这是我们在创建表格上的触发器之前做的。实际上,插入触发器不必具有必须在第一行(整个结构的根节点)时才能识别的特殊代码。该代码只会在插入第一行时执行,而不会再次执行。现在我们不必担心它。
所以现在我有结构的根源。请注意,它的边界是0和1.没有什么可以适合0和1,所以这是一个叶节点。树根也是一片叶子。这意味着树是空的。
所以现在我们编写触发器和dml程序。代码在脚本中,所以我不会在这里复制它,只要说插入和删除触发器不会允许任何人发出插入或删除语句。任何人都可以发布更新,但只允许更改名称。插入,删除和完成更新的唯一方法是通过程序执行。考虑到这一点,我们来创建根下的第一个节点。
call ins_category('Electronics', 1);
这将创建一个名为'Electronics'的节点作为ID = 1(根)节点的子节点。
ID name lBound rBound
== ========== ====== ======
1 Categories 0 3
2 Electronics 1 2
请注意触发器如何扩展根的右边界以允许新节点。下一个节点将是另一个级别。
call ins_category('Televisions', 2);
节点2是电子的,所以新节点将是它的子节点。
ID name lBound rBound
== ========== ====== ======
1 Categories 0 5
2 Electronics 1 4
3 Televisions 2 3
让我们创建一个新的较高级别的节点 - 它仍然必须在根目录下,但将电子旁边的子树的开始。
call ins_category('Antiques & Collectibles', 1);
ID name lBound rBound
== ========== ====== ======
1 Categories 0 7
2 Electronics 1 4
3 Televisions 2 3
4 Antiques & Collectibles 5 6
注意5-6不适合除根外的任何边界范围。所以它是一个直接位于根下的子节点,就像电子产品一样,但独立于其他子节点。
用于给出更清晰的结构图的SQL并不复杂。有很多更多的节点完成了树之后,让我们看看是什么样子:
-- Examine the tree or subtree using pre-order traversal. We start at the node
-- specified in the where clause. The root of the entire tree has lBound = 0.
-- Any other ID will show just the subtree starting at that node.
SELECT n.ID, n.NAME, n.lBound, n.rBound
FROM categories p
join categories n
on n.lBound BETWEEN p.lBound AND p.rBound
where p.lBound = 0
ORDER BY n.lBound;
+----+----------------------------+--------+--------+
| id | name | lBound | rBound |
+----+----------------------------+--------+--------+
| 1 | >Categories | 0 | 31 |
| 2 | -->Electronics | 1 | 20 |
| 3 | ---->Televisions | 2 | 9 |
| 4 | ------>Tube | 3 | 4 |
| 5 | ------>LCD | 5 | 6 |
| 6 | ------>Plasma | 7 | 8 |
| 7 | ---->Portable Electronics | 10 | 19 |
| 8 | ------>MP3 Players | 11 | 14 |
| 9 | -------->Flash | 12 | 13 |
| 10 | ------>CD Players | 15 | 16 |
| 11 | ------>2-Way Radios | 17 | 18 |
| 12 | -->Antiques & Collectibles | 21 | 28 |
| 14 | ---->Furniture | 22 | 27 |
| 15 | ------>Office Furniture | 23 | 26 |
| 16 | -------->Chairs | 24 | 25 |
| 13 | -->Art | 29 | 30 |
+----+----------------------------+--------+--------+
输出上面实际上是在脚本中定义的视图,但它清楚地显示出层次结构。这可以很容易地转换为一组嵌套菜单或导航节点。
可以进行增强,但它们不需要改变这个基本结构。你会发现它很容易维护。我已经开始认为在一个DBMS如Oracle,SQL Server或PostGreSQL中这会让事情变得容易很多,这允许触发视图。然后,访问权限可以仅限于视图,因此触发器会处理所有事情。这将消除对单独的存储过程的需要。但这种方式并不坏。我可以愉快地生活在一起。事实上,使用单独的视图无法使用的存储过程具有简单性和灵活性(不能将参数传递给视图)。
关键字功能也被定义,但我不会在这里显示。看剧本。一次执行一次,以清楚了解正在发生的事情。如果你有任何问题,你知道在哪里找到我。
[编辑]添加了一些enhancements,包括使用关键字。
您需要一个分层或递归查询。在SQLServer或Oracle中,这将使用CTE完成,但不幸的是,我不相信MySQL具有这种能力。是否只有3级?如果这样的话就像你的查询可以工作。 –