<script type="text/javascript">
</script> <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>
树状结构的存储与管理,是每一个在关系型平台上工作的程序员早晚都要遇到的问题。说大不大,怎么都能解决,说小不小,处理不好,有的是麻烦等着你。仁者见仁,智者见智,公说公有理,婆说婆有理(谁用机箱砸我?机箱是个好东西,乱丢会摔坏硬盘的,你看我话没说完你又把显示器丢了……),咳咳,好吧,闲话少说,我们从最大路的处理风格谈一谈吧。这里面的大部分内容并非我的独创,从很久很久以前,数据库程序员们就这样做啦。
树状表的结构化表达
在一切开始前,我们先就树状表的表示方式达成一个共识。在关系型数据库中,我们当然没有办法这样直接表示一个树:
a
b c
defg
相应的,我们会把它变形为平面表,这种变形让我想起拓扑几何:
r n1 n2
a b d
a b e
a c f
a c g
存储结构
稍有经验的朋友,大概都不会试着把树状结构一层一列的存进去了吧。这样做的问题是显而易见的:与表中存储的信息结构不同,表的结构应该是相对固定的,不能随便改动。而对于层数不能固定的普通树状结构,这是不可思议的。没有必要讨论过多的错误,我们选择一个相对正确的方式――把树状结构抽象成适合关系数据库的形式。
只考虑某一个节点的话,和这个节点相关的信息是:它的唯一标识、父节点、子节点、数据信息。这其中只有子节点的数目不定。不过如果每一个节点都确定了自己的父节点,显然可以省略子节点。这样一来,一个节点需要存储三部分信息――它的唯一标识、父节点、数据信息。一个理想的TreeView表只需要三个节点就可以了,用SQL语句来表达就是
CREATETABLE[dbo].[TreeView](
[ID][char](10)PRIMARYKEY,
[PID][char](10)FOREIGNKEYREFERENCES[dbo].[TreeView],
[DATA][char](10)
)
ID是当前节点的唯一标识号,显然它应当是主键;我们建立的是一个自闭的存储结构,每一个节点的父节点也应当出自表中存储的节点,所以PID列引用ID作为外键;至于DATA,它是节点中的信息,通常和树的结构没有绝对关系。我把这三列全设成Char(10),是为了后面做演示更方便,当然也有人喜欢用自动标识列来做主键,在这种场合,也自有其优点。为了维护数据的完整性或存储、检索等方面的考虑,实用中我们可能会采用更复杂的结构,不过骨干就这样子了。这个结构从数学上讲很简洁,而且是自洽的。如果一个节点没有父节点,那么它的PID就等于它自己的ID。这并不违反我们关于主外键的定义。
信息的管理与使用
树表的结构确定后,问题就集中在如何读写其中的数据。
增加节点:增加一个叶节点很简单,只要指定这个节点的父节点,把它“挂接”到TreeView中。从递归的角度来看,我们可以重复这一步骤,真到插入一个完整的子树。相对而言,比较麻烦的是,如何把一个子节点插入到现有的树中间,而不是最末端。比如存在一个节点N,它的根为R,现在要在R和N之间插入一个新节点N’,我们可以这样做:把N’挂在R下面,作为它的子节点,然后把N的父节点指定为N’即可。
修改节点:这里指修改树结构,改变某一节点的父节点,在这种结构中,修改是很方便的,只要调用标准的update就可以。 1 <script type="text/javascript"> </script> <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>
<script type="text/javascript">
</script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>
树状结构的存储与管理,是每一个在关系型平台上工作的程序员早晚都要遇到的问题。说大不大,怎么都能解决,说小不小,处理不好,有的是麻烦等着你。仁者见仁,智者见智,公说公有理,婆说婆有理(谁用机箱砸我?机箱是个好东西,乱丢会摔坏硬盘的,你看我话没说完你又把显示器丢了……),咳咳,好吧,闲话少说,我们从最大路的处理风格谈一谈吧。这里面的大部分内容并非我的独创,从很久很久以前,数据库程序员们就这样做啦。
树状表的结构化表达
在一切开始前,我们先就树状表的表示方式达成一个共识。在关系型数据库中,我们当然没有办法这样直接表示一个树:
a
b c
defg
相应的,我们会把它变形为平面表,这种变形让我想起拓扑几何:
r n1 n2
a b d
a b e
a c f
a c g
存储结构
稍有经验的朋友,大概都不会试着把树状结构一层一列的存进去了吧。这样做的问题是显而易见的:与表中存储的信息结构不同,表的结构应该是相对固定的,不能随便改动。而对于层数不能固定的普通树状结构,这是不可思议的。没有必要讨论过多的错误,我们选择一个相对正确的方式――把树状结构抽象成适合关系数据库的形式。
只考虑某一个节点的话,和这个节点相关的信息是:它的唯一标识、父节点、子节点、数据信息。这其中只有子节点的数目不定。不过如果每一个节点都确定了自己的父节点,显然可以省略子节点。这样一来,一个节点需要存储三部分信息――它的唯一标识、父节点、数据信息。一个理想的TreeView表只需要三个节点就可以了,用SQL语句来表达就是
CREATETABLE[dbo].[TreeView](
[ID][char](10)PRIMARYKEY,
[PID][char](10)FOREIGNKEYREFERENCES[dbo].[TreeView],
[DATA][char](10)
)
ID是当前节点的唯一标识号,显然它应当是主键;我们建立的是一个自闭的存储结构,每一个节点的父节点也应当出自表中存储的节点,所以PID列引用ID作为外键;至于DATA,它是节点中的信息,通常和树的结构没有绝对关系。我把这三列全设成Char(10),是为了后面做演示更方便,当然也有人喜欢用自动标识列来做主键,在这种场合,也自有其优点。为了维护数据的完整性或存储、检索等方面的考虑,实用中我们可能会采用更复杂的结构,不过骨干就这样子了。这个结构从数学上讲很简洁,而且是自洽的。如果一个节点没有父节点,那么它的PID就等于它自己的ID。这并不违反我们关于主外键的定义。
信息的管理与使用
树表的结构确定后,问题就集中在如何读写其中的数据。
增加节点:增加一个叶节点很简单,只要指定这个节点的父节点,把它“挂接”到TreeView中。从递归的角度来看,我们可以重复这一步骤,真到插入一个完整的子树。相对而言,比较麻烦的是,如何把一个子节点插入到现有的树中间,而不是最末端。比如存在一个节点N,它的根为R,现在要在R和N之间插入一个新节点N’,我们可以这样做:把N’挂在R下面,作为它的子节点,然后把N的父节点指定为N’即可。
修改节点:这里指修改树结构,改变某一节点的父节点,在这种结构中,修改是很方便的,只要调用标准的update就可以。 1 <script type="text/javascript"> </script> <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>
<script type="text/javascript">
</script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>