0%

2012年主要做了几件事:
(1)年初时,自学了一阵linux运维,但由于学习效率太低。于是到了四月份,就拜师学艺了。从六月份到现在一直坚持在学习。初级运维算是没问题了,现在是在研究中级运维的知识。收获是蛮大的,有时候早上起来听视频写文档,周未也窝在家研究,半年下来,linux基本原理、100多个命令操作、系统安装与调优、软件的安装与配置与调优等等都基本OK了。
(2)下半年研究起了JVM调优方面的知识,现在也是有点心得,跟Q群的人在YY上做了一次分享。
(3)本来想学习hadoop的,由于时间关系,只能放到明年了。
(4)12月份研究了一下lucene的源码。知道了搜索引擎的理论上的知识,只看了lucene索引部分的源码,搜索部分还要抽时间精力完善,文档也待补充。

2013年计划:
(1)继续学习linux运维(中高级)的知识。初级运维的基本都是单机房的问题,现在开始研究多机房的问题。 2013-06-29 写下了许多文档,部分问题还要钻研!
(2)学习大数据处理Hadoop。
2013-06-29 正在跟tigerfish 学hadoop
(3)学习数据分析与处理,也顺便研究一下数学方面的知识。现在已经花了400RMB报了tigerfish一个R语言学习班,不知道学完是否能拿回300RMB先?
2013-06-29 已完成,获优异证书。学习也有所获
(4)工作学习的PC转到MacBook Pro上。
2013-06-29 离不开她了!
(5)全面实践GTD与番茄工作法。
(6)最重要的一点:计划每个月写4、5篇博客,一年下来能坚持写50篇左右。
(7)熟悉python。虽然现在能用python写点简单的东西,但离精通还远着呢!
(8)坚持看英文原版图书。目前有两本书-《High Performance MySQL》,《MySQL High Availability》待看。
(9)研究jdk(部分)、spring等框架底层源码

目录

1
2
3
4
5
6
7
8
9
1.	团队思考
1.1. toruk框架的启示
1.2. hui99的问题
1.3. cmmi3评估中暴露的问题
(1) 杨ting在访谈前背"答案"时才知道OSSP是什么?
(2) 我们作为“编码测试”人员,在访谈时却被问了许多需求设计、配置、QA的问题,我们几个人都没回答上来。
1.4. 代码是否可以共享?
2. 员工、团队与成长
3. 总结

团队思考

toruk框架的启示

toruk框架目标

1
2
a)	相对于hui99使用的spring+ibatis2框架(或其它框架),使开发过程更加简单,提高效率;
b) 使公司具有不可替代的元素。turuk便是其中之一。

(1) 如果想开发过程简单,是否有更好的开源框架(技术/方案)?
如使用spring3 REST风格,使用Spring的注释式编程!
(2) 如果非要开发toruk,是否数据库这块可重用ibatis2而不是重新造轮子?
(3) 部分项目已经使用了toruk框架,可能以后还有项目会用到。

1
2
3
4
5
toruk的研发人员全部离职之后,toruk框架的理念、技术、知识、代码真的传下来了吗?
还有人非常了解toruk的源码吗?
还有人去主导toruk的研发吗?
toruk存在的bug,还会有人去fix吗?
toruk还会在公司存活多久呢?

(4) toruk(或其它)的知识为什么没有完全成为公司的财产,而随着人员的流失而流失了?是什么原因造成了这些问题?

hui99的问题

hui99有很多频道:机票、酒店、旅游、团购、租车、购物、电子优惠券、手机充值、社区等, 还有一个IVR系统在使用!去年年底的时候又加了一个分销商/积分系统上去。当初开发分配任务时,基本上都是每个频道由某个人单独负责的。从去年开始,陆续有人离职了,项目转由我整体负责了。但我对其它各频道业务不是很清楚,而相关负责的人已经离职,没有留下太多的文档(或文档已经过时),代码风格不一(包括缩进、代码的格式化都或多或少有点问题)、结构混乱,中间夹杂点小bug,也确实让人头痛。

1
2
3
4
(1)	“每个人按照频道划分开发任务”,这种任务分配的模式是否过时?
或者说每个频道的功能还可以分得细一点,然后团队一起开发。或每个频道进行交叉开发。
(2) 开发过程中,团队间是否可以评审一下代码,而保持风格的一致性?
(3) 同事离职了,你怎样才能更快的理解他的负责频道(或模块)业务和代码?

cmmi3评估中暴露的问题

公司今年过完年之后,确定实施CMMI3评审,而评审工作在4月份开始进行。两个月时间,四个项目(三个虚假项目),上千个文档需要准备。4月10多号开始背”答案”,4月22日开始访谈,我是作为”编码测试”人员进行访谈的。
评审过程中,有两件事情让我印象比较深刻:

杨ting在访谈前背”答案”时才知道OSSP是什么?

她已经写文档两个月了,所有的CMMI3的流程和文档都是根据OSSP来编写的。
这是谁的问题?个人问题?石老师(培训老师)的问题?CMMI负责人的问题?公司培训的问题?…
这到底是什么问题?如何去解决这个问题?

我们作为“编码测试”人员,在访谈时却被问了许多需求设计、配置、QA的问题,我们几个人都没回答上来。

几天背”答案”下来没有什么效果,主任评估师都没问到(你想让他问的)问题上。CMMI中编码测试不是编码测试人员的问题,团队成员间的工作都是有关联的。
在团队中,你知道别人做的具体工作吗?你知道别人的工作对你的影响吗?你真的知道你负责工作的内容吗?

代码是否可以共享?

去年年底的时候,建行团要改版(代理商版)。
具体的开发任务分配是:国栋负责平台后台、我负责分行后台、杨文涛负责商户后台。其中有一个功能模块:团购商品状态转换的, 平台、分行、商户后台都有涉及到,商户后台只有”提交审核”的操作,但分行后台和平台后台包括商品状态的所有操作。
基本功能介绍如下:

1
2
3
4
5
6
7
商品状态:暂存、待审核、上架。
相关操作有:提交审核、上架、下架、重新编辑。
状态转换关系是:
暂存 -提交审核 -> 待审核
待审核 -上架 -> 上架
上架 -下架 -> 待审核
待审核 -重新编辑 -> 暂存

我在任务确定后,就忙着做其它项目去了。我一回来,就code自已负责的代码去了。

但问题来了,状态转换的操作我是通过商品状态机类ProductStateMachine实现的,其它地方要用的话直接调就行了,商户后台这边则是直接操作数据库。同样的功能,实现的方式不同,方法的命名不同,若不修改就会给后期的维护带来问题。

为什么会出现此类的问题呢?任务确定之后,各人只埋头负责自己的部分,而完全不理会其他人的工作,只要功能实现了,其它的都是小问题。事实真的是这样吗?当别人提出你的功能有更好的实现方案时,所有人都会心甘情愿的修改自己”辛辛苦苦”写的代码吗?团队代码风格、方法命名等如何保持一致呢?…

你也许会觉得责任在于分配任务的人,为什么分的任务会有交叉呢?但(1)是人总会犯点错;(2)这就好像项目的成败决定于分配任务的人一样,个人决定成败,而非整个团队。

可以思考的问题:

1
2
(1)	任务为什么要这么分?是不是可以做得更好?
(2) 代码是否可以共享?

员工、团队与成长

新人进公司一年多来快速成长了吗?如果没成长,是招聘时看错人了吗?是员工的个人问题,还是公司或团队本身的问题?

公司需要生存发展,员工需要更好的工资(尤其是物价在疯涨的时候)、更好的发展,公司与员工是互相博弈的过程。

薪资不高、没有发展、没有成长,这不是在耽误人家的宝贵青春吗?

1
2
3
4
但公司的发展靠的是人啊?
(1) 好的氛围,才会引入、留住好的人。
(2) 好的人,才会有好的制度,并且保持好这个制度,毕竟制度是人定的。
(3) 好的人和好的制度,才会遇到好的客户。

是先有项目才有人,还是有了人,才会有项目?

做项目是仅仅为了做项目吗?如何打造一个全面成功的项目?

项目开发中,团队真的在成长吗?

《走出软件作坊》 阿朱

在项目管理中,我发现人是最重要的,但是很遗憾,总是能看到不少心胸狭窄、眼光短浅的老板,把员工当傻瓜,把客户当傻子。其实人对了,世界就对了。

《从优秀到卓越》 柯林斯

实现跨越公司的领导者首先是设法得到合适的人才(不合适的下车),然后才决定将汽车开向何方。

五年思想工作者 梦想风暴

去年带了一个项目,在那个项目里,我做了一个尝试。我把项目的目标订在了人才培养,而非传统意义的交付。于是,在这个过程中,我花了大量的精力去教新员工写代码,帮他们设定个人成长目标,教他们做事,带着他们吃好玩好……就是这样一个几乎都是由新ThoughtWorker——甚至大多是毕业生——组成的项目组,后来,这个项目成了Michael Chen口中最接近他心目中全面成功的项目。

总结

  • 员工离职之后,知识、经验会留下来吗?
  • 团队中的知识在流通吗?
  • 项目中,团队成员在成长吗?团队在成长吗?团队会反思总结吗?
  • 除了员工自我驱动外,公司有为员工做过职业规划吗?
  • 团队怎样才能写出更好的代码(Beautiful Code)?

你需要什么样的团队?

本文参考了:
Mike Hillyer的《Managing Hierarchical Data in MySQL》
及Yimin的翻译版《MYSQL中分层数据的管理》

#8.新增节点
到现在,我们已经知道了如何去查询我们的树,是时候去关注一下如何增加一个新节点来更
新我们的树了。让我们再一次观察一下我们的嵌套集合图:

Alt text

当我们想要在TELEVISIONS和PORTABLE ELECTRONICS节点之间新增一个节点,新节点的lft和rgt 的 值为10和11,所有该节点的右边节点的lft和rgt值都将加2,之后我们再添加新节点并赋相应的lft和rgt值。在MySQL 5中可以使用存储过程来完成,我假设当前大部分读者使用的是MySQL 4.1版本,因为这是最新的稳定版本。所以,我使用了锁表(LOCKTABLES)语句来隔离查询:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
--增加平行节点
procedure add_node(
cname nested_category.name%type,
add_name nested_category.name%type
)
is
myRight nested_category.rgt%type ;
BEGIN
SELECT rgt into myRight FROM nested_category WHERE name = cname;
UPDATE nested_category SET rgt = rgt + 2 WHERE rgt > myRight;
UPDATE nested_category SET lft = lft + 2 WHERE lft > myRight;
INSERT INTO nested_category(CATEGORY_ID,name, lft, rgt) VALUES(SEQ_CATEGORY.nextval,add_name, myRight + 1, myRight + 2);
commit;
exception
when others then
rollback;
end;
1
EXECUTE pkg_category.add_node('TELEVISIONS','GAME CONSOLES');

我们可以检验一下新节点插入的正确性:

1
2
3
4
5
SELECT  lpad( '+',  (COUNT(parent.name)-1),'+')||node.name name
FROM nested_category node, nested_category parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.name,node.lft
ORDER BY node.lft;

输出结果

1
2
3
4
5
6
7
8
9
10
11
12
13
NAME
----------------------------------
ELECTRONICS
+TELEVISIONS
++TUBE
++LCD
++PLASMA
+GAME CONSOLES
+PORTABLE ELECTRONICS
++MP3 PLAYERS
+++FLASH
++CD PLAYERS
++2 WAY RADIOS

如果我们想要在叶子节点下增加节点,我们得稍微修改一下查询语句。让我们在2 WAY RADIOS叶子节点下添加FRS节点吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
--在叶子节点下增加节点
procedure add_child_node(
cname nested_category.name%type,
add_name nested_category.name%type
)
is
myLeft nested_category.lft%type ;
BEGIN
SELECT lft into myLeft FROM nested_category WHERE name = cname;
UPDATE nested_category SET rgt = rgt + 2 WHERE rgt > myLeft;
UPDATE nested_category SET lft = lft + 2 WHERE lft > myLeft;
INSERT INTO nested_category(CATEGORY_ID, name, lft, rgt) VALUES(SEQ_CATEGORY.nextval, add_name, myLeft + 1, myLeft + 2);
commit;
exception
when others then
rollback;
end;


EXECUTE pkg_category.add_child_node('2 WAY RADIOS','FRS');


SELECT lpad( '+', (COUNT(parent.name)-1),'+')||node.name name
FROM nested_category node, nested_category parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.name,node.lft
ORDER BY node.lft;

输出结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
NAME
----------------------------------
ELECTRONICS
+TELEVISIONS
++TUBE
++LCD
++PLASMA
+GAME CONSOLES
+PORTABLE ELECTRONICS
++MP3 PLAYERS
+++FLASH
++CD PLAYERS
++2 WAY RADIOS
+++FRS

在这个例子中,我们扩大了新产生的父节点(2 WAY RADIOS节点)的右值及其所有它的右边节点的左右值,之后置新增节点于新父节点之下。正如你所看到的,我们新增的节点已经完全融入了嵌套集合中:

#9.删除节点

最后还有个基础任务,删除节点。删除节点的处理过程跟节点在分层数据中所处的位置有关,删除一个叶子节点比删除一个子节点要简单得多,因为删除子节点的时候,我们需要去处理孤立节点。

删除一个叶子节点的过程正好是新增一个叶子节点的逆过程,我们在删除节点的同时该节点右边所有节点的左右值和该父节点的右值都会减去该节点的宽度值2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
--删除叶子节点
procedure delete_leaf_node(leaf_name nested_category.name%type)
is
myLeft nested_category.lft%type ;
myRight nested_category.rgt%type ;
myWidth integer ;
BEGIN
SELECT lft , rgt , (rgt - lft+ 1) into myLeft , myRight, myWidth FROM nested_category
WHERE name = leaf_name;

DELETE FROM nested_category WHERE lft BETWEEN myLeft AND myRight;
UPDATE nested_category SET rgt = rgt - myWidth WHERE rgt > myRight;
UPDATE nested_category SET lft = lft - myWidth WHERE lft > myRight;
commit;
exception
when others then
rollback;
end;

--我们再一次检验一下节点已经成功删除,而且没有打乱数据的层次:
EXECUTE pkg_category.delete_leaf_node('GAME CONSOLES');

SELECT lpad( '+', (COUNT(parent.name)-1),'+')||node.name name
FROM nested_category node, nested_category parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.name,node.lft
ORDER BY node.lft;

输出结果

1
2
3
4
5
6
7
8
9
10
11
12
13
NAME
--------------------------------------------------------------------------------
ELECTRONICS
+TELEVISIONS
++TUBE
++LCD
++PLASMA
+PORTABLE ELECTRONICS
++MP3 PLAYERS
+++FLASH
++CD PLAYERS
++2 WAY RADIOS
+++FRS

这个方法可以完美地删除节点及其子节点:

EXECUTE pkg_category.delete_leaf_node('MP3 PLAYERS');

再次验证我们已经成功的删除了一棵子树:

1
2
3
4
5
6
7
8
9
10
11
NAME
----------------------------
ELECTRONICS
+TELEVISIONS
++TUBE
++LCD
++PLASMA
+PORTABLE ELECTRONICS
++CD PLAYERS
++2 WAY RADIOS
+++FRS

有时,我们只删除该节点,而不删除该节点的子节点。在一些情况下,你希望改变其名字为占位符,直到替代名字的出现,比如你开除了一个主管(需要更换主管)。在另外一些情况下,你希望子节点挂到该删除节点的父节点下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
--删除该节点,而不删除该节点的子节点
procedure delete_node_only(node_name nested_category.name%type)
is
myLeft nested_category.lft%type ;
myRight nested_category.rgt%type ;
myWidth integer ;
BEGIN
SELECT lft , rgt , (rgt - lft+ 1) into myLeft , myRight, myWidth FROM nested_category
WHERE name = node_name;

DELETE FROM nested_category WHERE lft = myLeft;
UPDATE nested_category SET rgt = rgt - 1, lft = lft - 1 WHERE lft BETWEEN myLeft AND myRight;
UPDATE nested_category SET rgt = rgt - 2 WHERE rgt > myRight;
UPDATE nested_category SET lft = lft - 2 WHERE lft > myRight;
commit;
exception
when others then
rollback;
end;

在这个例子中,我们对该节点所有右边节点的左右值都减去了2(因为不考虑其子节点,该节点的宽度为2),对该节点的子节点的左右值都减去了1(弥补由于失去父节点的左值造成的裂缝)。我们再一次确认,那些节点是否都晋升了:

1
2
3
4
5
6
7
EXECUTE pkg_category.delete_node_only('PORTABLE ELECTRONICS');

SELECT lpad( '+', (COUNT(parent.name)-1),'+')||node.name name
FROM nested_category node, nested_category parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.name,node.lft
ORDER BY node.lft;

输出结果

1
2
3
4
5
6
7
8
9
10
11
12
NAME
---------------------
ELECTRONICS
+TELEVISIONS
++TUBE
++LCD
++PLASMA
+CD PLAYERS
+2 WAY RADIOS
++FRS

8 rows selected

有时,当删除节点的时候,把该节点的一个子节点挂载到该节点的父节点下,而其他节点挂到该节点父节点的兄弟节点下,考虑到篇幅这种情况不在这里解说了。

#10.相关包sql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
create or replace package pkg_category  is
--增加平行节点
procedure add_node(
cname nested_category.name%type,
add_name nested_category.name%type
);

--在叶子节点下增加节点
procedure add_child_node(
cname nested_category.name%type,
add_name nested_category.name%type
);

--删除叶子节点
procedure delete_leaf_node(leaf_name nested_category.name%type);

--删除该节点,而不删除该节点的子节点
procedure delete_node_only(node_name nested_category.name%type);

end pkg_category;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
create or replace package body pkg_category is

--增加平行节点
procedure add_node(
cname nested_category.name%type,
add_name nested_category.name%type
)
is
myRight nested_category.rgt%type ;
BEGIN
SELECT rgt into myRight FROM nested_category WHERE name = cname;
UPDATE nested_category SET rgt = rgt + 2 WHERE rgt > myRight;
UPDATE nested_category SET lft = lft + 2 WHERE lft > myRight;
INSERT INTO nested_category(CATEGORY_ID,name, lft, rgt) VALUES(SEQ_CATEGORY.nextval,add_name, myRight + 1, myRight + 2);
commit;
exception
when others then
rollback;
end;

--在叶子节点下增加节点
procedure add_child_node(
cname nested_category.name%type,
add_name nested_category.name%type
)
is
myLeft nested_category.lft%type ;
BEGIN
SELECT lft into myLeft FROM nested_category WHERE name = cname;
UPDATE nested_category SET rgt = rgt + 2 WHERE rgt > myLeft;
UPDATE nested_category SET lft = lft + 2 WHERE lft > myLeft;
INSERT INTO nested_category(CATEGORY_ID, name, lft, rgt) VALUES(SEQ_CATEGORY.nextval, add_name, myLeft + 1, myLeft + 2);
commit;
exception
when others then
rollback;
end;


--删除叶子节点
procedure delete_leaf_node(leaf_name nested_category.name%type)
is
myLeft nested_category.lft%type ;
myRight nested_category.rgt%type ;
myWidth integer ;
BEGIN
SELECT lft , rgt , (rgt - lft+ 1) into myLeft , myRight, myWidth FROM nested_category
WHERE name = leaf_name;

DELETE FROM nested_category WHERE lft BETWEEN myLeft AND myRight;
UPDATE nested_category SET rgt = rgt - myWidth WHERE rgt > myRight;
UPDATE nested_category SET lft = lft - myWidth WHERE lft > myRight;
commit;
exception
when others then
rollback;
end;

--删除该节点,而不删除该节点的子节点
procedure delete_node_only(node_name nested_category.name%type)
is
myLeft nested_category.lft%type ;
myRight nested_category.rgt%type ;
myWidth integer ;
BEGIN
SELECT lft , rgt , (rgt - lft+ 1) into myLeft , myRight, myWidth FROM nested_category
WHERE name = node_name;

DELETE FROM nested_category WHERE lft = myLeft;
UPDATE nested_category SET rgt = rgt - 1, lft = lft - 1 WHERE lft BETWEEN myLeft AND myRight;
UPDATE nested_category SET rgt = rgt - 2 WHERE rgt > myRight;
UPDATE nested_category SET lft = lft - 2 WHERE lft > myRight;
commit;
exception
when others then
rollback;
end;

end pkg_category;

本文参考了:
Mike Hillyer的《Managing Hierarchical Data in MySQL》
及Yimin的翻译版《MYSQL中分层数据的管理》

#6.检索节点的直接子节点

可以想象一下,你在零售网站上呈现电子产品的分类。当用户点击分类后,你将要呈现该分类下的产品,同时也需列出该分类下的直接子分类,而不是该分类下的全部分类。为此,我们只呈现该节点及其直接子节点,不再呈现更深层次的节点。例如,当呈现PORTABLE ELECTRONICS分类时,我们同时只呈现MP3 PLAYERS、CD PLAYERS和2 WAY RADIOS分类,而不呈现FLASH分类。
要实现它非常的简单,在先前的查询语句上添加HAVING子句:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
SELECT node.name, (COUNT(parent.name) -(
sub_tree.depth + 1)) depth
FROM nested_category node,
nested_category parent,
nested_category sub_parent,
(
SELECT node.name, (COUNT(parent.name) -1)
AS depth
FROM nested_category node,
nested_category parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.name = 'PORTABLE ELECTRONICS'
GROUP BY node.name
ORDER BY node.lft
) sub_tree
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt
AND sub_parent.name = sub_tree.name
GROUP BY node.name,sub_tree.depth
HAVING (COUNT(parent.name) -(sub_tree.depth + 1)) <= 1
1
2
3
4
5
6
NAME DEPTH
-------------------- ----------
PORTABLE ELECTRONICS   0
CD PLAYERS          1
2 WAY RADIOS       1
MP3 PLAYERS         1

#7.嵌套集合模型中集合函数的应用

让我们添加一个产品表,我们可以使用它来示例集合函数的应用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
CREATE TABLE product(product_id,
product_id INT PRIMARY KEY,
name VARCHAR(40),
category_id INT NOT NULL
);

-- Create sequence
create sequence SEQ_PRODUCT
minvalue 1
maxvalue 999999999
start with 1
increment by 1
nocache;


INSERT INTO product(product_id, name, category_id) VALUES(SEQ_PRODUCT.NEXTVAL, '20" TV',3);
INSERT INTO product(product_id, name, category_id) VALUES(SEQ_PRODUCT.NEXTVAL, '36" TV',3);
INSERT INTO product(product_id, name, category_id) VALUES(SEQ_PRODUCT.NEXTVAL, 'SuperLCD42"',4);
INSERT INTO product(product_id, name, category_id) VALUES(SEQ_PRODUCT.NEXTVAL, 'UltraPlasma62"',5);
INSERT INTO product(product_id, name, category_id) VALUES(SEQ_PRODUCT.NEXTVAL, 'Value Plasma 38"',5);
INSERT INTO product(product_id, name, category_id) VALUES(SEQ_PRODUCT.NEXTVAL, 'PowerMP35gb',7);
INSERT INTO product(product_id, name, category_id) VALUES(SEQ_PRODUCT.NEXTVAL, 'SuperPlayer1gb',8);
INSERT INTO product(product_id, name, category_id) VALUES(SEQ_PRODUCT.NEXTVAL, 'Porta CD',9);
INSERT INTO product(product_id, name, category_id) VALUES(SEQ_PRODUCT.NEXTVAL, 'CD To go!',9);
INSERT INTO product(product_id, name, category_id) VALUES(SEQ_PRODUCT.NEXTVAL, 'Family Talk 360',10);

现在,让我们写一个查询语句,在检索分类树的同时,计算出各分类下的产品数量:

1
2
3
4
5
6
7
SELECT parent.name, COUNT(product.name)
FROM nested_category node ,
nested_category parent,
product
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.category_id = product.category_id
GROUP BY parent.name;

这条查询语句在检索整树的查询语句上增加了COUNT和GROUP BY子句,同时在WHERE子句中引用了product表和一个自连接。

本文参考了:
Mike Hillyer的《Managing Hierarchical Data in MySQL》
及Yimin的翻译版《MYSQL中分层数据的管理》

#3.检索单一路径

在嵌套集合模型中,我们可以不用多个自连接就可以检索出单一路径:

1
2
3
4
5
6
SELECT parent.name
FROM nested_category node,
nested_category parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.name = 'FLASH'
ORDER BY parent.lft;

输出结果

1
2
3
4
5
6
NAME
--------------------
ELECTRONICS
PORTABLE ELECTRONICS
MP3 PLAYERS
FLASH

#4.检索节点的深度

我们已经知道怎样去呈现一棵整树,但是为了更好的标识出节点在树中所处层次,我们怎样才能检索出节点在树中的深度呢?我们可以在先前的查询语句上增加COUNT函数和GROUP BY子句来实现:

1
2
3
4
5
SELECT node.name, (COUNT(parent.name) - 1) AS depth
FROM nested_category node,
nested_category parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.name

#5.检索子树的深度
当我们需要子树的深度信息时,我们不能限制自连接中的node或parent,因为这么做会打乱数据集的顺序。因此,我们添加了第三个自连接作为子查询,来得出子树新起点的深度
值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
SELECT node.name, (COUNT(parent.name) -(sub_tree.depth + 1))   depth
FROM nested_category node,
nested_category parent,
nested_category sub_parent,
(
SELECT node.name, (COUNT(parent.name) - 1)
AS depth
FROM nested_category node,
nested_category parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.name = 'PORTABLE ELECTRONICS'
GROUP BY node.name
) sub_tree
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt
AND sub_parent.name = sub_tree.name
GROUP BY node.name,node.lft,sub_tree.depth
ORDER BY node.lft;

这个查询语句可以检索出任一节点子树的深度值,包括根节点。这里的深度值跟你指定的节点有关。