You are on page 1of 9

Hoang Nguyen – vietnam_hoangminhnguyen@yahoo.com from http://tech-vnit.tk (or: kattyflea.co.

cc)

Lưu trữ dữ liệu phân cấp trong 1 database


( hierarchical data )
0. Foreword
Đây chỉ là bài nghiên cứu cá nhân, giúp các bạn chưa biết có cái nhìn dễ hơn về lưu trữ dữ liệu
dạng phân cấp trong database
Mọi ý kiến đóng góp của các bạn đều welcome để hoàn thiện hơn tài liệu này.
Các bạn hãy gửi feedback về : vietnam_hoangminhnguyen@yahoo.com hoặc commet qua
website: http://tech-vnit.tk ( hoặc http://kattyflea.co.cc )

1. Giới thiệu
Bất cứ khi nào chúng ta muốn xây dựng forum, gửi các message từ mail list trên website, viết
CMS: sẽ có một lúc nào đó chúng ta sẽ muốn lưu trữ dữ liệu phân cấp trong 1 database ( trong tài liệu
này dữ liệu phân cấp sẽ được hiểu là : hierarchical data ).
Việc lưu trữ có thể thực hiện thông qua XML ( một kiểu lưu trữ dữ liệu theo kiểu cấu trúc phân
cấp), rất phù hợp với dữ liệu phân cấp. vậy còn đối với table database thì sao, rõ ràng như chúng ta thấy
thì tables không phải là kiểu lưu trữ dữ liệu phân cấp mà nó lưu dưới dạng các mối quan hệ giữa các
bảng => tables chỉ là kiểu flat list.
Vậy thì làm sao chúng ta lưu được dữ liệu kiểu phân cấp trong một table, trong bài hướng dẫn
này chúng ta sẽ cùng tìm ra cách để làm chiện đó )
Lưu trữ dạng cây là một vấn đề hết sức phổ biến với những giải pháp lưu trữ khác nhau. Nhưng
vẫn có 2 cách tiếp cận việc lưu trữ dữ liệu dạng cây mà hay được sử dụng đó là:
+ mô hình danh sách kế cận ( the adjacency list model )
+ thuật toán duyệt cây theo thứ tự đã được định trước đó.
Trong bài hướng dẫn này, chúng ta sẽ khám phá 2 phương pháp trên bằng cách xem xét qua 1 ví
dụ. Dùng cây sau cho ví dụ của chúng ta:
Hoang Nguyen – vietnam_hoangminhnguyen@yahoo.com from http://tech-vnit.tk (or: kattyflea.co.cc)

2. Mô hình danh sách kế cận


Mô hình danh sách kế cận hay còn gọi là phương pháp đệ quy là một phương pháp đơn giản và
phổ biến cho những ứng dụng nhỏ bởi vì cơ chế của nó chỉ là duyệt qua tất cả các phần tử của cây.
Trong cây ở trên, table thực chất là một danh sách các phần tử liền kề :

Như ở bảng trên bạn cũng hình dung được phần nào, trong phương pháp danh sách liền kề.
Chúng ta sẽ lưu 'parent' của mỗi node (như bạn đã thấy cột parent ở trên): “Pear” là một node con của
“Fruit”, “Red” là node con của “Fruit” …

Giải sử trong database của bạn đã lưu như ở trên, bây giờ chúng ta sẽ thực hiện việc lấy dữ liệu
phân cấp ra. Phương pháp như sau: chúng ta sẽ bắt đầu ở node cha ( root node ) và sau đó sẽ lấy các
node con của node cha đó. Đối với mỗi node con, chúng ta lại tiếp tục lấy các node của con của node
con đó và tiếp tục làm như vậy cho tới khi không còn node con nào.... ( chúng ta cũng thấy được ý
nghĩa đệ qui ở đây... )

Ví dụ đoạn code sau sẽ thực hiện việc mô hình danh sách liền kề ( ở đây mình viết bằng php ).
Các bạn có thể dựa vào đó để chuyển qua ngôn ngữ mà bạn sử dụng )
Hoang Nguyen – vietnam_hoangminhnguyen@yahoo.com from http://tech-vnit.tk (or: kattyflea.co.cc)

Giả sử đã có tree ( lưu dạng table database )

Kết quả của chúng ta như sau:

như hình trên thì:


Foot----Fruit
| |-------- Red
| | |--------------Cherry
| |--------Yellow
| |--------------Banana
|--------Meat
|---------Beef
|---------Pork
Hoang Nguyen – vietnam_hoangminhnguyen@yahoo.com from http://tech-vnit.tk (or: kattyflea.co.cc)

Path của một node


Nếu bạn chúng ta đã biết tên hoặc id của 1 node thì việc tìm ra path trong tree của nó cũng rất
dễ dàng. Ví dụ, path dẫn tới 'Cherry' là 'Food' → 'Fruit' → 'Red'. Để lấy được path này, function của
chúng ta phải bắt đầu từ level sâu nhất là 'Cherry', sau đó sẽ tìm cha của node này và tiếp tục tìm node
cha của node vừa tìm.... cho tới khi tới root node.

Cũng tương tự như đoạn code trước, code tìm path chúng ta cũng thực hiện đệ qui ( bởi vậy mà
phương pháp mô hình danh sách gần kề còn được gọi là phương pháp đệ qui như đã nói ở trên )

nếu $node ='Cherry' thì phương thức trên sẽ trả về giá trị $path là một mảng, nó sẽ có dạng như
sau:
Array
(
[0] => Food
[1] => Fruit
[2] => Red
)
Hoang Nguyen – vietnam_hoangminhnguyen@yahoo.com from http://tech-vnit.tk (or: kattyflea.co.cc)

Ở trên chúng ta đã đi qua phương pháp mô hình danh sách kế cận, như các pạn đã thấy thì
phương pháp này đơn giản, dễ hiểu. Nhưng có một điểm bất lợi của phương pháp này đó là nó rất là
chậm ( do chúng ta sử dụng hàm đệ qui và chi phí cho việc query toàn bộ table rất tốn kém tài nguyên )

3. Duyệt theo thứ tự đã được định trước


Bây giờ chúng ta hãy xem xét tới một phương pháp khác trong việc lưu trữ dữ liệu. Việc sử
dụng đệ qui thì rất chậm do đó chúng ta ít khi sử dụng phương pháp đệ qui và ngoài ra cũng giảm mức
tối thiểu số lượng query database.

Hồi nãy chúng ta trình bày cây theo chiều dòng, bây giờ hãy xem xét nó theo hướng chiều
ngang. Đầu tiên bắt đầu ở root node ( 'Food' ) và đánh số thứ tự cho nó là 1. Tiếp theo là đến 'Fruit” và
đánh số cho nó là 2. Bằng cách này, bạn sẽ đi qua các các node và đánh số tương ứng ở mỗi phía của
node đó ..... Số cuối cùng là được viết phía bên phải của node 'Food'. Như hình bên dưới, chúng ta thấy
toàn bộ cây đã được đánh số, và mũi tên tương ứng chỉ thứ tự số đã được gán cho mỗi node

Chúng ta gọi những số này là những số bên trái và số bên phải( ví dụ: số bên trái của 'Food' là 1,
số bên phải của nó là 18 ). Như bạn thấy ở hình trên thì, những số này biểu thị mối quan hệ của mỗi
node. Ví dụ, 'Red' có số 3 và số 6 => chứng tỏ nó là cháu chắt gì đó của node 'Food' ( có giá trị 1 và 18
). Tương tự, chúng ta thấy các số có giá trị bên trái lớn hơn 2 và nhỏ hơn 11 thì sẽ là hậu duệ của 'Fruit'
( vì Fruit có giá trị là 2-11 ). ==> như vậy, cấu trúc cây của chúng ta bây giờ được lưu trữ theo kiểu giá
trị trái và phải.
Hoang Nguyen – vietnam_hoangminhnguyen@yahoo.com from http://tech-vnit.tk (or: kattyflea.co.cc)

Trước khi chúng ta tiếp tục, thì hãy xem bảng giá trị bên dưới:

***note***
các từ 'left' và 'right' có ý nghĩa đặc biệt trong SQL, do đó mà chúng ta phải sử dụng 'lft' và 'rgt' để đánh
tên cho column trong bảng của chúng ta ( tránh trùng với từ khóa của SQL ). Ngoài ra, bây giờ chúng
ta thực sự không cần cột 'parent' nữa. Bây giờ, chúng ta có giá trị 'lft' và 'rgt' để lưu trữ cấu trúc của cây

Cách để lấy dữ liệu


Để hiển thị dữ liệu cây sử dụng table với có các cột left và right, thì việc đầu tiên chúng ta cần
làm là xác định node mà chúng ta muốn lấy dữ liệu. Ví dụ, nếu mà bạn muốn cây con 'Fruit', bạn chỉ
cần chọn lựa các node mà có giá trị bên trái nằm giữa 2 và 11. Trong Sql, sẽ như sau:

SELECT * FROM tree WHERE lft BETWEEN 2 AND 11;

Kết quả thu được như sau:

với một câu query đơn giản, chúng ta dễ dàng thu về dữ liệu mong muốn mà ko cần phải duyệt
qua từng node như trong code php đã làm trước đó ( trong phần ví dụ ở trên )

Công việc còn lại là thực hiện indentation để hiển thị cấu trúc của cây, các node con sẽ được
indent so với node cha của nó. Chúng ta có thể làm việc này bằng cách giữ 1 stack của giá trị bên phải.
Mỗi lần chúng ta bắt đầu một node con của node đó, thì chúng ta phải thêm giá trị bên phải của nó đó
tới stack. Như bạn đã biết thì tất cả các node con của node cha đó đều có giá trị bên pahri nhỏ hơn giá
Hoang Nguyen – vietnam_hoangminhnguyen@yahoo.com from http://tech-vnit.tk (or: kattyflea.co.cc)

trị của node cha, vì vậy bằng cách so sánh giá trị bên phải của node hiện tại với giá trị bên phải của
node trước đó trong stack thì bạn sẽ thấy được sự phân cấp rõ ràng trong cấu trúc cây của chúng.
Các bạn xem đoạn code bên dưới:
Hoang Nguyen – vietnam_hoangminhnguyen@yahoo.com from http://tech-vnit.tk (or: kattyflea.co.cc)

Cách để lấy path của node


đơn giản chỉ bằng lệnh SQL sau:
SELECT title FROM tree WHERE lft < 4 AND rgt > 5 ORDER BY lft ASC;
kết quả chúng ta có được sau lệnh query:

+-------+
| title |
+-------+
| Food |
| Fruit |
| Red |
+-------+

Cách tạo table


Vậy để tạo bảng có 2 cột và đánh số như trên thì như thế nào, nếu bạn làm bằng tay thì đó quả
thực là một vấn đề lớn, vì nếu table của bạn có tới hàng nghìn hàng → haizzzz mệt đấy.

Nhưng pạn yên tâm, với đoạn script đơn giản sau, việc đó sẽ được thực hiện một cách dễ dàng
( với điều kiện là bạn đã có sẵn table, việc tiếp theo là thêm cột left, right và điền giá trị vào cho nó )
Hoang Nguyen – vietnam_hoangminhnguyen@yahoo.com from http://tech-vnit.tk (or: kattyflea.co.cc)

Cách tạo table


Thêm 1 node vào trong cấu trúc cây thì chúng ta cũng thực hiện cực kỳ đơn giản bằng 2 câu
lệnh SQL sau:

UPDATE tree SET rgt=rgt+2 WHERE rgt>5;


UPDATE tree SET lft=lft+2 WHERE lft>5;

Một điểm bất lợi của phương pháp này đó là update thì chúng ta phải thực hiện update 2 lần
cho 2 giá trị phải và trái tương ứng. Dĩ nhiên, lợi điểm lớn nhất của phương pháp này đó là tốc độ thực
hiện của nó lớn hơn rất nhìu so với phương pháp mô hình danh sách kế cận.

Welcome mọi feedback của các bạn để chúng ta sẽ có một bản full hơn về vấn đề: làm thế
nào để lưu trữ kiểu dữ liệu phân cấp trong table database

You might also like