樹型關係的數據,例如常見的類別表,即一個大類,下面有若干個子類,某些子類又有子類這樣的情況。當類別不確定,用戶希望可以在任意類別下添加新的子類,或者刪除某個類別和其下的所有子類,而且預計以後其數量會逐步增長,此時就會考慮用一個數據表來保存這些數據。
類別表_1(Type_table_1)
名稱 類型 約束條件 說明
type_id int 無重複 類別標識,主鍵
type_name char(50) 不允許爲空 類型名稱,不允許重複
type_father int 不允許爲空 該類別的父類別標識,如果是頂節點的話設定爲某個唯一值
這樣的設計短小精悍,完全滿足3NF,而且可以滿足用戶的所有要求。
我們來估計一下用戶希望如何羅列出這個表的數據的。對用戶而言,他當然期望按他所設定的層次關係一次羅列出所有的類別,例如這樣:
總類別
類別1
類別1.1
類別1.1.1
類別1.2
類別2
類別2.1
類別3
類別3.1
類別3.2
……
爲了實現這樣的列表顯示(樹的先序遍歷),要對上面的表進行多少次檢索?注意,儘管類別1.1.1可能是在類別3.2之後添加的記錄,答案仍然是N次。這樣的效率對於少量的數據沒什麼影響,但是日後類型擴充到數十條甚至上百條記錄後,單單列一次類型就要檢索數十次該表,整個程序的運行效率就不敢恭維了。只要對數據表進行一定的擴充,再對添加類型的數量進行一下約束就行了,要完成上面的列表只需一次檢索就行了。下面是擴充後的數據表結構:
類別表_2(Type_table_2)
名稱 類型 約束條件 說明
type_id int 無重複 類別標識,主鍵
type_name char(50) 不允許爲空 類型名稱,不允許重複
type_father int 不允許爲空 該類別的父類別標識,如果是頂節點的話設定爲某個唯一值
type_layer char(6) 限定3層,初始值爲000000 類別的先序遍歷,主要爲減少檢索數據庫的次數
按照這樣的表結構,我們來看看上面例子記錄在表中的數據是怎樣的:
type_id type_name type_father type_layer
1 總類別 0 000000
2 類別1 1 010000
3 類別1.1 2 010100
4 類別1.2 2 010200
5 類別2 1 020000
6 類別2.1 5 020100
7 類別3 1 030000
8 類別3.1 7 030100
9 類別3.2 7 030200
10 類別1.1.1 3 010101
……
現在按type_layer的大小來檢索一下:SELECT * FROM Type_table_2 ORDER BY type_layer
列出記錄集如下:
type_id type_name type_father type_layer
1 總類別 0 000000
2 類別1 1 010000
3 類別1.1 2 010100
10 類別1.1.1 3 010101
4 類別1.2 2 010200
5 類別2 1 020000
6 類別2.1 5 020100
7 類別3 1 030000
8 類別3.1 7 030100
9 類別3.2 7 030200
……
現在列出的記錄順序正好是先序遍歷的結果。在控制顯示類別的層次時,只要對type_layer字段中的數值進行判斷,每2位一組,如大於0則向右移2個空格。當然,我這個例子中設定的限制條件是最多3層,每層最多可設99個子類別,只要按用戶的需求情況修改一下type_layer的長度和位數,即可更改限制層數和子類別數。其實,上面的設計不單單隻在類別表中用到,網上某些可按樹型列表顯示的論壇程序大多采用類似的設計。
或許有人認爲,Type_table_2中的type_father字段是冗餘數據,可以除去。如果這樣,在插入、刪除某個類別的時候,就得對type_layer 的內容進行比較繁瑣的判定,所以我並沒有消去type_father字段,這也正符合數據庫設計中適當保留冗餘數據的來降低程序複雜度的原則,後面我會舉一個故意增加數據冗餘的案例。