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