?
This document uses PHP Chinese website manual Release
復(fù)合類型描述一行或者一條記錄的結(jié)構(gòu);它實(shí)際上只是一個(gè)字段名和它們的數(shù)據(jù)類型的列表。 PostgreSQL允許像簡(jiǎn)單數(shù)據(jù)類型那樣使用復(fù)合類型。比如,一個(gè)表的某個(gè)字段可以聲明為一個(gè)復(fù)合類型。
下面是兩個(gè)定義復(fù)合類型的簡(jiǎn)單例子:
CREATE TYPE complex AS ( r double precision, i double precision ); CREATE TYPE inventory_item AS ( name text, supplier_id integer, price numeric );
語法類似于CREATE TABLE,只是這里只可以聲明字段名字和類型;目前不能聲明約束 (比如NOT NULL)。請(qǐng)注意AS關(guān)鍵字是很重要的;沒有它, 系統(tǒng)會(huì)認(rèn)為這是完全不同的CREATE TYPE命令,因此你會(huì)看到奇怪的語法錯(cuò)誤。
定義了類型,我們就可以用它創(chuàng)建表:
CREATE TABLE on_hand ( item inventory_item, count integer ); INSERT INTO on_hand VALUES (ROW('fuzzy dice', 42, 1.99), 1000);
或者函數(shù):
CREATE FUNCTION price_extension(inventory_item, integer) RETURNS numeric AS 'SELECT $1.price * $2' LANGUAGE SQL; SELECT price_extension(item, 10) FROM on_hand;
在你創(chuàng)建表的時(shí)候,也會(huì)自動(dòng)創(chuàng)建一個(gè)復(fù)合類型,名字與表名字相同,表示該表的復(fù)合類型。比如,如果我們說過:
CREATE TABLE inventory_item ( name text, supplier_id integer REFERENCES suppliers, price numeric CHECK (price > 0) );
那么,和前面相同的inventory_item復(fù)合類型也會(huì)作為副產(chǎn)品創(chuàng)建, 并且可以和上面一樣使用。不過,需要注意一個(gè)重要限制: 因?yàn)楝F(xiàn)在還沒有對(duì)復(fù)合類型實(shí)現(xiàn)約束,所以在表定義中顯示的約束并不適用 于表之外的復(fù)合類型。一個(gè)部分繞開的辦法是使用域類型作為復(fù)合類型的成員。
要以文本常量書寫復(fù)合類型值,在圓括弧里包圍字段值并且用逗號(hào)分隔他們。 你可以在任何字段值周圍放上雙引號(hào),如果值本身包含逗號(hào)或者圓括弧, 你必須用雙引號(hào)括起(更多細(xì)節(jié)見下面)。因此,復(fù)合類型常量的一般格式如下:
'( val1 , val2 , ... )'
An example is: 一個(gè)例子是:
'("fuzzy dice",42,1.99)'
如果inventory_item類型在前面已經(jīng)定義了, 那么這是一個(gè)合法的數(shù)值。要讓一個(gè)字段值是NULL,那么在列表里它的位置上不要寫任何字符。 比如,下面這個(gè)常量在第三個(gè)字段聲明一個(gè)NULL:
'("fuzzy dice",42,)'
如果你想要一個(gè)空字符串,而不是NULL,寫一對(duì)雙引號(hào):
'("",42,)'
這里的第一個(gè)字段是一個(gè)非NULL的空字符串,第三個(gè)字段是NULL。
這些常量實(shí)際上只是我們?cè)赟ection 4.1.2.7 討論的一般類型常量的一個(gè)特殊例子。這些常量一開始只是當(dāng)作字符串, 然后傳遞給復(fù)合類型輸入轉(zhuǎn)換器。一個(gè)明確的類型聲明可能是必須的。
我們也可以用ROW表達(dá)式語法來構(gòu)造復(fù)合類型值。在大多數(shù)場(chǎng)合下, 這種方法都比用字符串文本的語法更簡(jiǎn)單,因?yàn)槟悴挥貌傩亩嘀匾?hào)。我們已經(jīng)在上面使用了這種方法了:
ROW('fuzzy dice', 42, 1.99) ROW('', 42, NULL)
只要你在表達(dá)式里有超過一個(gè)字段,那么關(guān)鍵字ROW就實(shí)際上是可選的,所以可以簡(jiǎn)化為
('fuzzy dice', 42, 1.99) ('', 42, NULL)
ROW表達(dá)式語法在Section 4.2.12里有更詳細(xì)的討論。
要訪問復(fù)合類型字段的一個(gè)域,我們寫出一個(gè)點(diǎn)以及域的名字, 非常類似從一個(gè)表名字里選出一個(gè)字段。實(shí)際上,因?yàn)閷?shí)在太像從表名字中選取字段, 所以我們經(jīng)常需要用圓括弧來避免分析器混淆。比如,你可能需要從on_hand例子表中選取一些子域,像下面這樣:
SELECT item.name FROM on_hand WHERE item.price > 9.99;
這樣將不能工作,因?yàn)?tt class="LITERAL">item是被當(dāng)做一個(gè)表名字選取的, 而不是on_hand的字段名。你必須像下面這樣寫:
SELECT (item).name FROM on_hand WHERE (item).price > 9.99;
或者如果你也需要使用表名字(比如,在一個(gè)多表查詢里),那么這么寫:
SELECT (on_hand.item).name FROM on_hand WHERE (on_hand.item).price > 9.99;
現(xiàn)在圓括弧對(duì)象正確地解析為一個(gè)指向item字段的引用,然后就可以從中選取子域。
類似的語法問題適用于在任何地點(diǎn)從一個(gè)復(fù)合類型值中查詢一個(gè)域。 比如,要從一個(gè)返回復(fù)合類型值的函數(shù)中只選取一個(gè)字段,你需要寫像下面這樣的東西:
SELECT (my_func(...)).field FROM ...
如果沒有額外的圓括弧,會(huì)產(chǎn)生一個(gè)語法錯(cuò)誤。
下面是一些插入和更新復(fù)合類型字段的正確語法。首先,插入或者更新整個(gè)字段:
INSERT INTO mytab (complex_col) VALUES((1.1,2.2)); UPDATE mytab SET complex_col = ROW(1.1,2.2) WHERE ...;
第一個(gè)例子省略了ROW,第二個(gè)使用它;兩種方法都行。
我們可以更新一個(gè)復(fù)合字段的獨(dú)立子域:
UPDATE mytab SET complex_col.r = (complex_col).r + 1 WHERE ...;
請(qǐng)注意,這里我們不需要(實(shí)際上是不能)在SET后面出現(xiàn)的字段名周圍放上圓括弧, 但是我們?cè)诘忍?hào)右邊的表達(dá)式里引用同一個(gè)字段的時(shí)候卻需要圓括弧。
我們也可以聲明子域是INSERT的目標(biāo):
INSERT INTO mytab (complex_col.r, complex_col.i) VALUES(1.1, 2.2);
如果我們沒有為字段的所有子域提供數(shù)值,那么剩下的子域?qū)⒂肗ULL填充。
一個(gè)復(fù)合類型的文本表現(xiàn)形式包含那些根據(jù)獨(dú)立的子域類型各自I/O轉(zhuǎn)換規(guī)則解析的項(xiàng), 加上一些表明這是復(fù)合結(jié)構(gòu)的修飾。這些修飾由整個(gè)數(shù)值周圍的圓括弧((和)) 加上相鄰域之間的逗號(hào)(,)組成。圓括弧外面的空白被忽略, 但是在圓括弧里面,它被當(dāng)作子域數(shù)值的一部分,根據(jù)該子域的數(shù)據(jù)類型,這些空白可能有用,也可能沒用。比如,在
'( 42)'
里,如果子域類型是整數(shù),那么空白將被忽略,但是如果是文本,那么就不會(huì)忽略。
如前面顯示的那樣,在給一個(gè)復(fù)合類型寫數(shù)值的時(shí)候,你可以在獨(dú)立的子域數(shù)值周圍用雙引號(hào)包圍。 如果子域數(shù)值會(huì)導(dǎo)致復(fù)合數(shù)值分析器產(chǎn)生歧義,那么你必須這么做。特別是子域包含圓括弧、逗號(hào)、雙引號(hào)、反斜杠的場(chǎng)合。 要想在雙引號(hào)括起來的子域數(shù)值里面放雙引號(hào),那么你需要在它前面放一個(gè)反斜杠。 同樣,在一個(gè)雙引號(hào)括起的子域數(shù)值里面的一對(duì)雙引號(hào)表示一個(gè)雙引號(hào)字符,就像SQL字符串文本的單引號(hào)規(guī)則一樣。 另外,你可以用反斜杠逃逸,而不必用引號(hào)的方法保護(hù)所有可能會(huì)當(dāng)作復(fù)合類型語法的數(shù)據(jù)字符。
一個(gè)完全空的子域數(shù)值(在逗號(hào)或者逗號(hào)與圓括弧之間沒有字符)表示一個(gè)NULL。 要寫一個(gè)空字符串,而不是一個(gè)NULL,寫""。
假如子域數(shù)值是空字符串或者包含圓括弧、逗號(hào)、雙引號(hào)、反斜杠、空白, 復(fù)合類型輸出程序會(huì)在子域數(shù)值周圍放上雙引號(hào)。這么處理空白不是必須的, 但是可以增強(qiáng)易讀性。在一個(gè)子域數(shù)值里面嵌入的雙引號(hào)和反斜杠將會(huì)寫成兩份。
Note: 請(qǐng)注意你寫的任何SQL命令都首先被當(dāng)作字符串文本解析,然后才當(dāng)作復(fù)合類型。 這就加倍了你需要的反斜杠數(shù)目。比如,要插入一個(gè)包含雙引號(hào)和一個(gè)反斜杠的text子域到一個(gè)復(fù)合類型數(shù)值里,你需要寫
INSERT ... VALUES (E'("\\"\\\\")');字符串文本處理器先吃掉一層反斜杠,這樣到達(dá)復(fù)合類型分析器的東西將變成("\"\\")。 接著,該字符串傳遞給text數(shù)據(jù)類型的輸入過程,變成"\。 如果我們使用的數(shù)據(jù)類型對(duì)反斜杠也有特殊待遇,比如 bytea , 那么我們可能需要在命令里放多達(dá)八個(gè)反斜杠以獲取在存儲(chǔ)的復(fù)合類型子域中有一個(gè)反斜杠。 美元符界定(參閱Section 4.1.2.4)可以用于避免雙份反斜杠的問題。
Tip: 在SQL命令里寫復(fù)合類型值的時(shí)候,ROW構(gòu)造器通常比復(fù)合文本語法更容易使用。 在ROW里,獨(dú)立的子域數(shù)值的寫法和并非作為復(fù)合類型的成員書寫的方法一樣。