Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
你需要先進行安裝,才能開始使用 PostgreSQL。當然,PostgreSQL 也可能已經被安裝在你的系統之中,因為你的作業系統預設套件包含了 PostgreSQL,或其他系統管理者已先行安裝。如果是這樣的話,那麼你應該先瞭解作業系統的資訊,或向你的系統管理員取得存取方式的資訊。
如果你並不確定 PostgreSQL 是否已經可以使用,那麼你也可以自行安裝試試。這樣做並不是很困難,而且是很好的操作練習。PostgreSQL 可以以一般使用者進行安裝,它並不需要系統管理者(root)的權限才能安裝。
如果你打算自行安裝 PostgreSQL,你可以參考第 16 章的指令進行,完成之後再回到這裡,以瞭解下面有關設定環境變數的內容。
如果你的系統管理者並非以預設的方式安裝,你可能還有一些額外的工作要做。例如,如果資料庫主機其實是遠端的伺服器,你會需要設定 PGHOST 的環境變數,將其指向資料庫主機的網路名稱。而 PGPORT 變數也是必須要設定的。最基本的情境是,如果你嘗試啓動一個應用程式,而它回報它無法取得資料庫連線時,你就必須洽詢你的系統管理者。而如果系統管理者就是你自己,那麼你應該依文件再確認你的環境設定是正確的。如果你仍然並不清楚前面所描述的事項,請詳細閱讀下一節的內容。
在開始使用之前,你需要瞭解基本的 PostgreSQL 系統架構。瞭解 PostgreSQL 如何回應操作,有助於讓你更清楚理解接下來的說明。
以資料庫的術語來說,PostgreSQL 採用了主從式架構(client/server)。PostgreSQL 會在進行下列操作時保持連線:
伺服器的執行程序,負責管理資料庫的檔案、受理用戶端的連線要求、執行相對應的資料庫動作。這樣的資料庫伺服端程式稱之為「postgres」。
用戶端的程式用來發起資料庫操作的行為,其設計的形態很廣泛:可能是文字介面的工具、圖型介面的程式、將資料庫內容顯示成網頁的網際網路伺服器、甚或是專用的資料庫管理工具。有一些用戶端程式是由 PostgreSQL 官方所提供,大部份由第三方的其他使用者所開發。
如同一般的主從式架構,用戶端與伺服端可以是兩台不同的主機,而他們透過 TCP/IP 的網路協定溝通。你應該將這個觀念謹記在心,因為某些在用戶端可以被存取的檔案,在伺服端可能就無法存取(或使用不同的檔案名稱)。
PostgreSQL 伺服器可以管理來自多個用戶端的同時連線。為了達到這樣的功能,它會自我複製(fork)成新的執行程序,一對一地處理每一個連線。這個部份進一步來說,用戶端和新的伺服器執行程序之間的溝通,並不需要原始的 postgres 執行程序介入。也就是說,主要的資料庫服務執行程序會持續等待其他用戶端的連線,協助安排好其與伺服端執行程序的配對之後便完全交接,再回到等待的狀態。(當然,使用者完全不會察覺這些行為,在此說明僅僅是為了整體性的概念描繪)
PostgreSQL 是一個關連式資料庫管理系統(RDBMS)。這表示它是一個管理關連性質資料的系統。關連性,基本上在數學裡是以資料表(table)的形式來表現的。今天,以資料表為形式儲存資料是很常見的事,它是很自然的表現,但也有很多其他組識資料庫的方式。在 Unix-like 的作業系統中,檔案和目錄是一個階層式資料庫的案例。更先進的發展是採用物件導向式的資料庫。
每一個資料表是很多資料列(row)的集合。而每一個資料列則以許多相同集合的欄位(column)所組成。每一個欄位都被指定了特定的資料型別。每一個資料列中欄位的次序是固定的。很重要且必須記得的是,SQL 並不保證資料列在資料表中的次序(雖然他們可以在顯示的時候被明確表現)。
一個資料庫中集合了許多資料表,而很多的資料庫則被一個 PostgreSQL 服務所管理,形成一個資料庫叢集。
本章適合初學資料庫的朋友閱讀,以簡單的語法範例,實際操作以瞭解資料庫的運作方式。事實上,更複雜的資料庫行為,也不脫這個基本的操作模式。
第一個測試確認你是否能夠存取一個資料庫服務,就是嘗試去建立一個資料庫。一個執行中的 PostgreSQL 服務可以管理許多個資料庫。一般來說,每一個專案或使用者會分開使用不同的資料庫。
你的系統管理員也可能已經為你建立了一個資料庫,如果是這樣的話,那你可以略過本節說明,直接進入到下一節的內容。
要建立一個新的資料庫,在本例中取名叫「mydb」,你可以使用以下的命令:
如果在這個步驟沒有產生任何回應,那就是成功了。你可以跳過本節剩餘的部份。
但你如果看到如下的訊息:
這個訊息代表 PostgreSQL 並沒有被正確的安裝。不是它沒有被安裝好,那就是你的命令路徑設定並未包含這個指令。 嘗試使用下列這個包含絕對路徑的指令看看:
命令路徑在你的系統可能會有些不同。洽詢你的系統管理員,或著檢查安裝步驟以修正這個情況。
另一種回應可能是如此:
這代表了資料庫服務尚未啓動,或者它並不存在於createdb預設連線的位置。同樣地,檢查安裝的步驟或洽詢系統管理者。
而另一種回應也可能是:
這裡指出你用來連線的使用者名稱。這種情況可能會發生在你的資料庫管理員並未建立屬於你的資料庫。(PostgreSQL 的使用者帳戶是獨立於作業系統的使用者帳戶的)如果你是資料庫管理員,請參閱第 21 章,進行建立資料庫帳戶。你必須是 PostgreSQL 初始安裝的管理者(通常是 postgres),以建立第一個一般資料庫使用者的帳戶。這個情況也可能發生在,你被發配的 PostgreSQL 使用者名稱有別於你的作業系統使用者名稱,如果是這樣的話,那你需要在指令上使用 -U 選項,或者設定 PGUSER 環境變數,以指定你的 PostgreSQL 使用者名稱。
如果你有一個資料庫帳戶,但你並沒有建立資料庫的權限,你將會看到下列訊息:
並非每一個使用者都被授權可以建立一個新的資料庫。如果 PostgreSQL 拒絕你建立資料庫,那麼系統管理者就需要賦予你建立資料庫的權限。洽詢你的系統管理者,如果是這種情況的話。如果你是自行安裝 PostgreSQL,那麼你應該以你啓動資料庫服務的使用者登入作業系統,再嘗試這個操作。
你也可以建立資料庫,但使用其他的名稱。PostgreSQL 允許在資料庫系統中建立無限制數量的資料庫。資料庫名稱必須是以英文字母為開頭,總長度限制為 63 位元組。一個簡便的方式是,建立一個與你使用者名稱同名的資料庫。許多工具會預設假定資料庫名稱和你同名,所以這可以省略一些文字的輸入。要建立這樣的資料庫,只要簡單地輸入:
如果你不再使用你的資料庫,你可以移除它。舉例來說,你是 mydb 這個資料庫的擁有者(建立者),你可以使用下列指令來消毁它:
(對這個指令來說,資料庫名稱並不會預設使用你的使用者同名資料庫。你必須明確地指定名稱)這個動作會完全地移除所有和這個資料庫相關的檔案,並且沒有回復的可能,所以要進行這個動作的話,請一定要考慮清楚。
INSERT 指令被用來將資料以資料列(row)的形式,新增至資料表(table)之中:
注意,所有的資料型別都有明確的輸入格式。只要不是簡單的數值內容,都必須要以單引號(')括住,如同在本例中的形式。日期時間型別(date type)的資料內容就比較有彈性,但在這個導覽之中,我們仍然使用較固定的格式來表現。
地理資訊型別(point type)需要有座標組作為輸入,如下所示:
到目前為止,語法的使用需要你依照欄位宣告的次序擺放,而另一種語法可以允許你明確地指定資料相對應的欄位:
你可以將欄位以不同的次序擺放,甚或略去某些欄位,例如,precipitation 欄位(prcp)內容未知:
許多開發者會認為,在撰寫習慣上,明確指定欄位是比較好的方式。
請執行下列的指令,你將會擁有後續章節所需要的範例資料。
你可能需要使用 COPY 這個指令從文字檔案來載入大量的資料。這個指令會比 INSERT 要快上許多,因為 COPY 指令的設計就是為了大量資料輸入而產生的。它少了一些彈性,但提供了效率上的最佳表現。使用範例如下所示:
資料來源的檔案必須存在於後端的伺服器之中,並且可被 PostgreSQL 使用者(postgres)所存取,注意不是用戶端的主機,因為後端伺服器的服務需要直接讀取該檔案。你可以取得更多詳細說明,在 COPY 指令的說明頁面。
:從無到有,安裝一個 PostgreSQL 資料庫系統。
:認識 PostgreSQL 的資料庫架構。
:建立第一個 PostgreSQL 資料庫。
:開始存取你的 PostgreSQL 資料庫。
到目前為止,我們的一個查詢都只涉及到一個資料表。其實可以在同一個查詢中,同時查詢多個資料表,或者在同一個資料表之中同時處理多個資料列的資料。在一個查詢之中,涉及到同一個或多個不同的資料表中的資料,稱作為交叉查詢(join)。舉個例子來說,你希望同時列出天氣和城市位置的資料。要完成這項工作,我們需要關連資料表 weather 中的 city 欄位與表格 cities 中的 name 欄位,然後回傳符合條件的資料。
這只是一個概念式的模形,交叉查詢(join)會以更有效率的方式運行,並非真正需要比較每一種組合是否符合條件,不過這些過程對於使用者而言並不會產生操作或結果上的差異。
下列查詢會產生交叉查詢的結果:
在這個結果中可以觀察到兩件事情:
不會有關於 Hayward 的結果出現。這是因為在資料表 cities 中未有 Hayward 的資料,所以交叉查詢會忽略資料表 weather 中未能關連的資料。關於這點,我們很快就會有解決辦法。
有兩個欄位顯示了城市的名稱。這樣是正確的,因為來自於資料表 weather 和 cities 的欄位被串連起來了。實務上,這樣的結果並不令人滿意,所以也許你可以明確地指出輸出的欄位,取代「 * 」的使用:
練習:試試看,當 WHERE 表示式被省略的話,查詢語句的意義會怎麼樣?
因為所有的欄位都使用不同的名稱,所以解譯器會自動發現他們所屬的資料表為何。如果在兩個資料表之中,存在有相同名稱的欄位時,你最好明確指出確定的欄位,如下所示:
多數開發者認為,在交叉查詢中,明確指出確定的欄位名稱,是良好的撰寫習慣。這樣查詢就不會因為有相同的欄位名稱而產生錯誤。而相同名稱的欄位可能是開發後續才加入的,未指明的話,就可能造成意外的結果。
交叉查詢也可以寫成如下的另一種形式:
這種語法並不如上述的常見,但我們會在這裡說明,以幫助你在後續章節的學習。
現在我們要回到前面的問題,把 Hayward 的資料放在輸出的結果之中。我們要在查詢中做的是,掃描資料表 weather,找到有所關連的每一列資料;沒有關連到的資料列,我們要填上「空值」(null)在資料表 cities 相對的欄位之中。這樣的查詢我們稱作「外部交叉查詢」(outer join)。(先前的交叉查詢為「內部交叉查詢」(inner join))。這樣的查詢指令如下所示:
這種查詢稱作為「左側外部查詢」(left outer join),因為這個交叉查詢,放在左側的資料表中的資料列,一定會在結果中至少出現一次,而右側的資料表中,則只有輸出有關連到左側資料表的資料列。當左側資料表的資料列,並沒有在右側資料表中被關連到時,屬於右側資料表的欄位就會被填上空值輸出。
練習:也有「右側外部交叉查詢」(right outer join)和「完全外部交叉查詢」(full outer join),試著找出他們都做了些什麼。
我們也可以對同一個資料表做交叉查詢,稱作為「自我交叉查詢」(self join)。接下來的範例,假設我們希望找到所有氣溫範圍的天氣資料。所以我們需要讓 temp_lo 及 temp_hi 兩個欄位,和其他的 temp_lo 及 temp_high 相比較。我們可以用下列的查詢來符合需求:
這裡我們重新命名了資料表 weather 為 W1 及 W2,以在交叉查詢中區分左側及右側。你也可以在其他查詢中使用這個技巧,以節省輸入的複雜度,例如:
你將會在後續內容中,不斷練習到這樣的使用方式。
如同其他的關連式資料庫產品,PostgreSQL 也支援彙總查詢的功能。彙總查詢指的是能夠把多個資料列的資料經過計算,產生單一結果的功能。舉例來說, count、sum、avg(平均值)、max(最大值)、min(最小值)都是彙總查詢的函式。
這裡的例子,我們可以得到所有低溫中的最大值:
如果我們想要知道,這個數值是發生在哪一個城市?也許可以試試:
不過,這行不通,因為 max 不能使用在 WHERE 條件式當中。(會有這樣的限制,是因為 WHERE 條件式目的是要判斷有哪些資料列的資料應該被彙總計算,所以很明顯地,這件事必須要在彙整計算前發生,這就產生了矛盾。)所以,像本例的查詢一般會使用子查詢(subquery)來取得適當的結果:
這樣就對了,因為子查詢是一個獨立的查詢,它可以獨立進行彙總查詢,有別於括號以外的查詢語句。
彙總查詢和 GROUP BY 一起使用會很方便的。舉例來說,我們可以得到每個城市所觀測到的最高氣溫:
這個查詢對每個城市都輸出一列的結果。每一個彙總的結果,將整個資料表,以關連到的城市進行計算。 而我們可以進一步過濾資料內容,使用 HAVING:
如果限制所有 temp_lo 的數值必須要小於 40 (WHERE temp_lo < 40)的話,也可能得到相同的結果。 最後,如果我們只關心以"S"開頭的城市的話,可以這樣做:
LIKE 運算子進行特徵比對運算,這將會在 9.7. 特徵比對中進一步說明。
這裡很重要的是,瞭解 SQL 中 WHERE 和 HAVING 之間的行為。其根本上的差異是:WHERE 會在合併和彙總計算之前進行選擇資料的動作(也就是它控制著,哪些資料需要被彙總計算);而 HAVING 是在合併及彙整計算之後,才進行過濾資料的動作。所以,在 WHERE 條件式當中,絕不可以使用彙整運算式;另一方面,HAVING 條件式總是使用彙整運算式。(嚴格來說,你也可以不在 HAVING 條件式中使用彙整運算式,但很少人這樣使用,通常就會改寫到 WHERE 條式件當中,那會更有效率。)
在先前的例子當中,我們可以把城市名稱的限制放在 WHERE 條件式之中,因為它不需要彙總。這將會比放在 HAVING 條件式中更有效率,因為這樣可以避免合併及彙整運算整個表格,不用浪費時間在本來就會被過濾掉的資料上。
你可以創建一個新的資料表,為它取一個名字,並且宣告所有的欄位名稱與其資料型別:
你可以把上述內容在 psql 中輸入,包含換行字元不會影響判讀。psql 是以分號作為指令結束的判定。
空白(包含「空白」、「定位符號」和「換行符號」)都可以自由使用在 SQL 指令當中。這表示你可以將指令以不同的形式排版,甚至全部寫都在一行也沒問題。使用破折號,連續2個("--"),表示緊接的內容只是註解,直到該行結束為止。PostgreSQL 是不分大小寫字母的,包括各類關鍵字和描述語,除非是使用雙引號括起來的文字。(更精確地說,沒有被雙引號括起來的識別字,都會轉為小寫字母進行識別)
varchar(80) 表示指定一個資料型別,它可以儲放任意 80 個字元以內的字串。int 是一般認知的整數型別。real 表示資料是單精確度的浮點數。date 顧名思義,就是日期時間型別。(本例中欄位名稱和型別都使用 date,這可能是方便,也可能是困擾,端看你如何使用。)
PostgreSQL 支援標準的資料型別 int, smallint, real, double precision, char(N), varchar(N), date, time, timestamp, interval,也支援了複合型的地理資料型別。PostgreSQL 可以自訂組合任意數量的資料型別。語法上,資料型別名稱並不是保留關鍵字的範圍,除非特定的標準 SQL 支援需求之外。
第二個例子用來儲存城市及其所在的地理位置:
point 型別是一個 PostgreSQL專屬資料型別的範例。
最後,應該被點出來的是,如果你不再需要一個表格,或者想要重新以別的方式創建它,那麼你可以以下列的指令來移除它:
一旦你已經建立一個資料庫,你就可以開始以下列方式進行存取:
執行 PostgreSQL 互動式的終端程式,稱作 psql,它可以讓你輸入、編輯、執行 SQL 指令。
使用既有的圖型化介面工具,例如 pgAdmin 或是支援 ODBC 或 JDBC 的辦公室軟體,以建立並輸入資料到資料庫裡。不過這部份並未包含在這份手冊之中。
自行撰寫一個程式,可以使用許多種程式語言來完成。這個部份將會在第 IV 章中進行介紹。
在這份指南中,你可能會先使用 psql 來進行一些嘗試。你可以藉由下列指令開始操作 mydb 這個資料庫:
如果你並未指明資料庫名稱,那麼它預設會以你的使用者名稱作為資料庫名稱。在先前的章節使用 createdb 時,你已經知道這個隱含的規則了。
在 psql 中,你會以下列訊息開始:
最後一行也可能是:
這表示你是資料庫的超級使用者(superuser),如果你是自行安裝 PostgreSQL 的話,大概就會是這個情況。 作為一個超級使用者,表示你不會受限於任何存取控制。不過在這份指南中,這並不是重要的事。
如果你在啓動 psql 時遭遇了一些問題,那麼請回到前一節。createdb 和 psql 的行為很類似,如果前者正常,後者也應該如期運行。
最後一行會輸出的是 psql 的提示字串,它表示 psql 正在等待你輸入 SQL 查詢語句。試試下面的指令吧:
psql 程式中也內建了一些非 SQL 的命令。他們會以倒斜線(\)起頭。舉例來說,你可以輸入下列指令以取得一些有關 PostgreSQL 所支援的 SQL 語法資訊:
要離開 psql 的話,請輸入:
如此的話,psql 將會結束,並回到你的命令列介面之中。(想瞭解更多內建指令,在 psql 提示字串後輸入 \? 。)完整的 psql 說明,都記載在 psql 頁面之中。在這份指南中,我們並未使用這些功能,但你可以在需要的時候使用他們。
在這一章之中,提供了一個如何使用 SQL 進行簡易操作的大致概念。這裡主要讓你有基本的認識,但無法提供 SQL 完整且巨細靡遺的說明。許多書籍詳細介紹了 SQL,例如「Understanding the New SQL. A complete quide.」及「A Guide to the SQL Standard. A user's guid to the standard database language SQL.」。你應該瞭解的是,一些 PostgreSQL 語法來自於標準 SQL 的延伸。
在下面的例子當中,我們假設你已經建立了一個資料庫 mydb,如同前面章節所述,你也能夠使用 psql 了。
這些例子也放在 PostgreSQL 的原始碼之中,你可以在目錄 src/tutorial/ 下找到他們。(PostgreSQL的可執行套件可能未包含這些檔案)想要使用這些檔案的話,首先請切換到該目錄之下,然後執行 make:
這將會建立編譯 C 語言的程序,包含了使用者自訶的函式及型別。接下來,進行下列動作,以開始這個導覽:
\i 指令會去指定的檔案讀取內容,並且執行。而在 psql 的 -s 選項則可以使用單步模式執行,也就是在每一個與伺服器互動的指令之後暫停。這個指令被使用在本節的檔案 basics.sql 之中。
要把某些資料列從資料表中移除,就使用 DELETE 這個指令。假設你對於 Hayward 這個城市的天氣不再感興趣了,那麼你可以執行下列指令,來刪除資料表中的這些資料:
所有關於 Hayward 的資料都被刪除了。
這個指令有一個應該要特別注意的情況:
沒有任何限制的條件,DELETE 將會刪去所有該資料表中的資料,使成為空的資料表。資料庫系統並不會在這個動作執行前和你確認!
要從資料表(table)中取出資料,稱作資料表的查詢。要進行這個行為,你需要 SQL 中的 SELECT 指令。這個指令由幾個部份所組成,回傳列表(select list,想要回傳的欄位)、資料表列表(資料來源的資料表)、選擇性的條件定義(指定一些限制條件)。舉個例子來說,要取得資料表 weather 中所有的資料的話,請輸入:
這裡的星號 * 表示「所有欄位」。下列的指令會回傳相同的結果。
其輸出結果將會如下所示:
你可以在回傳列表中撰寫一些運算表示式,而不只是簡單的欄位引用。舉例來說,你可以輸入:
這應該會產生這樣的結果:
注意,「AS」被用來重新命名輸出的欄位。(選用)
查詢語句可以加上「WHERE」來設定限制條件,以指定哪些列才需要被回傳。WHERE 的內容是一個布林(truth value)表示式,而只有在其運算值為真(true)時,該列才會被回傳。一般的布林運算子(AND, OR, NOT)都是被允許出現在表示式中的。舉例來說,下列的指令將會回傳 San Francisco 在雨天的天氣數值:
結果:
你可以將結果進行排序:
在這個例子之中,其次序並沒有完全地被指定,所以你可能會得到 San Francisco 的列以另一種次序呈現。而你如果以下列指令查詢的話,那你就會得到如上但固定的結果:
你可以在查詢時去除重覆的列:
再一次,其結果的次序可能每次都不同,你可以同時使用 DISTINCT 及 ORDER BY 來確保能得到一致性的查詢結果:
你可以使用 UPDATE 指令以列為單位來更新資料。假設你發現氣溫的數值測量在 11 月 28 日之後都多了 2 度。你可以以下列語法來修正這些資料:
查看一下這些更新後的資料:
回想一下在第 2 章中的表格 weather 及 cities,思考下列問題: 你想要保證沒有另一個人可以新增在 cities 中沒有的城市資料到 weather 中。這就是所謂資料關連性的管理。在簡單的資料庫系統當中,可能會這樣實作:先檢查 cities 中是否已有對應的資料,然後再決定資料表 weather 中新增或拒絕新的天氣資料。這個辦法還有很多問題,而且很不方便,所以 PostgreSQL 可以幫助你解決這個需求。
新的資料表宣告如下所示:
現在嘗試新增一筆不合理的資料:
外部索引鍵或簡稱外部鍵(foreign key)的行為可以讓你的應用程式變得容易調整。我們在這個導覽中不會再深入這個簡單的例子了,但你可以在第 5 章取得進一步的資訊。正確地使用外部索引鍵,可以改善資料庫應用程式的品質,所以強烈建議一定要好好學習它。
讓我們回到 2.6 節的查詢範例。假設關連天氣資訊和城市位置的結果,是你的應用中特別常用的,但你並不想要每次都要輸入一長串的查詢語句。那麼,你可以為這個查詢語句建立一個「檢視表(View)」,你可以取一個名字,當你需要使用的時候,你可以把它當作一個資料表來使用:
妥善地使用檢視表,對於良好的 SQL 資料庫設計而言,是很關鍵的部份。檢視表允許你可封裝你的資料表結構與細節,當你的應用系統在逐步發展成熟的過程中,扮演一致性的資料介面。
檢視表可以用在大多數資料表可以使用的地方。而用檢視表來封裝其他檢視表的情況,也不少見。
繼承是一個物件導向資料庫的概念,它開啓了資料庫設計的更多可能性。
讓我們創建兩個資料表:cities 和 capitals。很自然地,首都(capitals)也是城市(cities),所以你希望有個方式,可以在列出所有城市時,同時也包含首都。如果你真的很清楚的話,你可以建立如下的結構:
這樣的查詢結果會是正確的,不過它有點不是很漂亮,當你需要更新一些資料的時候。
有一個更好的方法是這樣:
在這個例子中,captitals 繼承了 cities 的所有欄位(name, population, altitude)。欄位 name 的資料型別是文字型別(text),是一個 PostgreSQL 內建的資料型別,它允許字串長度是動態的。然後宣告 capitals 另外多一個欄位,state,以呈現它是屬於哪一個州。在 PostgreSQL,一個資料表可以繼承多個其他的資料格。
舉個例子,下面的查詢可以找出所有的城市名稱,包含各州的首都,而其海拔高過於 500 英呎以上:
回傳結果:
另一方面,下面的查詢可以列出非首都的城市,且其海拔在 500 英呎以上:
這裡的「ONLY」(cities之前),指的是這個查詢只要在資料表 cities 上就好,不包含繼承 cities 其他資料表。這裡許多我們都已經討論的指令 — SELECT、UPDATE、DELETE — 都支援 ONLY 這個修飾字。
雖然繼承經常被使用,但尚未整合唯一性限制或外部索引鍵的功能,這限制了它的可用性。詳情請參考 5.9 節的說明。
PostgreSQL 還有許多這份導覽中未能介紹到的功能,這裡主要是針對新鮮的 SQL 使用者所準備的內容。 這些功能將會在後續的章節進行更詳細的討論。
如果你覺得你需要更多介紹的資訊,可以到 PostgreSQL 的官方網站取得更多訊息。
交易(Transaction),是所有資料庫的基礎概念。基本上來說,一個交易指的是,一系列的執行步驟包裹在一起,其結果只有全部成功或全部失敗兩種情況的操作行為。而其即時的執行狀態,對於其他同時在進行的交易而言,相互之間都是不可見的。如果在執行過程中產生了錯誤而造成整個交易無法完成,那麼所有的指令都不會對資料庫原來的內容產生影響。
舉例來說,某個銀行資料庫存放著各個客戶的存款資訊,也存放著分行的存款總額資訊。假設我們想要轉帳 $100.00,從 Alice 的帳號轉到 Bob 的帳戶。可以很直觀地依敘述,直接以下列指令執行:
這些指令的細節在這裡並不重要,重要的是,有好幾個更新資料的動作要被執行。我們銀行的營業員需要保證所有的更新資料都要完成,或是保持原樣。如果因為系統錯誤,而造成 Bob 收到 $100.00,但 Alice 卻沒有轉出金額,就不是應該發生的事。又或是 Alice 轉出了現金,而 Bob 卻沒有轉入金額,她也不會是開心的客戶。我們需要具有保證交易安全的方法,也就是如果在執行過程中,有部份出了錯,那麼即使是已經執行的部份,也不會對資料庫產生影響。把這些更新資料的指令,包裝在一個交易之中,就是這個保證交易安全的方法。這樣的交易稱作為 atomic:從其他的交易的角度來看,整個行為只有完全執行,亦或是什麼都沒有做,兩種結果而已。
我們也希望有某個保證是,一旦某個交易被完成了,那麼會由資料庫系統發出通知,使它確實是永久性的資料,即使發生短暫的當機之後,資料也不會遺失。舉例來說,如果我們正在進行 Bob 的提款系統操作行為,在他走出銀行大門之後,我們不要有任何可能性使他的提款記錄消失。一個具備交易安全的資料庫,會將這裡交易裡的更新行為,在它們被回報完成之前,都記錄在長效型儲存裝置上(也就是磁碟機)。
交易安全資料庫的另一個重要性質是, atomic update 的概念:當多個交易同時在進行時,每一個交易都不能夠看到其他交易未完成交易的資料狀態。舉個例子,如果某個交易正在進行總計所有分行的餘額,它不會只包含 Alice 的分行的提款,或不計算 Bob 的分行的存款,反之亦然。所以交易必須是全有全無的結果,而不只是資料庫資料的永久性,還包含了交易執行過程的可視性。一個未完成的交易直到完全完成之前,其間資料的改變,對其他的交易而言都看不見;而當交易完成的同時,資料的改變也同時全部呈現出來。
在 PostgreSQL 中,所謂的交易,是以 SQL 的 BEGIN 及 COMMIT 兩個指令相夾的過程。 所以我們前述的銀行交易實際上會像這樣:
如果在交易的過程之中,我們決定不要完成交易(也許我們發現 Alice 的帳戶餘額不足),我們可以使用 ROLLBACK 指令來取代 COMMIT,那麼所有資料的變更都會取消。
PostgreSQL 一般將每一個 SQL 指令都視為一個交易來執行。如果你並沒有使用 BEGIN 指令,那麼每一個個別的指令就會隱含 BEGIN 先行,然後如果成功的話,COMMIT 也自動執行。一系列被 BEGIN 和 COMMIT 包夾的區域,有時候就稱為交易區塊。
有一些用戶端程式會自動加入執行 BEGIN 及 COMMIT 指令,使得你不需要要求就獲得交易區塊的效果。請詳閱你所所用的工具文件。
還有一種交易的控制更為細緻,就是使用交易儲存點(savepoint)。交易儲存點允許你可以選擇性地取消部份交易,而只成交剩下的部份。使用 SAVEPOINT 指令定義一個交易儲存點之後,你可以使用 ROLLBACK,回復該交易狀態到交易儲存點。所有在交易儲存點之後所造成的資料庫變更,都會被回復,但交易儲存點之前的變更會暫時留存。
在回復到交易儲存點之後,它仍然可以繼續進行,而你可以多次回到該儲存點。相反地,如果你確定你不要再回復到某個特定的交易儲存點時,它也可以被釋放出來,系統資源也可以獲得舒解。記得,釋放或回復到一個交易儲存點時,將會自動釋放所有在那之後的交易儲存點。
所有這些過程都發生在交易區塊之中,所有沒有任何改變會讓其他資料庫連線所發現。當你確認完成了交易區塊的時候,完成交易的動作就會讓其他的連線知道,也能發現資料的改變;同時,回復的動作也會再也無法執行了。
記得這個銀行的資料庫,假設我們從 Alice 的帳號提出了 $100.00,然後存入了 Bob 的帳戶之中,隨後又發現應該要存到 Wally 的帳戶。我們可以使用交易儲存點來完成這個過程:
當然,這個例子是過度於簡化了,但這呈現出在交易區塊中使用交易儲存點,有著更多的可能性。進一步來說,ROLLBACK TO 是唯一能夠控制交易區塊執行流程的方式,當系統產生錯誤時,可以縮小回復的範圍,而不是只能全部回復再執行。
窗函數(window function)提供了在一個資料表中,進行資料列與資料列之間的關連運算。這部份可以和彙總函數的功能相呼應。然而,窗函數並無法像彙總函數一樣,把多個資料列運算合併為單一資料列的結果。取而代之的是,這些資料列仍然是分開並列的狀態。在這樣的情境下,窗函數能讓查詢結果的每一個資料列,都得到更多資訊。
這裡有一個列子,試著比較每一個員工他的薪資及他的部門平均薪資的情況:
前面三個欄位是由資料表 empsalary 直接取得,每一個資料列就是該資料表的每一個資料列列。而第四個欄位則呈現整個資料表中,與其 depname 相同的平均薪資。(這實際上就是由非窗函數的 avg 彙總而得,只是 OVER 修飾字讓它成為窗函數,透過「窗」的可見範圍做計算。)
窗函數都會使用 OVER 修飾字,然後緊接著窗函數及其參數。這是在語法上使其有別於一般函數或非窗函數的彙總。OVER 區段需要確切指出如何分組要被窗函數計算的資料列。PARTITION BY 在 OVER 中,意思是要以 PARTITION BY 之後的表示式來分組或拆分資料列的資料。對於每一個資料列而言,窗函數的結果是,透過所有和該資料列相同分組的資料,共同運算而得。
你也可以控制列被窗函數處理的次序,透過在 OVER 中加入 ORDER BY。(窗內的 ORDER BY 不見得需要對應到資料列輸出的次序)例子如下:
如上所示,rank 函數為每個有使用 ORDER BY 的分組,標記一系列數字的次序。rank 不需要特定的參數,因為它標記的範圍一定是整個 OVER 所涵蓋定的範圍。
窗函數所計算的範圍,是一個虛擬資料表的概念,是由 WHERE、GROUP BY、HAVING、或其他方式虛擬出來的。舉例來說,當某個資料列被 WHERE 過濾掉時,它也不會被任何窗函數看見。一個查詢中可以包含多個窗函數,透過不同 OVER 修飾字的指定,將資料做不同觀點的處理。但他們都會在一個相同的虛擬資料表中進行處理。
我們已經瞭解如果次序不重要的話, ORDER BY 可以被省略;且如果所有的資料列都只區分成一組的話,其實 PARITION BY 也可以省略。
還有另一個窗函數相關的重要概念:對於每一個資料列來說,它會在分組中還有個分組,另稱作窗框(window frame),有一些窗函數只對窗框裡的資料列進行處理,而不是整個分組。預設的情況是,如果 ORDER BY 被指定了,以 ORDER BY 排序後,那麼窗框的範圍就是從分組的第一列到該列為止,而在那之後資料列的值都會相同。當 ORDER BY 被省略的時候,預設窗框的範圍就是整個分組。下面是使用 sum 的例子:
上面可以看到,因為在 OVER 裡面沒有 ORDER BY,窗框就等於整個分組,甚至因為沒有 PARTITION BY,所以等於整個資料表。換句話說,每一個資料列總和都是整個資料表的總計,所以我們在每一個資料列中都得到相同的結果。但如果我們加入了 ORDER BY 之後,結果將會不同:
這裡的總和就是從第一筆(最小),加計到每一列,包含薪資相同的每一列(注意薪資相同的)。
窗函數只允許出現在 SELECT 的輸出列表及 ORDER BY 子句裡,在其他地方都是被禁止的,像是 GROUP BY,HAVING,WHERE等區段。這是因為窗函數在邏輯上,都是在他們處理完之後才進一步處理資料的。也就是說,窗函數是在非窗函數之後才執行的。這意指在窗函數中使用非窗函數是可以的,但反過來就不行了。
如果有一個需要在窗函數處理完再進行過濾或分組的查詢的話,你可以使用子查詢。舉列來說:
上面的查詢只會顯示內層查詢的次序(rank)小於 3 的資料。
當一個查詢使用了多個窗函數的話,它就會分別使用 OVER 子句來描述,但如果相同的分組方式要被多個函數所引用的話,就重覆了,也容易出錯。這種情況可以使用 WINDOW 子句來取一個別名,來取代 OVER。舉個例子: