国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 開(kāi)發(fā) > 綜合 > 正文

SQL Story(十一)--樹(shù)狀表游戲

2024-07-21 02:08:55
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友
國(guó)內(nèi)最大的酷站演示中心!

樹(shù)狀結(jié)構(gòu)的存儲(chǔ)與管理,是每一個(gè)在關(guān)系型數(shù)據(jù)庫(kù)平臺(tái)上工作的程序員早晚都要遇到的問(wèn)題。說(shuō)大不大,怎么都能解決,說(shuō)小不小,處理不好,有的是麻煩等著你。仁者見(jiàn)仁,智者見(jiàn)智,公說(shuō)公有理,婆說(shuō)婆有理(誰(shuí)用機(jī)箱砸我?機(jī)箱是個(gè)好東西,亂丟會(huì)摔壞硬盤(pán)的,你看我話沒(méi)說(shuō)完你又把顯示器丟了……),咳咳,好吧,閑話少說(shuō),我們從最大路的處理風(fēng)格談一談吧。這里面的大部分內(nèi)容并非我的獨(dú)創(chuàng),從很久很久以前,數(shù)據(jù)庫(kù)程序員們就這樣做啦。

樹(shù)狀表的結(jié)構(gòu)化表達(dá)

    在一切開(kāi)始前,我們先就樹(shù)狀表的表示方式達(dá)成一個(gè)共識(shí)。在關(guān)系型數(shù)據(jù)庫(kù)中,我們當(dāng)然沒(méi)有辦法這樣直接表示一個(gè)樹(shù):

a

b  c

d e f g

    相應(yīng)的,我們會(huì)把它變形為平面表,這種變形讓我想起拓?fù)鋷缀危?br>
r      n1    n2

a      b     d

a      b     e

a      c     f

a      c     g

存儲(chǔ)結(jié)構(gòu)

稍有經(jīng)驗(yàn)的朋友,大概都不會(huì)試著把樹(shù)狀結(jié)構(gòu)一層一列的存進(jìn)去了吧。這樣做的問(wèn)題是顯而易見(jiàn)的:與表中存儲(chǔ)的信息結(jié)構(gòu)不同,表的結(jié)構(gòu)應(yīng)該是相對(duì)固定的,不能隨便改動(dòng)。而對(duì)于層數(shù)不能固定的普通樹(shù)狀結(jié)構(gòu),這是不可思議的。沒(méi)有必要討論過(guò)多的錯(cuò)誤,我們選擇一個(gè)相對(duì)正確的方式――把樹(shù)狀結(jié)構(gòu)抽象成適合關(guān)系數(shù)據(jù)庫(kù)的形式。

只考慮某一個(gè)節(jié)點(diǎn)的話,和這個(gè)節(jié)點(diǎn)相關(guān)的信息是:它的唯一標(biāo)識(shí)、父節(jié)點(diǎn)、子節(jié)點(diǎn)、數(shù)據(jù)信息。這其中只有子節(jié)點(diǎn)的數(shù)目不定。不過(guò)如果每一個(gè)節(jié)點(diǎn)都確定了自己的父節(jié)點(diǎn),顯然可以省略子節(jié)點(diǎn)。這樣一來(lái),一個(gè)節(jié)點(diǎn)需要存儲(chǔ)三部分信息――它的唯一標(biāo)識(shí)、父節(jié)點(diǎn)、數(shù)據(jù)信息。一個(gè)理想的treeview表只需要三個(gè)節(jié)點(diǎn)就可以了,用sql語(yǔ)句來(lái)表達(dá)就是

create table [dbo].[treeview] (

       [id] [char] (10) primary key,

       [pid] [char] (10) foreign key references [dbo].[treeview],

       [data] [char] (10) 

)

       id是當(dāng)前節(jié)點(diǎn)的唯一標(biāo)識(shí)號(hào),顯然它應(yīng)當(dāng)是主鍵;我們建立的是一個(gè)自閉的存儲(chǔ)結(jié)構(gòu),每一個(gè)節(jié)點(diǎn)的父節(jié)點(diǎn)也應(yīng)當(dāng)出自表中存儲(chǔ)的節(jié)點(diǎn),所以pid列引用id作為外鍵;至于data,它是節(jié)點(diǎn)中的信息,通常和樹(shù)的結(jié)構(gòu)沒(méi)有絕對(duì)關(guān)系。我把這三列全設(shè)成char(10),是為了后面做演示更方便,當(dāng)然也有人喜歡用自動(dòng)標(biāo)識(shí)列來(lái)做主鍵,在這種場(chǎng)合,也自有其優(yōu)點(diǎn)。為了維護(hù)數(shù)據(jù)的完整性或存儲(chǔ)、檢索等方面的考慮,實(shí)用中我們可能會(huì)采用更復(fù)雜的結(jié)構(gòu),不過(guò)骨干就這樣子了。這個(gè)結(jié)構(gòu)從數(shù)學(xué)上講很簡(jiǎn)潔,而且是自洽的。如果一個(gè)節(jié)點(diǎn)沒(méi)有父節(jié)點(diǎn),那么它的pid就等于它自己的id。這并不違反我們關(guān)于主外鍵的定義。

信息的管理與使用

       樹(shù)表的結(jié)構(gòu)確定后,問(wèn)題就集中在如何讀寫(xiě)其中的數(shù)據(jù)。

       增加節(jié)點(diǎn):增加一個(gè)葉節(jié)點(diǎn)很簡(jiǎn)單,只要指定這個(gè)節(jié)點(diǎn)的父節(jié)點(diǎn),把它“掛接”到treeview中。從遞歸的角度來(lái)看,我們可以重復(fù)這一步驟,真到插入一個(gè)完整的子樹(shù)。相對(duì)而言,比較麻煩的是,如何把一個(gè)子節(jié)點(diǎn)插入到現(xiàn)有的樹(shù)中間,而不是最末端。比如存在一個(gè)節(jié)點(diǎn)n,它的根為r,現(xiàn)在要在r和n之間插入一個(gè)新節(jié)點(diǎn)n’,我們可以這樣做:把n’掛在r下面,作為它的子節(jié)點(diǎn),然后把n的父節(jié)點(diǎn)指定為n’即可。

       修改節(jié)點(diǎn):這里指修改樹(shù)結(jié)構(gòu),改變某一節(jié)點(diǎn)的父節(jié)點(diǎn),在這種結(jié)構(gòu)中,修改是很方便的,只要調(diào)用標(biāo)準(zhǔn)的update就可以。

       刪除節(jié)點(diǎn):刪除節(jié)點(diǎn)時(shí)要注意這個(gè)節(jié)點(diǎn)下面還有沒(méi)有子節(jié)點(diǎn),如果有,我們通常以兩種方式處理,一是把相關(guān)子節(jié)點(diǎn)全刪掉,如果是ms sql server 2000這樣的系統(tǒng),你可以很簡(jiǎn)單的在建表時(shí)將外鍵約束指定為支持級(jí)聯(lián)刪除,自己寫(xiě)一個(gè)級(jí)聯(lián)刪除比較麻煩,不過(guò)也不是不可能,重點(diǎn)在于,為這個(gè)過(guò)程建立遞歸。簡(jiǎn)要示例如下:

--定義存儲(chǔ)過(guò)程

create procedure deletenode

       @nodeid char(10)

as

begin

       --以當(dāng)前節(jié)點(diǎn)的子節(jié)點(diǎn)作為記錄集建立游標(biāo)

       declare childnodes cursor

       read_only

              for select id from treeview where pid = @nodeid

       declare @childnode varchar(40)

       open childnodes

       fetch next from childnodes into @childnode

       while (@@fetch_status <> -1)--判斷記錄集是否成功打開(kāi)

       begin

              if (@@fetch_status <> -2)

              begin

                     --遞歸調(diào)用

                     exec deletenode @childnode

              end

              fetch next from childnodes into @childnode

       end

       close childnodes

       deallocate childnodes

       --代碼執(zhí)行到這里,可以確定@nodeid不再有子節(jié)點(diǎn),現(xiàn)在,我們刪除它

       delete from treeview where id = @nodeid

end;

當(dāng)然,這是一種比較低效的設(shè)計(jì),每一個(gè)將要?jiǎng)h除的節(jié)點(diǎn)都要執(zhí)行一次delete,比較有效率的方法是多深入一層,操作當(dāng)前節(jié)點(diǎn)的子節(jié)點(diǎn)。有興趣的讀者可以一試。

       查詢:樹(shù)狀表的查詢是最有趣的內(nèi)容之一。當(dāng)然僅僅查出某一個(gè)節(jié)點(diǎn)的信息沒(méi)有太大的意思,我們希望的是得到從當(dāng)前節(jié)點(diǎn)一直向下(通常是到最底層)的完整子樹(shù)。這同樣需要一個(gè)遞歸。讓我們從一個(gè)歸納法游戲開(kāi)始,事先,我們先在treeview中插入以下數(shù)據(jù):

id

pid

data

----------

----------

----------

root

root

root

n11

root

node1-1

n12

root

node1-2

n13

root

node1-3

n21

n11

node2-1

n22

n11

node2-2

n23

n12

node2-3

n24

n13

node2-4

n31

n21

node3-1

n32

n21

node3-2

n33

n21

node3-3

n34

n22

node3-4

 

       歸納法第一步,當(dāng)然是構(gòu)造初值,查詢子樹(shù)的根節(jié)點(diǎn)就是標(biāo)準(zhǔn)的sql,select id, data form treeview where id = …,在這里有一個(gè)細(xì)節(jié),查找表中所有的根節(jié)點(diǎn)(細(xì)心的讀者可能早就發(fā)現(xiàn),我們?cè)O(shè)計(jì)的這個(gè)treeview可以存儲(chǔ)任意多個(gè)樹(shù))可以通過(guò)select r.id, r.data form treeview r where r.id = r.pid來(lái)實(shí)現(xiàn)。簡(jiǎn)單起見(jiàn),我們就從這里起步吧。

       第二步,選擇出前兩層也不難,

select r.id, n1.id, n1.data

from treeview r

left join treeview n1

on r.id = n1.pid

and r.id <> n1.id

where r.id = r.pid

       我們要注意的是加藍(lán)的部分,這是增加一層后做改動(dòng)的部分。

       很容易,我們得到前三層,

select r.id, n1.id, n2.id, n2.data

from treeview r

left join treeview n1

on r.id = n1.pid

and r.id <> n1.id

left join treeview n2

on n1.id = n2.pid

and n1.id <> n2.id

where r.id = r.pid

       同樣的,我們重點(diǎn)觀察加藍(lán)的部分。相信經(jīng)過(guò)這兩步,大家會(huì)發(fā)現(xiàn)其中的規(guī)律?,F(xiàn)在我們可以把這個(gè)過(guò)程自動(dòng)化了……

       ……

       不知道大家是否還記得前幾集里那個(gè)關(guān)于四色問(wèn)題的笑話,我又一次犯了這樣的錯(cuò)誤。一個(gè)多月以來(lái),我就陷在這里不能自拔。顯然,我低估了問(wèn)題的難度。這個(gè)問(wèn)題似乎不能轉(zhuǎn)化為一個(gè)簡(jiǎn)單表達(dá)式,只能通過(guò)一個(gè)遞歸的流程來(lái)實(shí)現(xiàn)。而流程化的程序又非sql所長(zhǎng)。這里面有著各種各樣的麻煩。相信試過(guò)的朋友都有體會(huì)?,F(xiàn)在我使用的架構(gòu)問(wèn)題是顯然的,它需要明確知道到底從子樹(shù)的頂點(diǎn)向下到底有多少層。所以,計(jì)算樹(shù)的層數(shù)就首先被提了出來(lái)。

       但是,但是,但是……(我簡(jiǎn)直沒(méi)臉見(jiàn)人啦)我一直找不到一個(gè)簡(jiǎn)潔優(yōu)雅的方法。一個(gè)多月的時(shí)間就這么過(guò)去了。我不得不一點(diǎn)點(diǎn)放棄我的原則(此乃人生墮落之始?。?。最后,我得承認(rèn),要想寫(xiě)出一個(gè)優(yōu)雅的樹(shù)狀表選擇來(lái),對(duì)我還是比較困難的。所以,在這一篇文章里,我們先討論到這里,樹(shù)狀表的選擇――其實(shí)應(yīng)該說(shuō)樹(shù)狀視圖構(gòu)造,還是留到下次再討論吧。
發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 漳浦县| 固安县| 玉环县| 建瓯市| 华亭县| 霍林郭勒市| 镇赉县| 温泉县| 新密市| 四川省| 南投市| 奉贤区| 星座| 文水县| 方山县| 峨眉山市| 化隆| 阿城市| 托克逊县| 北碚区| 东港市| 石家庄市| 沈阳市| 嫩江县| 巫山县| 越西县| 互助| 剑阁县| 广昌县| 马龙县| 嘉义市| 乌鲁木齐县| 安化县| 阿拉善右旗| 天长市| 安福县| 榆林市| 中阳县| 吉林省| 基隆市| 永福县|