5.8. 模式

一個 PostgreSQL 資料庫集群 包含一個或多個命名的資料庫。用戶和用戶組在整個集群的範圍內 是共享的,但是其它資料並不是共享的。任何給定的與伺服器的客戶連接都只 能訪問在一個資料庫裡的資料,就是那個在連接請求裡聲明的。

注意: 一個集群的用戶並不一定要有訪問集群內所有資料庫的權限。 共享用戶名的意思是不能有同名用戶,也就是,在同一個集群裡的兩個 資料庫裡都有叫 joe 的用戶;但是系統可以配置成 只允許 joe 訪問某些資料庫。

一個資料庫包含一個或多個命名的 模式, 模式又包含資料表。模式還包含其它命名的對象,包括資料類型,函數, 以及操作符。同一個對像名可以在不同的模式裡使用而不會導致衝突; 比如,schema1myschema 都可以包含叫做 mytable 的資料表。和資料庫不同,模式不是嚴格分離的: 一個用戶可以訪問他所連接的資料庫中的任意模式中的對象, 只要他有權限。

我們需要模式的原因有好多:

模式類似於操作系統層次的目錄,只不過模式不能嵌套。

5.8.1. 建立一個模式

要建立一個模式,使用命令 CREATE SCHEMA。 給出您選擇的模式名字。比如:

CREATE SCHEMA myschema;

要建立或者訪問在模式中的對象,寫出一個受修飾的名字, 這個名字包含模式名以及資料表(對像名),它們之間用一個句點分開:

schema.table

(出於簡化,我們將只討論資料表,這個概念適用於所有其它有名對像類型,比如類型和函數。)

實際上,更一般的語法是

database.schema.table

這個語法也可以使用,但目前它只是為了和 SQL 標準形式上兼容。 如果您寫了一個資料庫名,那麼它必須和您目前連接的資料庫同名。

要在新模式裡建立一個資料表,用

CREATE TABLE myschema.mytable (
 ...
);

這樣的方法在任何需要一個資料表名字的地方都可以用, 包括修改資料表的命令和我們在下一章要講的資料訪問命令。

如果一個模式是空的(所有它裡面的對象都已經刪除),那麼刪除一個模式的命令

DROP SCHEMA myschema;

要刪除一個包含所有對象的模式,使用

DROP SCHEMA myschema CASCADE;

參閱 Section 5.10 獲取躲藏在這些動作背後 的東西的一般機制的描述。

通常您想建立一個別人擁有的模式(因為這是一種限制您的用戶 在定義良好的模式中的活動的方法)。其語法如下:

CREATE SCHEMA schemaname AUTHORIZATION username;

您甚至可以省略模式名字,這時模式名將和用戶名同名。 參閱 Section 5.8.6 獲取這種情況 的適用場合。

pg_ 開頭的模式名是保留給系統使用的, 用戶不能建立這樣的名字。

5.8.2. Public 模式

在前面的小節裡,我們沒有聲明任何模式名字就建立了資料表。 預設時,這樣的資料表(以及其他對像)都自動放到一個叫做"public" 的模式中去了。每個新資料庫都包含一個這樣的模式。因此,下面的命令是等效的:

CREATE TABLE products ( ... );

CREATE TABLE public.products ( ... );

5.8.3. 模式搜索路徑

全稱的名字寫起來非常費勁,並且我們最好不要在應用裡直接 寫上特定的模式名。因此,資料表通常都是用未修飾的名字 引用的,這樣的名字裡只有資料表名字。系統透過查找一個搜索路徑 來判斷一個資料表究竟是哪個資料表,這個路徑是一個需要查找的模式列資料表。 在搜索路徑裡找到的第一個資料表將被當作選定的資料表。如果在搜索路徑中 沒有匹配資料表,那麼就報告一個錯誤,即使匹配資料表的名字在資料庫其它的 模式中存在也如此。

在搜索路徑中的第一個模式叫做目前模式。除了是搜索的第一個模式之外, 它還是在 CREATE TABLE 沒有聲明模式名的時候,新建資料表 所在的地方。

要顯示目前搜索路徑,使用下面的命令:

SHOW search_path;

在預設的設置中,返回下面的東西:

 search_path
--------------
 $user,public

第一個元素聲明將要搜索一個和目前用戶同名的模式。 因為還沒有這樣的模式存在,所以這條記錄被忽略。第二個元素指向 我們已經看過的公共模式。

搜索路徑中存在的第一個模式是建立新對象的預設位置。 這就是為什麼預設的對象都會建立在 public 模式裡的原因。 如果在任何其它環境中引用對象,而且沒有用模式修飾 (資料表修改,資料變更,或者查詢命令),那麼系統會遍歷 搜索路徑,直到找到一個匹配的對象。因此,在預設的配置裡, 任何未修飾的訪問同樣也只能引用 public 模式。

要把新的模式放到路徑中來,我們用

SET search_path TO myschema,public;

(我們在這裡省略了 $user 是因為我們 並不是立即需要它。)然後我們就可以不用加模式修飾訪問 資料表了:

DROP TABLE mytable;

同樣,因為 myschema 是路徑中的第一個元素, 新對像預設時將建立在這裡。

我們也可以寫成

SET search_path TO myschema;

然後我們如果不明確修飾的話,就不能再訪問 public 模式了。 public 模式沒有任何特殊之處,只不過它預設時就存在。 我們也可以把它刪除了。

又見 Section 9.19 獲取其它訪問模式搜索路徑的方法。

搜索路徑對於資料類型名,函數名以及操作符名的運作方式和資料表名字完全相同。 資料類型和函數名可以像資料表名字一樣加以修飾。如果您需要在資料表達式裡寫一個有修飾的操作符名字, 我們有一個特殊的要求:您必須這麼寫

OPERATOR(schema.operator)

這樣是為了避免語法歧義。下面是一個例子

SELECT 3 OPERATOR(pg_catalog.+) 4;

實際上我們通常依賴搜索路徑尋找操作符, 這樣就不用寫這麼難看的東西了。

5.8.4. 模式和權限

預設時,用戶看不到模式中不屬於他們所有的對象。 為了讓他們看得見,模式的所有者需要在模式上賦予 USAGE 權限。為了讓用戶使用模式中的對象,我們可能需要賦予額外的權限, 只要是適合該對象的。

用戶也可以允許在別人的模式裡建立對象。要允許這麼做, 我們需要賦予在該模式上的 CREATE 權限。 請注意,預設時,每個人都在 public 模式上 有 CREATE 權限。這樣就允許所有可以連接到 指定資料庫上的用戶在這裡建立對象。如果您不允許這麼做, 您可以撤銷這個權限:

REVOKE CREATE ON public FROM PUBLIC;

(第一個 "public" 是模式,第二個 "public" 意思是"所有用戶"。 第一句裡它是個標識符,而第二句裡是個關鍵字,所以有不同的大小寫; 記住我們在 Section 4.1.1 裡面 說過的原則。)

5.8.5. 系統資料表模式

除了 public 和用戶建立的模式之外, 每個資料庫都包含一個 pg_catalog 模式,它包含 系統資料表和所有內置資料類型,函數和操作符。pg_catalog 總是搜索路徑中的一部分。如果它沒有明確出現在路徑中,那麼 它會隱含地在路徑裡地模式之前搜索。這樣就保證了內置地名字 總是可以搜索地。不過,您可以明確地把pg_catalog 放在您的搜索路徑的後面,如果您想用用戶定義的名字覆蓋內置的名字的話。

PostgreSQL 版本 7.3 之前, 以 pg_ 開頭的資料表名字是保留的。這個規則現在 不再是正確的了:如果必要,您可以建立這樣的資料表名字, 只要是在非系統模式裡。不過,我們最好還是不要使用這樣的名字, 以保證自己將來不會和新版本衝突:那些版本也許會定義一些和 您的資料表同名的資料表。(在預設搜索路徑中,一個對您的資料表的無修飾 引用將解析為系統資料表。)系統資料表將繼續遵循以pg_ 開頭的傳統,因此,只要您的資料表不是以pg_ 開頭, 就不會和無修飾的用戶資料表名字衝突。

5.8.6. 使用方式

模式可以以多種方式組織您的資料。下面是一些建議使用的模式, 它們也很容易在預設配置中得到支援:

5.8.7. 移植性

在 SQL 標準裡,在同一個模式裡的對象由不同的用戶所有的概念是不存在的。 而且,有些實現不允許您建立和它們的所有者不同名的模式。實際上, 模式和用戶的概念在那些只實現了標準中規定的基本模式支援的資料庫系統裡幾乎是一樣的。 因此,許多用戶考慮對名字加以修飾,使它們真正由 username.tablename組成。 如果您為每個用戶都建立了一個模式,這實際上就是 PostgreSQL 的行為。

同樣,在 SQL 標準裡也沒有 public 模式的概念。 為了最大限度地遵循標準,您不應該使用(可能甚至是應該刪除) public 模式。

當然,有些 SQL 資料庫系統可能根本沒有實現模式,或者是透過允許 (可能是有限制的)跨資料庫訪問來提供模式的支援。 如果您需要在這些系統上幹活,那麼最大限度的移植性來自根本不用模式。