?
本文檔使用
php中文網(wǎng)手冊 發(fā)布
PostgreSQL實(shí)現(xiàn)了表繼承,這個(gè)特性對數(shù)據(jù)庫設(shè)計(jì)人員來說是一個(gè)很有效的工具。 SQL99及以后的標(biāo)準(zhǔn)定義了類型繼承特性,和我們在這里描述的很多特性有區(qū)別。
讓我們從一個(gè)例子開始:假設(shè)我們試圖制作一個(gè)城市數(shù)據(jù)模型。每個(gè)州都有許多城市,但是只有一個(gè)首府。 我們希望能夠迅速檢索任何州的首府。這個(gè)任務(wù)可以通過創(chuàng)建兩個(gè)表來實(shí)現(xiàn),一個(gè)是州府表,一個(gè)是非州府表。 不過,如果我們不管什么城市都想查該怎么辦?繼承的特性可以幫助我們解決這個(gè)問題。 我們定義capitals 表,它繼承自cities表:
CREATE TABLE cities ( name text, population float, altitude int -- in feet ); CREATE TABLE capitals ( state char(2) ) INHERITS (cities);
在這種情況下,capitals表繼承它的父表cities中的所有屬性。州首府有一個(gè)額外的state屬性顯示其所在的州。
在PostgreSQL里,一個(gè)表可以從零個(gè)或多個(gè)其它表中繼承屬性,而且一個(gè)查詢既可以引用一個(gè)表中的所有行, 也可以引用一個(gè)表及其所有后代表的行(后面這個(gè)是缺省行為)。比如,下面的查詢查找所有海拔500英尺以上的城市名,包括州首府:
SELECT name, altitude FROM cities WHERE altitude > 500;
使用PostgreSQL教程里面的數(shù)據(jù)(參閱Section 2.1),它返回:
name | altitude -----------+---------- Las Vegas | 2174 Mariposa | 1953 Madison | 845
另一方面,如果要找出不包括州首府的所有海拔超過500英尺的城市,查詢應(yīng)該是這樣的:
SELECT name, altitude FROM ONLY cities WHERE altitude > 500; name | altitude -----------+---------- Las Vegas | 2174 Mariposa | 1953
cities前面的ONLY表明該查詢應(yīng)該只針對cities而不包括其后代。許多我們已經(jīng)討論過的命令— SELECT, UPDATE,DELETE —都支持ONLY關(guān)鍵字。
有時(shí)候你可能想知道某個(gè)行版本來自哪個(gè)表。在每個(gè)表里我們都有一個(gè) tableoid系統(tǒng)屬性可以告訴你源表是誰:
SELECT c.tableoid, c.name, c.altitude FROM cities c WHERE c.altitude > 500;
結(jié)果如下(你可能會(huì)得到不同的 OID): tableoid | name | altitude ----------+-----------+---------- 139793 | Las Vegas | 2174 139793 | Mariposa | 1953 139798 | Madison | 845 tableoid | name | altitude ----------+-----------+---------- 139793 | Las Vegas | 2174 139793 | Mariposa | 1953 139798 | Madison | 845 通過和pg_class做一個(gè)連接,就可以看到實(shí)際的表名字:
SELECT p.relname, c.name, c.altitude FROM cities c, pg_class p WHERE c.altitude > 500 AND c.tableoid = p.oid;
它返回:
relname | name | altitude ----------+-----------+---------- cities | Las Vegas | 2174 cities | Mariposa | 1953 capitals | Madison | 845
對于INSERT或COPY,繼承并不自動(dòng)影響其后代表。在我們的例子里,下面的INSERT語句將會(huì)失?。?
INSERT INTO cities (name, population, altitude, state) VALUES ('New York', NULL, NULL, 'NY');
我們可能希望數(shù)據(jù)被傳遞到capitals表里面去,但這是不會(huì)發(fā)生的:INSERT總是插入明確聲明的那個(gè)表。 在某些情況下,我們可以使用規(guī)則進(jìn)行重定向插入(參閱Chapter 37)。不過它不能對上面的例子有什么幫助, 因?yàn)?tt class="STRUCTNAME">cities表并不包含state字段,因此命令在規(guī)則施加之前就會(huì)被拒絕掉。
所有父表的檢查約束和非空約束都會(huì)自動(dòng)被所有子表繼承。不過其它類型的約束(唯一、主鍵、外鍵)不會(huì)被繼承。
一個(gè)子表可以從多個(gè)父表繼承,這種情況下它將擁有所有父表字段的總和,并且子表中定義的字段也會(huì)加入其中。 如果同一個(gè)字段名出現(xiàn)在多個(gè)父表中,或者同時(shí)出現(xiàn)在父表和子表的定義里,那么這些字段就會(huì)被"融合", 這樣在子表里就只有一個(gè)這樣的字段。要想融合,字段的數(shù)據(jù)類型必須相同,否則就會(huì)拋出一個(gè)錯(cuò)誤。融合的字段將會(huì)擁有其父字段的所有檢查約束, 并且如果某個(gè)父字段存在非空約束,那么融合后的字段也必須是非空的。
表繼承通常使用帶INHERITS子句的CREATE TABLE語句定義。另外,一個(gè)已經(jīng)用此方法定義的子表 可以使用帶INHERIT的ALTER TABLE命令添加一個(gè)新父表。注意:該子表必須已經(jīng)包含新父表的所有字段且類型一致, 此外新父表的每個(gè)約束的名字及其表達(dá)式都必須包含在此子表中。同樣,一個(gè)繼承鏈可以使用帶NO INHERIT的ALTER TABLE命令從子表上刪除。 允許動(dòng)態(tài)添加和刪除繼承鏈對基于繼承關(guān)系的表分區(qū)(參見Section 5.9)很有用。
創(chuàng)建一個(gè)將要作為子表的新表的便利途徑是使用帶LIKE子句的CREATE TABLE命令。 它將創(chuàng)建一個(gè)與源表字段相同的新表。如果源表中存在約束,那么應(yīng)該聲明LIKE的INCLUDING CONSTRAINTS選項(xiàng), 因?yàn)樽颖肀仨毎幢碇械?tt class="LITERAL">CHECK約束。
任何存在子表的父表都不能被刪除,同樣,子表中任何從父表繼承的字段也不能被刪除或修改。如果你想刪除一個(gè)表及其所有后代, 最簡單的辦法是使用CASCADE選項(xiàng)。
ALTER TABLE會(huì)把所有數(shù)據(jù)定義和檢查約束傳播到后代里面去。另外,只有在使用CASCADE選項(xiàng)的情況下,才能刪除父表的字段或者約束。 ALTER TABLE在重復(fù)字段融合和拒絕方面和CREATE TABLE的規(guī)則相同。
注意表的訪問權(quán)限是如何處理的。查詢父表可以自動(dòng)訪問子表的數(shù)據(jù)而不用進(jìn)一步檢查訪問權(quán)限。 這個(gè)保留數(shù)據(jù)(也)在父表中的外貌。然而,直接訪問子表不會(huì)自動(dòng)被允許,而且需要要求被授予進(jìn)一步的權(quán)限。
注意,不是所有的SQL命令可以在所有的繼承層次上正常工作。數(shù)據(jù)查詢,數(shù)據(jù)修改,模式修改的命令(比如,SELECT,UPDATE,DELETE, ALTER TABLE的大多數(shù)變型, 但不是INSERT和ALTER TABLE ...RENAME) 典型的默認(rèn)包括子表和支持ONLY符號來排除它們。 為數(shù)據(jù)庫維護(hù)和調(diào)優(yōu)的命令(例如,REINDEX,VACUUM)通常只對個(gè)別工作, 物理表格不支持遞歸超過繼承層次結(jié)構(gòu)。每個(gè)命令的各自行為都被記錄在參考部分(Reference I, SQL命令 )。
繼承的一個(gè)嚴(yán)重局限性是索引(包括唯一約束)和外鍵約束只能用于單個(gè)表,而不能包括它們的子表 (不管對引用表還是被引用表都是如此),因此,在上面的例子里:
即使我們聲明cities.name為UNIQUE或PRIMARY KEY也不會(huì)阻止capitals表擁有重復(fù)名字的cities數(shù)據(jù)行。 并且這些重復(fù)的行在查詢cities表的時(shí)候會(huì)顯示出來。實(shí)際上,缺省時(shí)capitals將完全沒有唯一約束, 因此可能包含帶有同名的多個(gè)行。你應(yīng)該給capitals增加唯一約束,但即使這樣做也不能避免與cities的重復(fù)。
類似的,即使我們聲明cities.name參照REFERENCES某些其它的表,這個(gè)約束也不會(huì)自動(dòng)傳播到capitals表。在這種條件下, 你可以通過手工給capitals表增加同樣的REFERENCES約束來做到這點(diǎn)。
聲明一個(gè)其它表的字段為REFERENCES cities(name)將允許其它表包含城市名,但是不包含首府名。這種情況下沒有很好的繞開辦法。
這些缺點(diǎn)很可能在將來的版本中修補(bǔ),但同時(shí)你也需要考慮一下,繼承是否對你的問題真正有用。
已廢棄: 在7.1以前的PostgreSQL版本里,缺省的行為是不在查詢里包含子表。后來發(fā)現(xiàn)這么做很容易出錯(cuò)并且也違反了SQL標(biāo)柱。 你可以通過關(guān)閉sql_inheritance配置選項(xiàng)來兼容以前的行為。