Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
以下所提到慣例,用於指令的語法描述上(均為半型字元):
中括號([
和 ]
)指可選擇是否輸入的選項。(在 Tcl 指令的語法中,習慣使用問號 ?
來表達這樣的可選擇性)
大括號({
和 }
)及垂直線(|
)指的是必須要輸入的部份。
連續句點(...
)指的是該段落可以允許不斷重覆。
為了使說明更簡潔:
SQL 指令會跟在命令提示字元 =>
之後
Shell 命令會跟在命令提示字元 $
之後。雖然一般而言,提示字元可能不會顯示。
關於操作人員的定義:
管理者(Administrator) 一般的定義是負責安裝及運行資料庫系統的人
使用者(User) 指的是任何正在使用資料庫的人,或者正要使用任何 PostgreSQL 相關系統的人。
這些定義不應該被解釋得太過嚴格,在本文件中,對於系統管理的工作,並沒有固定的假設。
現在被稱為 PostgreSQL 的物件關聯式資料庫管理系統,是根據美國加州伯克萊大學所研發的 POSTGRES 衍生而成。經過超過二十年以上的演進,PostgreSQL 現在是世界上最先進的開源資料庫系統。
POSTGRES 專案是由 Michael Stonebraker 教授領導的團隊進行研發,由美國國防高等研究計劃署(DARPA, Defense Advanced Research Projects Agency)、美國陸軍研究辦公室(ARO, the Army Research Office)、美國國家科學基金會(NSF, the National Science Foundation)及美國電磁系統實驗室(ESL, Inc)所贊助。POSTGRES 專案始於 1986 年,最原始的設計,"The design of POSTGRES",作為開端,其最初的資料結構模型則揭露於"The POSTGRES data model"。規則系統設計發表於"The design of the POSTGRES rules system",而當時的關連式資料儲存的架構則刊載於"The design of the POSTGRES storage system"。
POSTGRES 接下進行了幾次重大的變革。第一代的"demoware"在 1987 年真的實作成為可用的系統,並在 1988 年的 ACM-SIGMOD 研討會中進行展示,並在 1989 年六月,釋出了第一版可供外部使用者使用的資料庫系統。為了回應當時使用者對於第一代規則系統的批評,其規則系統重新進行設計,並在隔年 1990 年的六月份,隨即推出第二版系統,搭載新的規則系統設計。第三版系統則於 1991 年發表,新增支援多重儲存管理機制,改善查詢處理器,並又改寫了規則系統。如此直到 Postgres 95 誕生之前,主要都專注於移植性及可信賴度的發展。
POSTGRES 接下來開始被運用在許多不同的研究和產品上,財務資料分析系統、噴氣引擎效能監控系統、小行星追蹤資料庫、醫療資訊系統、以及數個地理資訊系統。POSTGRES 也被好幾所大學用於其教學工具。最後,由 Illustra Information Technologies(後來併入 Informix,而 Informix 目前為 IBM 所擁有)技術移轉,並將其商業化。於 1992 年末,POSTGRES 成為 Sequoia 2000 scientific computing project 主要的資料管理系統。
在 1993 年間,用戶數量呈現倍數成長,伴隨而來的是大量的程式碼維護與服務支援,占去絕大部份原來應該進行研究的時間。為了減少維運的負擔,伯克萊的 POSTGRES 專案正式終止於 4.2 版。
1994年,Andrew Yu 和 Jolly Chen 在 POSTGRES 增加了 SQL 語法的直譯器,並且以新的 Postgres95 為名,在網路上開放讓全世界的人使用,他們成為伯克萊 POSTGRES 原始碼最初的繼承者。
Postgres95 的程式碼是完全以 ANSI C 開發,並且輕量化了 25%。許多內部的改良增進了效率及可維護性。當時 Wisconsin Benchmark 進行測試,Postgres95 在 1.0.x 時的效能比原始的 POSTGRES 4.2 快了約 30% 至 50%。除了一些錯誤修正之外,還有下面這些主要的改良:
原有的 PostQUEL 以 SQL(實作於伺服器端)所取代。(連接介面在 PostQUEL 之後便採 libpq 函式庫)子查詢一直到 PostgreSQL 出現之前都還未支援,但在 Postgres95 便已能使用自訂的 SQL 函數,聚合函數 Aggregate function 則被重新實作。GROUP BY
查詢語句也在此時被加入。
提供新的工具 psql
可進行互動式的 SQL 操作,採用的是 GNU Readline 的技術,大量地取代了老舊的管理工具。
提供新的前端函式庫 libpgtcl
,支援 Tcl-based 用戶端程式。還有一個簡易的 shell 接口 pgtclsh
,使用新的 Tcl 命令來和 Postgres95 伺服器進行操作。
重新改寫了大型物件的交換介面,只保留大型物件翻轉(inversion)作為儲存大型物件的唯一機制。(移除了 inversion 檔案系統)
淘汰了實例層級(instance-level)的規則系統,但其規則仍用於重構規則所使用。
製作了一個簡短的說明,介紹標準的 SQL 功能,並隨 Postgres95 原始碼發佈。
使用 GNU make(取代 BSD make)來編譯程式碼。除此之外,Postgres95 也支援使用未修正的 GCC 編譯器(修正高精度資料對齊問題)。
1996 年,「Postgres95」這個名稱很明顯不再適合。於是我們選擇了新的名稱「PostgreSQL」來呈現出與原始 POSTGRES 之間的源由,也彰顯了結合 SQL 力量的意義。同時,我們設定其版本由 6.0 開始,重回伯克萊 POSTGRES 專案的版號序列。
許多人持續使用「Postgres」(現在已經很少使用全大寫字母表現)來代表 PostgreSQL,是因為傳統,也可能是因為比較好發音。這樣的用法也廣為用於暱稱或別名。
Postgres95 的發展主要在於瞭解及定義伺服器程式既有的問題,而 PostgreSQL 則更重視系統的能力與爭議性的功能上,不過所有的工作是全面性的。
更多有關於 PostgreSQL 的發展,請參閱 附錄 E。
本使用手冊由台灣 PostgreSQL 社群提供,翻譯自 PostgreSQL 官方使用手冊,以推廣 PostgreSQL 於台灣的應用。
本使用手冊目前編譯內容為 PostgreSQL 12。
每一個頁面均附上對應連結,翻譯未詳盡之處,可對照閱讀。 未翻譯完成之段落,將暫以原文(英文)替代。
參與協作請在任何頁面,點選右上角的「Edit on GitHub」,修改後直接送 PR 給我們即可。 (只翻一句也可以唷!)
任何問題或建議可以 Email 給我們的文件小組:
PostgreSQL 是美國加州伯克萊大學資訊科學系基於 所研發的物件關聯式資料庫管理系統(ORDBMS, Object-Relational Database Management System)。POSTGRES 中的許多重要概念成為日後一些商用資料庫系統重要的一部份。
PostgreSQL 由伯克萊大學公開其原始碼所誕生,它支援了大多數的標準 SQL 語法,並提供許多先進的功能:
複雜查詢(complex queries)
外部索引鍵(foreign keys)
觸發器(triggers)
可更新檢查表(updatable views)
事務完整性(transactional integrity)
多版本併行控制(multiversion concurrency control)
同時,PostgreSQL 也支援讓使用者能以自己的方式進行擴充。比如透過新增:
資料型別(data types)
函數(functions)
操作(operators)
聚合函數(aggregate functions)
索引方法(index methods)
過程式語言(procedural languages)
並且基於自由許可證,任何人都能夠以任何目的,免費地使用、修改、與散布 PostgreSQL,不論是個人使用、商業用途還是學術研究。
除了此份文件之外,PostgreSQL 還有其他的參考資源:
PostgreSQL的 記錄了這個專案的 ,,以及其他更多不同主題的資訊。
PostgreSQL wiki 也有的頁面喔。
PostgreSQL 的,有最新軟體的釋出訊息,讓你能夠和 PostgreSQL 相處得更棒!
郵件列表的功能,是一個為您解答疑問的好地方,你也可以分享使用經驗給其他同好,或直接和開發者溝通。詳情請參閱 PostgreSQL 的官方網站。
PostgreSQL 是一個開源的專案,也就是說,它仰賴社群的每一個人給予支持。當你開始使用 PostgreSQL,你會需要其他人的幫助,可能是透過文件或是郵件列表的功能。請考慮也可以回饋您的知識。在閱讀郵件列表和回答疑問的同時,如果你學到了未被文件記載的知識時,請寫下來,並且供獻出來。如果你撰寫了一些程式碼增加了特別的功能,也希望能夠回饋到社群之中。
本手冊是 PostgreSQL 的官方手冊。由 PostgreSQL 開發人員和其他志願者在 PostgreSQL 軟體開發的同時所撰寫的。它描述了目前 PostgreSQL 版本正式支援的所有功能。
為了使有關 PostgreSQL 的大量資訊易於管理,本書劃分為幾個部分。每個部分針對的是不同需求的使用者,或針對處於 PostgreSQL 經驗不同階段的使用者:
第一部分是對新使用者的入門簡介。
第二部分將介紹 SQL 查詢語言環境,包括資料型別、函數以及使用者層級的效能調教。每個 PostgreSQL 使用者都應該閱讀此部份的內容。
第三部分則介紹伺服器的安裝及管理。任何維運 PostgreSQL 伺服器的人,無論是供私人使用還是提供給其他人使用,都應該閱讀此部分。
第四部分描述了 PostgreSQL 用戶端的程式設計介面。
第五部分為進階使用者提供有關資料庫服務進階功能的資訊。主題包括使用者定義的資料型別與函數。
第六部分包含有關 SQL 命令、用戶端和伺服器程式的參考資訊。這部分以命令或程序分類結構化資訊。
第七部分包含了對 PostgreSQL 開發人員可能有用的各種資訊。
:從無到有,安裝一個 PostgreSQL 資料庫系統。
:認識 PostgreSQL 的資料庫架構。
:建立第一個 PostgreSQL 資料庫。
:開始存取你的 PostgreSQL 資料庫。
本篇談的是如何回報問題到 PostgreSQL 官方組織,而您目前閱覽的正體中文手冊並非由官方提供,所以如果您希望指出的問題是本手冊的相關問題,請透過 討論區 或 台灣 PostgreSQL 使用者社群 所提供的聯絡資訊進行回報。
如果你在 PostgreSQL 中發現了問題,我們會很希望可以得到通知。你的問題回報可以讓 PostgreSQL 變得更值得信任,因為百密仍有一疏,PostgreSQL 無法保證在任何平台或任何情況下,都一定是完美無缺的。
下面的建議提供你在回報問題時能夠更有效率。你不一定要完全遵照下面的方式,但如果你試著遵循的話,對大家都有幫助。
我們無法保證可以立即修正所有的錯誤。但如果那個問題是明顯的、關鍵的、或是有重大影響的,那就會有人進行瞭解。也可能會回覆你更新你的資料庫版本,如果是因為版本問題的話。我們也可能會判定該錯誤不會被修正,在我們進行重大修改之前;又也許它不容易簡單處理,而且有其他更重要的需求排程已經在進行中。如果你需要立即性的支援,請接洽當地的商業服務。
在報告錯誤之前,請再三閱讀文件,以確認你真的在進行你正在嘗試的事情。 如果從文件中不清楚是否可以做某事,請回報;這是屬於文件的一個錯誤。如果確實證明一個程式與文件所描述地不同,那就是一個錯誤。這可能包括但不限於以下情況:
程式以致命信號(fatal signal)或程式中某個問題造成作業系統錯誤訊息而終止。(反例可能是「磁盤已滿」的訊息,因為您必須自己修復。)
程式對於任何輸入都產生錯誤輸出結果。
程式拒絕接受有效的輸入 (如文件中所定義的)。
程式接受了無效的輸入,卻沒有警告或錯誤消息。但請記住,您對於無效輸入的認知可能來自於我們對傳統做法的延伸或相容性。
PostgreSQL 在支援的平台上,按照指示進行編譯,構建或安裝卻失敗了。
這裡的「程式」是指任何可執行文件,不僅僅是後端執行的程序。
緩慢或資源匱乏不一定是一個錯誤。閱讀文件或在某個郵件列表中提問,可以幫助你調整應用程式。不符合 SQL 標準不代表是錯誤,除非明確聲明相容某個特定的功能。
在繼續之前,請檢查 TODO 列表和常見問題解答,看看您的錯誤是否已知。 如果您無法瞭解 TODO 列表中的資訊,請報告你的問題。我們至少可以做的是使 TODO 列表更清楚。
關於錯誤報告最重要的是陳述所有事實並且只有事實。不要對錯誤的可能性進行推測,比如用「似乎(it seemed to do)」字句進行描述或程式的哪個部分有故障。如果你不熟悉實作的方法,你可能會猜測錯誤,而無法幫助我們。即使你有知識性的解釋,也應該是對事實的補充而不是替代它們。如果我們要修復這個錯誤,我們還是首先要看到它發生。報告裸露的事實是相對簡單的(你可以從屏幕上複製貼上它們),但是經常重要的細節被忽略,因為有人認為這並不重要,或者認為報告被理解是應當的。
每個錯誤報告中都應包含以下內容:
程式執行步驟的確切順序是重現問題所必需的。這應該是獨立的;如果輸出應該依賴於資料表中的資料,那麼沒有包含前面的 CREATE TABLE
和 INSERT
語句的 SELECT
語句是不夠的。我們沒有時間來對你的資料庫進行逆向工程,如果我們需要建立自己的資料庫的話,我們很可能會錯過這個問題。
SQL 相關問題的測試最佳格式是可以透過 psql
執行並重現問題。(請確認您的 ~/.psqlrc
啟動設定中沒有任何內容。)建立這個檔案的一個簡單方式是使用 pg_dump
來轉出設定該情境的資料表宣告及資料,然後加入產生問題的查詢語句。我們希望您盡量減少您的問題規模,但這並不是絕對必要的。如果錯誤是可重現的,我們會以任何一種方式找到它。
如果您的應用程式使用某些其他客戶端界面(如 PHP),請嘗試突顯那些問題查詢語句。我們應該不會設置一個網頁伺服器來重現您的問題。無論如何,請記得提供確切的輸入檔案;不要猜測「大文件」或「中型數據庫」等問題的可能性。因為這些訊息不夠精確,沒有參考價值。
你所得到的輸出。請不要說「不能用(didn't work)」或「壞掉了(crashed)」。如果出現錯誤訊息,請列出來,即使您並不瞭解它。如果程式終止是因為作業系統錯誤,請說明哪個系統錯誤。如果沒有發生任何事情,也如實說明。即使您的測試案例的結果是當機或其他明顯的情況,也不一定會在我們的平台上發生。如果可以的話,最簡單的方法是從終端視窗中複製輸出內容。
注意如果您要報告錯誤訊息,請取得該訊息最詳細的形式。在
psql
,事先輸入\set VERBOSITY verbose
。 如果從伺服器記錄取得訊息,請在執行時將參數log_error_verbosity
設定為 verbose,以便記錄所有詳細訊息。如果是嚴重錯誤的情況下,客戶端報告的錯誤訊息可能不包含所有可用的信息,還請查看資料庫伺服器的系統記錄輸出。如果你沒有保留伺服器的系統記錄,那麼這是開始這樣做的好時機。
你所期待的輸出情況對於情境說明非常重要。如果你只是寫「這個命令給我這樣的輸出」或「這結果不是我的預期」,我們可以自己運行它,檢視該輸出結果,並認為它看起來正常,正是我們的預期。我們不應該把時間花在解讀你的命令背後確切的語意。特別是不要僅僅說「這不是 SQL 或 Oracle 所做的那樣」。從 SQL 挖掘所謂正確的行為並不是一件有趣的事情,我們更不會知道所有其他關連式資料庫的做法。(如果你的問題是當機,很明顯地你可以省略此項。)
任何命令列選項和其他啟動選項,包括您從預設值修改的任何相關的環境變數或設定檔案。再次提醒,請提供確切的訊息。如果您正在使用預先封裝好的套件,在開機時啟動資料庫伺服器,您應該嘗試瞭解它如何進行。
所有你所做的與安裝說明不同之處。
PostgreSQL 的版本。你可以執行 SELECT version();
來找到你正在連線的資料庫系統版本。大多數的程式工具也會支援 --version
選項;至少 postgres --version
和 psql --version
都可以使用。如果功能或選項不存在,那麼您的版本已經太舊而無法進行升級。如果您運行預先編譯的套件(如 RPM),請說明,包括該套件可能具有的子版本。如果您正在使用 Git 某個快照,請說明快照版本,包括提交碼(commit hash)。
如果您的版本比 10 還舊,我們幾乎肯定會告訴您進行升級。每個新版本都有很多錯誤修復和改進,所以很可能在 PostgreSQL 的舊版本中遇到的錯誤已經被修復了。我們只能對使用較早版本的 PostgreSQL 伺服器提供有限的支援;如果你的需求多於我們所能提供的,請考慮取得商業支援合約。
執行平台資訊。這包括作業系統核心名稱和版本,C 語言函式庫,中央處理器、記憶體資訊等等。在大多數情況下,報告系統供應商和版本是足夠的,但不要假設每個人都知道「Debian」究竟是什麼,或是每個人都運行在 i386 上。如果您有安裝問題,則還需要你的機器上有關系統工具組的訊息(編譯器、make 工具等等)。
不要擔心你的錯誤報告會因此而變得冗長。這是一個事實過程的呈現。第一次報告所有事情,對我們比較好的做法是,儘量把事實從你身上擠出來。 另一方面,如果你輸入的檔案很大,持平來說,首先要問是否有人有興趣去研究它。這裡有一篇 文章,概述了有關報告錯誤的更多建議。
不要花所有的時間來指出輸入中的哪些變化會使問題消失。這可能對解決問題沒有幫助。如果事實證明該錯誤不能立即解決,您仍然有時間找到並分享您的解決方案。此外,再一次,不要浪費你的時間猜測為什麼這個問題會存在。我們會很快找到。
在撰寫錯誤報告時,請避免混淆術語。這個軟體整體來說被稱為「PostgreSQL」,有時候會簡稱為「Postgres」。如果你特別在談論後端的程序,請明確指出,而不要只是說「PostgreSQL 當掉了」。其中一個後端程序的當掉與主要的「postgres」程序當掉有很大的不同;當你的意思是某個後端程序終止時,請不要說「伺服器當掉了」,反之亦然。此外,例如交互式前端的「psql」用戶端程序與後端完全分離的。請嘗試具體說明問題是在用戶端還是伺服器端。
一般來說,將錯誤報告發送到錯誤報告的郵件列表:<
pgsql-bugs@postgresql.org
>
。你需要為你的電子郵件使用描述性主題,可能是錯誤消息的一部分。
另一種方法是填寫官方網站上提供的錯誤報告網頁式表單。用這個方式的話,它也會把內容轉寄到<
pgsql-bugs@postgresql.org
>
郵件列表之中。
如果您的錯誤報告隱含有安全疑慮,並且您希望它不會在公共討論區中立即顯示,請不要將其發送到pgsql-bugs。安全問題可以非公開地回報至<
security@postgresql.org
>
。
不要向任何用戶的郵件列表發送錯誤報告,像是<
pgsql-sql@postgresql.org
>
或<
pgsql-general@postgresql.org
>
。這些郵件列表用於回答用戶的問題,用戶通常不希望收到錯誤報告。更重要的是,他們不太可能修復它們。
另外,請不要向開發者的郵件列表發送報告<
pgsql-hackers@postgresql.org
>
。這個列表用於討論 PostgreSQL 的開發,如果我們可以將問題報告分開來,那將是很棒的。 不過,我們可能會選擇討論你的錯誤報告pgsql-hackers
,如果那個問題需要更多討論的話。
如果你的問題是使用手冊,回報問題的最佳位置是使用手冊的郵件列表<
pgsql-docs@postgresql.org
>
。請具體說明文件的哪一部分讓你不滿意。
如果您的錯誤是非支持平台上的可移植性問題,請寄送郵件到<
pgsql-hackers@postgresql.org
>
,讓我們(和你)可以將 PostgreSQL 移植到你的平台上。
注意不幸由於垃圾郵件數量,所有上述電子郵件地址都是封閉的郵件列表。也就是說,您需要訂閱列表才能發布。(然而,您不需要訂閱使用錯誤報告 Web 表單。)如果你想發送郵件但不想接收郵件列表,您可以訂閱並設置您的訂閱選項為「nomail」。想得到更多訊息的話,請寄一封郵件到
<
majordomo@postgresql.org
>
,內文請輸入「help」一個單字,就會自動回覆你進一步的訊息。`
你需要先進行安裝,才能開始使用 PostgreSQL。當然,PostgreSQL 也可能已經被安裝在你的系統之中,因為你的作業系統預設套件包含了 PostgreSQL,或其他系統管理者已先行安裝。如果是這樣的話,那麼你應該先瞭解作業系統的資訊,或向你的系統管理員取得存取方式的資訊。
如果你並不確定 PostgreSQL 是否已經可以使用,那麼你也可以自行安裝試試。這樣做並不是很困難,而且是很好的操作練習。PostgreSQL 可以以一般使用者進行安裝,它並不需要系統管理者(root)的權限才能安裝。
如果你打算自行安裝 PostgreSQL,你可以參考第 16 章的指令進行,完成之後再回到這裡,以瞭解下面有關設定環境變數的內容。
如果你的系統管理者並非以預設的方式安裝,你可能還有一些額外的工作要做。例如,如果資料庫主機其實是遠端的伺服器,你會需要設定 PGHOST 的環境變數,將其指向資料庫主機的網路名稱。而 PGPORT 變數也是必須要設定的。最基本的情境是,如果你嘗試啓動一個應用程式,而它回報它無法取得資料庫連線時,你就必須洽詢你的系統管理者。而如果系統管理者就是你自己,那麼你應該依文件再確認你的環境設定是正確的。如果你仍然並不清楚前面所描述的事項,請詳細閱讀下一節的內容。
在開始使用之前,你需要瞭解基本的 PostgreSQL 系統架構。瞭解 PostgreSQL 如何回應操作,有助於讓你更清楚理解接下來的說明。
以資料庫的術語來說,PostgreSQL 採用了主從式架構(client/server)。PostgreSQL 會在進行下列操作時保持連線:
伺服器的執行程序,負責管理資料庫的檔案、受理用戶端的連線要求、執行相對應的資料庫動作。這樣的資料庫伺服端程式稱之為「postgres」。
用戶端的程式用來發起資料庫操作的行為,其設計的形態很廣泛:可能是文字介面的工具、圖型介面的程式、將資料庫內容顯示成網頁的網際網路伺服器、甚或是專用的資料庫管理工具。有一些用戶端程式是由 PostgreSQL 官方所提供,大部份由第三方的其他使用者所開發。
如同一般的主從式架構,用戶端與伺服端可以是兩台不同的主機,而他們透過 TCP/IP 的網路協定溝通。你應該將這個觀念謹記在心,因為某些在用戶端可以被存取的檔案,在伺服端可能就無法存取(或使用不同的檔案名稱)。
PostgreSQL 伺服器可以管理來自多個用戶端的同時連線。為了達到這樣的功能,它會自我複製(fork)成新的執行程序,一對一地處理每一個連線。這個部份進一步來說,用戶端和新的伺服器執行程序之間的溝通,並不需要原始的 postgres 執行程序介入。也就是說,主要的資料庫服務執行程序會持續等待其他用戶端的連線,協助安排好其與伺服端執行程序的配對之後便完全交接,再回到等待的狀態。(當然,使用者完全不會察覺這些行為,在此說明僅僅是為了整體性的概念描繪)
第一個測試確認你是否能夠存取一個資料庫服務,就是嘗試去建立一個資料庫。一個執行中的 PostgreSQL 服務可以管理許多個資料庫。一般來說,每一個專案或使用者會分開使用不同的資料庫。
你的系統管理員也可能已經為你建立了一個資料庫,如果是這樣的話,那你可以略過本節說明,直接進入到下一節的內容。
要建立一個新的資料庫,在本例中取名叫「mydb」,你可以使用以下的命令:
如果在這個步驟沒有產生任何回應,那就是成功了。你可以跳過本節剩餘的部份。
但你如果看到如下的訊息:
這個訊息代表 PostgreSQL 並沒有被正確的安裝。不是它沒有被安裝好,那就是你的命令路徑設定並未包含這個指令。 嘗試使用下列這個包含絕對路徑的指令看看:
命令路徑在你的系統可能會有些不同。洽詢你的系統管理員,或著檢查安裝步驟以修正這個情況。
另一種回應可能是如此:
這代表了資料庫服務尚未啓動,或者它並不存在於createdb預設連線的位置。同樣地,檢查安裝的步驟或洽詢系統管理者。
而另一種回應也可能是:
這裡指出你用來連線的使用者名稱。這種情況可能會發生在你的資料庫管理員並未建立屬於你的資料庫。(PostgreSQL 的使用者帳戶是獨立於作業系統的使用者帳戶的)如果你是資料庫管理員,請參閱第 21 章,進行建立資料庫帳戶。你必須是 PostgreSQL 初始安裝的管理者(通常是 postgres),以建立第一個一般資料庫使用者的帳戶。這個情況也可能發生在,你被發配的 PostgreSQL 使用者名稱有別於你的作業系統使用者名稱,如果是這樣的話,那你需要在指令上使用 -U 選項,或者設定 PGUSER 環境變數,以指定你的 PostgreSQL 使用者名稱。
如果你有一個資料庫帳戶,但你並沒有建立資料庫的權限,你將會看到下列訊息:
並非每一個使用者都被授權可以建立一個新的資料庫。如果 PostgreSQL 拒絕你建立資料庫,那麼系統管理者就需要賦予你建立資料庫的權限。洽詢你的系統管理者,如果是這種情況的話。如果你是自行安裝 PostgreSQL,那麼你應該以你啓動資料庫服務的使用者登入作業系統,再嘗試這個操作。
你也可以建立資料庫,但使用其他的名稱。PostgreSQL 允許在資料庫系統中建立無限制數量的資料庫。資料庫名稱必須是以英文字母為開頭,總長度限制為 63 位元組。一個簡便的方式是,建立一個與你使用者名稱同名的資料庫。許多工具會預設假定資料庫名稱和你同名,所以這可以省略一些文字的輸入。要建立這樣的資料庫,只要簡單地輸入:
如果你不再使用你的資料庫,你可以移除它。舉例來說,你是 mydb 這個資料庫的擁有者(建立者),你可以使用下列指令來消毁它:
(對這個指令來說,資料庫名稱並不會預設使用你的使用者同名資料庫。你必須明確地指定名稱)這個動作會完全地移除所有和這個資料庫相關的檔案,並且沒有回復的可能,所以要進行這個動作的話,請一定要考慮清楚。
一旦你已經建立一個資料庫,你就可以開始以下列方式進行存取:
執行 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 頁面之中。在這份指南中,我們並未使用這些功能,但你可以在需要的時候使用他們。
本章適合初學資料庫的朋友閱讀,以簡單的語法範例,實際操作以瞭解資料庫的運作方式。事實上,更複雜的資料庫行為,也不脫這個基本的操作模式。
PostgreSQL 是一個關連式資料庫管理系統(RDBMS)。這表示它是一個管理關連性質資料的系統。關連性,基本上在數學裡是以資料表(table)的形式來表現的。今天,以資料表為形式儲存資料是很常見的事,它是很自然的表現,但也有很多其他組識資料庫的方式。在 Unix-like 的作業系統中,檔案和目錄是一個階層式資料庫的案例。更先進的發展是採用物件導向式的資料庫。
每一個資料表是很多資料列(row)的集合。而每一個資料列則以許多相同集合的欄位(column)所組成。每一個欄位都被指定了特定的資料型別。每一個資料列中欄位的次序是固定的。很重要且必須記得的是,SQL 並不保證資料列在資料表中的次序(雖然他們可以在顯示的時候被明確表現)。
一個資料庫中集合了許多資料表,而很多的資料庫則被一個 PostgreSQL 服務所管理,形成一個資料庫叢集。
在這一章之中,提供了一個如何使用 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 之中。
你可以創建一個新的資料表,為它取一個名字,並且宣告所有的欄位名稱與其資料型別:
你可以把上述內容在 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專屬資料型別的範例。
最後,應該被點出來的是,如果你不再需要一個表格,或者想要重新以別的方式創建它,那麼你可以以下列的指令來移除它:
到目前為止,我們的一個查詢都只涉及到一個資料表。其實可以在同一個查詢中,同時查詢多個資料表,或者在同一個資料表之中同時處理多個資料列的資料。在一個查詢之中,涉及到同一個或多個不同的資料表中的資料,稱作為交叉查詢(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,以在交叉查詢中區分左側及右側。你也可以在其他查詢中使用這個技巧,以節省輸入的複雜度,例如:
你將會在後續內容中,不斷練習到這樣的使用方式。
要從資料表(table)中取出資料,稱作資料表的查詢。要進行這個行為,你需要 SQL 中的 SELECT 指令。這個指令由幾個部份所組成,回傳列表(select list,想要回傳的欄位)、資料表列表(資料來源的資料表)、選擇性的條件定義(指定一些限制條件)。舉個例子來說,要取得資料表 weather 中所有的資料的話,請輸入:
這裡的星號 * 表示「所有欄位」。下列的指令會回傳相同的結果。
其輸出結果將會如下所示:
你可以在回傳列表中撰寫一些運算表示式,而不只是簡單的欄位引用。舉例來說,你可以輸入:
這應該會產生這樣的結果:
注意,「AS」被用來重新命名輸出的欄位。(選用)
查詢語句可以加上「WHERE」來設定限制條件,以指定哪些列才需要被回傳。WHERE 的內容是一個布林(truth value)表示式,而只有在其運算值為真(true)時,該列才會被回傳。一般的布林運算子(AND, OR, NOT)都是被允許出現在表示式中的。舉例來說,下列的指令將會回傳 San Francisco 在雨天的天氣數值:
結果:
你可以將結果進行排序:
在這個例子之中,其次序並沒有完全地被指定,所以你可能會得到 San Francisco 的列以另一種次序呈現。而你如果以下列指令查詢的話,那你就會得到如上但固定的結果:
你可以在查詢時去除重覆的列:
再一次,其結果的次序可能每次都不同,你可以同時使用 DISTINCT 及 ORDER BY 來確保能得到一致性的查詢結果:
如同其他的關連式資料庫產品,PostgreSQL 也支援彙總查詢的功能。彙總查詢指的是能夠把多個資料列的資料經過計算,產生單一結果的功能。舉例來說, count、sum、avg(平均值)、max(最大值)、min(最小值)都是彙總查詢的函式。
這裡的例子,我們可以得到所有低溫中的最大值:
如果我們想要知道,這個數值是發生在哪一個城市?也許可以試試:
不過,這行不通,因為 max 不能使用在 WHERE 條件式當中。(會有這樣的限制,是因為 WHERE 條件式目的是要判斷有哪些資料列的資料應該被彙總計算,所以很明顯地,這件事必須要在彙整計算前發生,這就產生了矛盾。)所以,像本例的查詢一般會使用子查詢(subquery)來取得適當的結果:
這樣就對了,因為子查詢是一個獨立的查詢,它可以獨立進行彙總查詢,有別於括號以外的查詢語句。
彙總查詢和 GROUP BY 一起使用會很方便的。舉例來說,我們可以得到每個城市所觀測到的最高氣溫:
這個查詢對每個城市都輸出一列的結果。每一個彙總的結果,將整個資料表,以關連到的城市進行計算。 而我們可以進一步過濾資料內容,使用 HAVING:
如果限制所有 temp_lo 的數值必須要小於 40 (WHERE temp_lo < 40)的話,也可能得到相同的結果。 最後,如果我們只關心以"S"開頭的城市的話,可以這樣做:
這裡很重要的是,瞭解 SQL 中 WHERE 和 HAVING 之間的行為。其根本上的差異是:WHERE 會在合併和彙總計算之前進行選擇資料的動作(也就是它控制著,哪些資料需要被彙總計算);而 HAVING 是在合併及彙整計算之後,才進行過濾資料的動作。所以,在 WHERE 條件式當中,絕不可以使用彙整運算式;另一方面,HAVING 條件式總是使用彙整運算式。(嚴格來說,你也可以不在 HAVING 條件式中使用彙整運算式,但很少人這樣使用,通常就會改寫到 WHERE 條式件當中,那會更有效率。)
在先前的例子當中,我們可以把城市名稱的限制放在 WHERE 條件式之中,因為它不需要彙總。這將會比放在 HAVING 條件式中更有效率,因為這樣可以避免合併及彙整運算整個表格,不用浪費時間在本來就會被過濾掉的資料上。
LIKE 運算子進行特徵比對運算,這將會在 中進一步說明。
INSERT 指令被用來將資料以資料列(row)的形式,新增至資料表(table)之中:
注意,所有的資料型別都有明確的輸入格式。只要不是簡單的數值內容,都必須要以單引號(')括住,如同在本例中的形式。日期時間型別(date type)的資料內容就比較有彈性,但在這個導覽之中,我們仍然使用較固定的格式來表現。
地理資訊型別(point type)需要有座標組作為輸入,如下所示:
到目前為止,語法的使用需要你依照欄位宣告的次序擺放,而另一種語法可以允許你明確地指定資料相對應的欄位:
你可以將欄位以不同的次序擺放,甚或略去某些欄位,例如,precipitation 欄位(prcp)內容未知:
許多開發者會認為,在撰寫習慣上,明確指定欄位是比較好的方式。
請執行下列的指令,你將會擁有後續章節所需要的範例資料。
你可能需要使用 COPY 這個指令從文字檔案來載入大量的資料。這個指令會比 INSERT 要快上許多,因為 COPY 指令的設計就是為了大量資料輸入而產生的。它少了一些彈性,但提供了效率上的最佳表現。使用範例如下所示:
資料來源的檔案必須存在於後端的伺服器之中,並且可被 PostgreSQL 使用者(postgres)所存取,注意不是用戶端的主機,因為後端伺服器的服務需要直接讀取該檔案。你可以取得更多詳細說明,在 COPY 指令的說明頁面。
你可以使用 UPDATE 指令以列為單位來更新資料。假設你發現氣溫的數值測量在 11 月 28 日之後都多了 2 度。你可以以下列語法來修正這些資料:
查看一下這些更新後的資料:
讓我們回到 2.6 節的查詢範例。假設關連天氣資訊和城市位置的結果,是你的應用中特別常用的,但你並不想要每次都要輸入一長串的查詢語句。那麼,你可以為這個查詢語句建立一個「檢視表(View)」,你可以取一個名字,當你需要使用的時候,你可以把它當作一個資料表來使用:
妥善地使用檢視表,對於良好的 SQL 資料庫設計而言,是很關鍵的部份。檢視表允許你可封裝你的資料表結構與細節,當你的應用系統在逐步發展成熟的過程中,扮演一致性的資料介面。
檢視表可以用在大多數資料表可以使用的地方。而用檢視表來封裝其他檢視表的情況,也不少見。
交易(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 是唯一能夠控制交易區塊執行流程的方式,當系統產生錯誤時,可以縮小回復的範圍,而不是只能全部回復再執行。
要把某些資料列從資料表中移除,就使用 DELETE 這個指令。假設你對於 Hayward 這個城市的天氣不再感興趣了,那麼你可以執行下列指令,來刪除資料表中的這些資料:
所有關於 Hayward 的資料都被刪除了。
這個指令有一個應該要特別注意的情況:
沒有任何限制的條件,DELETE 將會刪去所有該資料表中的資料,使成為空的資料表。資料庫系統並不會在這個動作執行前和你確認!
回想一下在第 2 章中的表格 weather 及 cities,思考下列問題: 你想要保證沒有另一個人可以新增在 cities 中沒有的城市資料到 weather 中。這就是所謂資料關連性的管理。在簡單的資料庫系統當中,可能會這樣實作:先檢查 cities 中是否已有對應的資料,然後再決定資料表 weather 中新增或拒絕新的天氣資料。這個辦法還有很多問題,而且很不方便,所以 PostgreSQL 可以幫助你解決這個需求。
新的資料表宣告如下所示:
現在嘗試新增一筆不合理的資料:
外部索引鍵或簡稱外部鍵(foreign key)的行為可以讓你的應用程式變得容易調整。我們在這個導覽中不會再深入這個簡單的例子了,但你可以在第 5 章取得進一步的資訊。正確地使用外部索引鍵,可以改善資料庫應用程式的品質,所以強烈建議一定要好好學習它。
繼承是一個物件導向資料庫的概念,它開啓了資料庫設計的更多可能性。
讓我們創建兩個資料表: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 節的說明。
窗函數(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。舉個例子:
更多窗函數的細節可以參閱 、、、及 的說明頁。
PostgreSQL 還有許多這份導覽中未能介紹到的功能,這裡主要是針對新鮮的 SQL 使用者所準備的內容。 這些功能將會在後續的章節進行更詳細的討論。
如果你覺得你需要更多介紹的資訊,可以到 PostgreSQL 的官方網站取得更多訊息。
在這個部份介紹如何在 PostgreSQL 中使用 SQL 語言。首先,我們從一般性的 SQL 語法開始說明,然後解釋如何建立結構來保存資料,如何充實資料庫,以及如何查詢資料的方法。中段的部份列出 SQL 指令中的資料型別與函數。最後剩餘的部份,將會針對一些調教資料庫的重要議題進行說明。
這個部份的內容設計讓初學者可以循序漸進地完整瞭解該主題,而不需要反覆前後查閱。各章的內容設計上都是獨立的,所以進階的使用者可以分別閱讀他們需要的部份。在這個部份的內容,針對於主題式的單元描述。需要瞭解詳情的讀者,請參閱第 6 部份中,個別指令的說明頁面。
在這個部份裡的讀者,應該要知道如何連線到一個 PostgreSQL 資料庫,並且執行 SQL 指令。如果不熟悉這些操作的讀者,建議先閱讀第 1 部份的內容。SQL 指令一般是使用終端工具 psql,但其他具有類似功能的程式也可以使用。
這章中說明 SQL 的使用語法。從這裡建立後續章節所需的理解基礎,然後進一步瞭解 SQL 如何使用去定義及修改資料。
我們也建議已經熟悉 SQL 語法的使用者,仔細地閱讀本章,因為這裡包含了一些有別於其他 SQL 資料庫或專屬於 PostgreSQL 的規則和觀念。
PostgreSQL 允許函數呼叫的時候,使用編號或名稱記號。名稱記號特別好用在於有很多參數的時候,因為它能讓參數與實際的引數有更明確的關連,也更有信賴感。使用編號記號的話,函數呼叫就會依其宣告時的參數次序給予編號;而使用名稱記號的話,參數就會依宣告時的名稱配對,不需要次序對應。
不論哪一種記號方式,如果在宣告時有設定預設值的話,那就不一定要在呼叫時設定其值。不過這點對名稱記號特別好用,因為任何參數的組合都可以省略,而編號記號時就只有從最右邊的參數開始省略。
PostgreSQL 也支援混合式的記號方式,也就是同時使用編號,也使用名稱。在這個例子中,編號的參數會先使用,然後名稱的參數在其之後使用。
接下來的例子,將會描繪所有三種記號方式,都使用下列的函數定義:
函數 concat_lower_or_upper 有兩個必要的參數,a 與 b。然後有一個參數是選擇性的,uppercase 的預設值是 false。參數 a 和 b 的文字會被連結起來,然後依 uppercase 的設定,強制轉換為大寫或小寫字母。這個函數定義的其他部份在這裡並不重要(詳情請參閱)。
編號記號是 PostgreSQL 傳統的參數呼叫方式,如下所示:
所有的參數會依序指定。結果是全大寫,因為 uppercase 設定為 true。另一個例子如下:
這裡的 uppercase 省略了,所以會使用預設值 false,結果就以小寫字母輸出。在編號的記號方式時,參數的省略是由右至左,只有具有預設值的部份才能省略。
使用名稱作為參數記號方式的話,每一個參數名使用「=>」來指定其所代表的表示式,如下所示:
In named notation, each argument's name is specified using=>
to separate it from the argument expression. For example:
再一次省略 uppercase,所以它自動設為 false。使用名稱記號的一項好處就是參數不用固定次數,如下例所示:
有一種比較舊的語法是使用「:=」,因為相容性而保留下來:
混用記號指的就是混合使用編號及名稱來設定參數。然而,如前所述,名稱參數不能先於編號參數。例如:
在上面的查詢中,a 和 b 兩個參數以編號指定,而 uppercase 就以名稱指定。在本例子,只有增加一點點內容而已。使用比較複雜的函數時,會有許多參數設定了預設值,以名稱或混合的方式來設定參數,可以節省許多撰寫的程式碼,也可以減少出錯的可能性。
名稱記號和混用記號目前不能用於彙總函數的呼叫(但如果是用於窗函數是就可以)。
這一章涵蓋了如何建立資料庫結構。在關連式資料庫中,原始資料儲存在表格之中,所以在這一章裡,主要說明表格如何建立及調整,以及有什麼樣的功能可以操控所存放的資料。再來我們會討論表格如何以結構來管理,以及權限的設定。最後,我們會簡短地看一下其他的功能如何影響資料儲存,像是繼承、表格分割、view、函數、還有觸發函數。
SQL 語法包含一連串的命令,命令是由一系列的指示記號所組合而成,以分號結尾。最後如果是串流輸入,也會結束一個命令。指示的合法性是由特別的命令語法所定義的。
指示記號可能是關鍵字、識別項、引號識別項、文字、或一個特別的字元符號。指示一般來說是以空白分隔(空白符號、定位符號、換行符號),但如果不會混淆的話,也不一定需要。(一般只出現在特殊字元用來調整了其他指示的型別)
舉個例子,下面就是一個合法(符合語法)的 SQL 輸入:
這個序列包含了 3 個命令,每行一個(然而這不是一定的,同一行可以超過一個命令,而一個命令也可以分解為多行使用)。
順帶一提的是,註解也是 SQL 輸入的一部份,但不屬於任何指示記號,他們等同於空白字元。
SQL 語法並不是很嚴格要求什麼樣的指示記號來識別命令,或是哪些是運算子或參數。通常最前面的指示記號是命令的名稱,以上面的例子來說,我們通常會說是一個「SELECT」、一個「UPDATE」、以及一個「INSERT」命令。但對於 UPDATE 命令而言,有一個 SET 指示記號出現在某個地方是必要的;同樣地,INSERT 也需要有 VALUES 來搭配。精確的語法規則都在第 6 部份中的章節進行說明。
在上面的例子中的 SELECT、UPDATE、或是 VALUES,都是屬於關鍵字的範圍。所謂關鍵字,意即在 SQL 語言中,其具有固定的意義。像指示記號 MY_TABLE 則是屬於識別項。它識別表格的名稱,欄位名稱,或是其他的資料庫物件,端看命令如何看待該識別項。然而,有時候它們會被簡稱為「名稱」。關鍵字和識別項的文法結構是相同的,意即不看整個命令的話,是無法辨別到底是識別項還是關鍵字的。完整的關鍵字列表,收錄在附件 C 當中。
SQL 識別項與關鍵字必須以英文字母開頭(a - z,也可以是附加符號和非拉丁字母,中文沒問題)或是底線(_)。剩餘的字元可以是字母、底線、數字(0 - 9)、或錢字號($)。注意錢字號,在標準 SQL 語法中是不允許使用的,所以可能會降低一些應用程式的可攜性。標準 SQL 也沒有定義包含數字或是以底線起迄的關鍵字,所以識別項這樣的形式定義是安全的,不會和標準未來的修訂相衝突。
資料庫系統不能使用長度超過 NAMEDATALEN -1 的識別項;太長的名稱仍然可以在命令中被輸入,但會被截斷。預設上,NAMEDATALEN 的設定是 64,所以最長的識別項名稱長度是 63 位元組。如果這個限制會造成困擾的話,你也可以調整 NAMEDATALEN 的編譯值,它的設定在 src/include/pg_config_manual.h 檔案中。
關鍵字和無引號識別項都是不分大小寫的,所以:
等同於:
有一種寫法很常使用,就是把關鍵字用大寫表示,而識別項名稱使用小寫,例如:
第二種要介紹的識別項是,受限制的識別項,或是引號識別項。它的形式就是以雙引號括住的任何字串。受限制的識別項,就一定是識別項,不會是關鍵字。所以,「"select"」就會被識別為名稱為「select」的表格或欄位,而無引號的 select 就會被視為是關鍵字,也可能會產生解譯錯誤,如果剛好用在可能是表格或欄位名稱的位置上的話。使用引號識別項的例子如下:
引號識別項可以包含任何字元,除了字元碼為 0 的字元以外。(要包含雙引號字元的話,請使用連續兩個雙引號。)這可以用來建立原來不能使用的表格或欄位名稱,甚至是包含空白或"&"。但長度的限制仍然要遵守。
還有一種變形的引號識別項,允許包含跳脫的形式來表現萬國碼(unicode)。這種變形會以「U&」開頭(U大小寫皆可)緊接在前面的雙引號的前面,不能有任何空白在它們之間,例如:U&"foo"。(注意,這可能會和運算子的 & 產生混淆,但可以在運算子的 & 前後都加上空白來避免這個問題。)在雙引號內,萬國碼字元以跳脫的形式表現,也就是以倒斜線再接 4 位數的 16 進位碼,或倒斜線接一個加號再串一組 6 位數的 16 進位碼。例如,識別項 "data" 可以寫成這樣:
下面是稍微不簡明的例子是,俄文的"slon"(大象),以希伯萊文字母表現:
如果希望以不同的跳脫字元來代替倒斜線的話,那麼可以雙引號結束後使用 UESCAPE 子句來指定,舉例來說:
跳脫字元可以是任何的單一字元,除了 16 進位數字的字元、單引號、雙引號、或空白以外。注意指定的跳脫字元是以單引號括住,而不是雙引號。
內容要使用到跳脫字元的話,就重覆輸入 2 次。
萬國碼的跳脫語法,只能使用 UTF8 的編碼。如果有用到其他的編碼的話,只有在 ASCII 範圍(最大為 \007F)可以使用。4 位數及 6 位數的形式,可以組合配對用來指定 UTF-16 中,大於 U+FFFF 的字元,雖然 6 位數的形式單獨就可以解決這個問題(組合配對並不會直接被儲存起來,他們會被編碼成 UTF-8 再儲存。)
把識別項用引號括起來也可以用來保持它的大小寫狀態,沒有括起來的話,都會被轉成小寫字母。舉例來說,對 PostgreSQL 而言,FOO、foo、"foo",三者都是一樣的,但 "Foo" 和 "FOO" 就彼此及前面三者都視為不同。(在 PostgreSQL 中,把未引號括起的名稱轉成小寫,並不是 SQL 的標準。SQL 標準反而是都轉成大寫。所以在 SQL 標準中,foo 應該是等同於 "FOO" 而不同於 "foo"。如果你要增加語法的可攜性的話,建議最好都使用引號括起特別的名稱,或者都不要使用引號。)
PostgreSQL 中有三種隱含型別的常數:字串、位元字串、和數值。常數也可以強制型別,有助於更精確的表達,也可以讓系統處理更有效率。接下來就開始進行相關的說明。
在 SQL 中,所謂的字串常數,指的是用單引號括住的任意字元串列,例如:'This is a string'。如果在字串常數內需要有單引號的話就使用連續兩個單引號,例如:'Dianne''s horse'。注意這不是雙引號,是兩個單引號。
兩個字串常數如果只用空白及至少一個換行符號所分隔的話,那個它們會被連在一起,和寫成一個字串是一樣的。舉例來說:
等同於:
但如果是這樣:
語法上就不正確了。(這是來自於 SQL 奇怪的常規,PostgreSQL 單純只是遵循。)
PostgreSQL 也支援跳脫字串常數,這些是 SQL 標準的延伸。跳脫字串常數使用的是字母 E (大小寫皆可),緊接著單引號所組成,例如:E'foo'。(如果字串有超過一行的話,也只要在第一個單引號前有 E 就可以了。)在跳脫字串當中,使用倒斜線開頭,就可以使用 C 語言式的倒斜線跳脫字串,通常是一個倒斜線再接一個字元,對應到一個特殊位元組的值,如 Table 4.1 所示。
Table 4.1. 倒斜線跳腳字串(Backslash Escape Sequence)
倒斜線跳腳字串
字元意義
\b
backspace(倒退)
\f
form feed(換頁)
\n
newline(換行)
\r
carriage return(回到行首)
\t
tab(定位符號)
\o
,\oo
,\ooo
(o
= 0 - 7)
octal byte value(8 進位值)
\xh
,\xhh
(h
= 0 - 9, A - F)
hexadecimal byte value(16 進位值)
\uxxxx
,\Uxxxxxxxx
(x
= 0 - 9, A - F)
16 or 32-bit hexadecimal Unicode character value(16 位元或 32 位元的 16 進位萬國碼字元值)
任何其他接在倒斜線後面的字元都僅以原樣呈現。而如果要包含一個倒斜線的話,就使用連續兩個倒斜線輸入。同樣地,要包含一個單引號的話,可以使用跳脫字串 \' 輸入,也可以用一般連續兩個單引號的方式輸入。
你需要確保你所使用的 8 進位或 16 進位創建的位元組序列,都是屬於資料庫中合法的字元集。當資料庫編輯是 UTF-8 時,就應該使用萬國碼跳脫寫法,或其他萬國碼的輸入方式,如前 4.1.2.3 中所述。(所謂其他的方式可能是自行組合每一個位元組,但這樣會是相當麻煩的事。)
萬國碼跳脫語法只有在 UTF8 的編碼下才完整支援。當有其他的字元編碼被使用時,就只能使用 ASCII 的範圍(最大值為 \u007F)中的值。4 位數及 6 位數的型式可以用來配對指定 UTF-16 超過 U+FFFF 的字元,即使 6 位數的型式就足以解決這個問題。(當使用配對語法,且字元編碼為 UTF8 時,他們會先被合併成單一字元,然後再編碼成 UTF-8。)
如果設定檔參數 standard_conforming_string 設定為 off,PostgreSQL 不論在一般字串還是跳脫字串常數,都會把倒斜線識別為跳脫符號。然而,在 PostgreSQL 9.1 之前,這個參數的預設值為 on,表示只在跳脫字串常數裡,才把倒斜線視為跳脫符號。這樣的模式是更與標準相容的,但可能會破壞默認舊有設定的應用程式,也就是總是把倒斜線視為跳脫符號。在這樣的背景之下,你可以把這個參數設為 off,但更好的是,修改程式不再使用倒斜線跳脫符號。如果你需要使用倒斜線跳脫符號來表示一個特殊字元,請使用 E 開頭的字串常數。
有關 standard_conforming_string,順帶一提的是,還有 escape_string_warning 和 backslash_quote 兩個參數,也提供調整倒斜線在字串常數中的使用。
字元代碼 0 的字元不能使用在字串常數當中。
PostgreSQL 也支援其他跳脫字串的語法,可以用來直接輸入任意的萬國碼字元。萬國碼跳脫字串常數是以 U& (U& 或 u& 皆可)開頭,然後緊接著單引號括住的字串,記得中間不能有任何空白,例如:U&'foo'。(注意這可能會混淆到 & 的使用,最好在其他使用 & 作為運算子的指令中,在 & 前後 加上空白字元,以避免這個問題。)在括住的內容裡,萬國碼字元可以使用跳脫字元來指定,也就是使用倒斜線再接一組 4 位數的 16 進位值,或者以倒斜線加上加號再接一組 6 位數的 16 進位值。舉個例子,字串 'data' 也可以寫成:
下面是稍微不簡明的例子是,俄文的"slon"(大象),以希伯萊文字母表現:
如果希望以不同的跳脫字元來代替倒斜線的話,那麼可以雙引號結束後使用 UESCAPE 子句來指定,舉例來說:
跳脫字元可以是任何的單一字元,除了 16 進位數字的字元、單引號、雙引號、或空白以外。
萬國碼跳脫語法只有在 UTF8 的編碼下才完整支援。當有其他的字元編碼被使用時,就只能使用 ASCII 的範圍(最大值為 \u007F)中的值。4 位數及 6 位數的型式可以用來配對指定 UTF-16 超過 U+FFFF 的字元,即使 6 位數的型式就足以解決這個問題。(當使用配對語法,且字元編碼為 UTF8 時,他們會先被合併成單一字元,然後再編碼成 UTF-8。)
然而,萬國碼的跳脫字串語法,只有在參數 standard_conforming_strings 設定為 on 時有效。這是因為這個語法可能會造成 SQL 指令在編譯時的困擾,造成 SQL 隱碼攻擊(SQL injection) 或其他安全性的問題。如果這個參數設定為 off,那麼這個語法就會被禁止,並且產生錯誤訊息。
內容要使用到跳脫字元的話,就重覆輸入 2 次。
標準的語法用於字串常數的設定很方便的,但如果字串裡有很多單引號或倒斜線,可讀性就很低了,因為它們都必須再連續多一個符號輸入。像這樣的例子,要改善可讀性的話,PostgreSQL 提供了另一個方式,稱作「錢字引號」(dollar quoting),來描述字串常數。錢字引號字串常數包含一個錢字號($),可省略或多個字元所組成的「標籤」,另一個錢字號,組成字川的任何序列文字,再一個錢字號,與起始的錢字引號同樣的標籤,再一個錢字號。舉例來說,這裡有兩個不同使用錢字引號的方式,但都是「Dianne's horse」
注意在錢字引號字串中,單引號的使用就不需要跳脫處理了。實際上,在錢字引號字串中,沒有字元需要跳脫處理:字串內容就原樣輸出。倒斜錢並不特別,就算是錢字號也是,除非它們是引號標籤配對的一部份。
巢狀錢字字串常數是可以的,只要在不同層選擇不同的標籤就好。最常見的用途就是撰寫函數定義。舉例如下:
這裡,「$q$[\t\r\n\v\]$q$」以錢字引號字串輸出就是「[\t\r\n\v\]」,作為 PostgreSQL 的函數內容。但這個字串並不會和外層的 $function$ 配對。對外層的字串而言,它只是被包裏的一部份字元而已。
以錢字符作為標籤(如果有的話)的引號字串和無引號的識別項,遵循相同的規則,除了它無法包含錢字符號以外。標籤是區分大小寫的,所以 $tag$String content$tag$ 是正確的,而 $TAG$String content$tag$ 是不合法的。
錢字引號字串緊接著關鍵字或識別項的話,就必須以空白分隔;否則錢字號的終止符可能會被當作前面識別項的一部份。
錢字引號並不是標準 SQL 的用法,但當撰寫一些複雜字串的時候,會比標準語法更為便利。當字串常數內嵌於另一個常數時,也是很好用的情境,像自訂函數時就時常用到。使用單引號的語法時,前面例子中的每一個倒斜線,需要使用 4 個倒斜線才能表示(原來字串常數時需要雙倒斜線,然後在執行階段時也需要雙倒斜線,一共就是 4 倍)。
位元字串常數看起來就像是一般的字串常數,只是將 B(大小寫皆可)放在引號的前面(不能有空白),例如:B'1001'。而在位元字串當中,只能有 0 或 1 的存在。
另一方面,位元字串常數也可以表示一個 16 進位的值,使用的先導字為 X(大小寫皆可),例如:X'1FF'。這個撰寫方式與使用前段方式,以 4 位數 2 進位表示每一個 16 進位位數,是相同的結果。
這兩種位元字串常數的表達方式,都可以在字串中換行,如同一般的字串常數。錢字引號表示方式不能使用在位元字串常數上。
數值常數可以以下列語法輸入:
這裡的 digits 指的是 0 到 9 的多位數十進位數字。如果有小數點的話,在小數點之前或之後要有數字。在指數標記 e 之前,也必須要有數字。字串中間不能再有其他字元或空白出現。注意,最前面正負號並不是數值常數的一部份,它是屬於運算子的概念。
下面是一些合法數值常數的例子:
42 3.5 4. .001 5e2 1.925e-3
數值常數如果沒有小數點或指數標記的話,預設就會被假定為整數,32 位元以內的為整數型別(interger),否則就會以 64 位元的大整數型別(bigint)來處理。其次就會宣告為數值型別(numeric)。只要包含小數點或指數標記的數值,都會預設使用數值型別。
預設數值常數的資料型別只是整個型別解析演算法的開端而已。在多數的情況下,各種常數會自動被轉換為最貼近內容的適當型別。不過,如果需要的話,你可以強制指定一個資料型別給該常數。舉例來說,你可以強制以實數型別(real 或 float4)來處理該數值:
實際上,在型別轉換上還有一些特殊的情況,留待後續探討。
任意型別的常數,可以使用下列的語法來表示:
字串常數的內容會由型別轉換的程序 type 來處理,其結果就會得到該常數的專屬型別。明定型別轉換可以被省略,如果不會混淆的話(舉例來說,要輸入給特定的表格欄位的話,因為已有型別宣告,就不會混淆),那麼就會自動給定型別。
字串常數可以使用一般 SQL 標準寫法,或是錢字引號寫法。
還可以使用函數式的語法來撰寫:
但並非所有的型別都可以使用這個方式,請參閱 4.2.9 節取得詳細說明。
「::」、CAST()、及函數式語法,也可以用來指定任何表示式在執行中的型別轉換,如同 4.2.9 節中所描述的。要避免語法上的混淆,「type 'string'」這個語法,只能用在指定簡單的文字常數,另一個限制是,不能用於陣列型別。陣列常數的型別指定,請使用 :: 或 CAST() 的語法。
一個運算子最長可以是 NAMEDATALEN - 1(預設為 63 個字元),除了以下的字元之外:
* / <> = ~ ! @ # % ^ & | ` ?
還有一些運算子的限制:
「--」和「/*」都不能出現在運算子裡,因為它們表示註解的開始。
多字元的運算子不能以 + 或 - 結尾,除非名稱裡也包含了下列字元:
~ ! @ # % ^ & | ` ?
舉個例子,@- 可以是合法的運算子,但 *- 就不合法。這個限制是讓 PostgreSQL 解譯 SQL 語法時,可以不需要在不同的標記間使用空白分隔。
當使用非 SQL 標準的運算子時,你通常需要在相隣的運算子間使用空白以免混淆。舉例來說,如果你已經定義了一個左側單元運算子 @,你就不能使用 X*@Y,必須寫成 X* @Y,以確保 PostgreSQL 可以識別為兩個運算子,而不是一個。
有一些字元並不是字母型態,而具有特殊意義,但並非運算子。詳細的說明請參閱相對應的語法說明。本節僅簡要描述這些特殊字元的使用情境。
錢字號($)其後接著數字的話,用來表示函數宣告或預備指令的參數編號。其他的用法還有識別項的一部份,或是錢字引號常數。
小括號(( ))一般用來強調表示式並且優先運算。還有某些情況用於表示某些 SQL 指令的部份的必要性。
中括號([ ])用於組成陣列的各個元素。詳情請參閱 8.15 節有關於陣列的內容。
逗號(,)用於一般語法上的結構需要,來分隔列表中的單元。
分號(;)表示 SQL 指令的結束。它不能出現在指令中的其他位置,除非是在字串常數當中,或是引號識別項。
冒號(:)用在取得陣列的小項。(參閱 8.15 節)在某些 SQL 分支(篏入式 SQL 之類的)中,冒號用來前置變數名稱。
米字號(*)用來表示表格中所有的欄位,或複合性的內容。它也可以用於函數宣告時,不限制固定數量的參數。
頓號(.)用在數值常數之中,也用於區分結構、表格、及欄位名稱。
註解是以連續兩個破折號開頭,一直到行結尾的字串。例如:
另外,C 語言的註解語法也可以使用:
這樣的註解,以「/*」開頭,一直持續到對應的「*/」出現才結束。這樣區塊式的註解可以巢狀使用,所以你可以一次註解掉一堆包含註解的指令。這點是 SQL 的標準,和 C 語言的使用不太一樣的地方。
註解會在進一步的語法分析前被消去,也可以方便地以空白字元替代。
Table 4.2 列出在 PostgreSQL 中,運算子的運算優先權及運算次序。大多數的運算子都是相同的運算優先權,並且是左側運算。這些優先權與次序是撰寫在解譯器的程式當中的。
你有時候需要加上括號,當遇到二元運算子與一元運算子一起出現時。舉個例子:
會被解譯為:
因為解譯器並不知道實際的情況,所以它可能會搞錯。「!」是一個後置運算子,並非中置運算子。在這個例子中,要以想要的方式進行運算的話,你必須要改寫為:
這是為了延展性而需要付出的代價。
Table 4.2. Operator Precedence (highest to lowest)
Operator/Element
Associativity
Description
.
left
table/column name separator
::
left
PostgreSQL-style typecast
[]
left
array element selection
+-
right
unary plus, unary minus
^
left
exponentiation
*/%
left
multiplication, division, modulo
+-
left
addition, subtraction
(any other operator)
left
all other native and user-defined operators
BETWEEN / IN / LIKE / ILIKE / SIMILAR
range containment, set membership, string matching
<>=<=>=<>
comparison operators
IS / ISNULL/ NOTNULL
IS TRUE
,IS FALSE
,IS NULL
,IS DISTINCT FROM
, etc
NOT
right
logical negation
AND
left
logical conjunction
OR
left
logical disjunction
注意,使用與內建運算子同名的自訂運算子,運算優先權的規則也會以原規則適用,如同上面的樣子。舉例來說,如果你定義了一個「+」的運算子,用於自訂的資料型態,那麼它就會和內建的「+」擁有相同的運算優先權,而與你的運算內容無關。
當某個結構操作的運算子用於 OPERATOR 語法之中時,如下所示:
OPERATOR 建構式被用來為任何運算子,取得如 Table 4.2 中所示的預設運算優先權。不論在 OPERATOR() 中指定什麼運算子,都會回傳 true 的結果。
PostgreSQL 在 9.5 之前的運算優先權有一些不同。比較特別的是,比較運算子「<= >= <>」是和一般其他運算子是相同等級的;「IS」先前的優先權較高;而「NOT BETWEEN」和相關的建構式行為不一致,使得在某些情況下,「NOT」和「BETWEEN」的優先權不同。這些規則的改變是為了與 SQL 標準有更好的相容性,減少因為等價轉換的不一致處理所造成的困擾。大多數的情況,這些改變並不需要使用習慣的改變,也不會產生沒有運算子的錯誤,而且都可以透過增加括號來解決。然而,有一些極端的情況可能會在沒有錯誤的情況改變其運算行為。如果你很關心這些變化,很擔心這些無聲的錯誤,你可以打開參數 operator_precedence_warning 來測試你的程式,然後檢查是否有警告被記錄下來。
欄位可以指定一個預設值。當新的列被插入,某些欄位卻沒有指定其值時,這些欄位將會被填入相對應的預設值。資料處理的過程中,當有欄位的值不確定時,也會被設定為其預設值。(關於資料處理的詳細內容,請參閱第 6 章。)
如果預設值並沒有明確被指定時,預設值就會是 null。一般來說空值是可接受的情況,因為空值可以表示「未知的資料」的意義。
在表格定義時,預設值接在資料型別後宣告,如下所示:
預設值也可以是運算表示式,會在資料插入的同時進行運算(不是在表格建立時)。常見的例子是 timestamp 欄位,會設定一個 CURRENT_TIMESTAMP 的預設值,使其在資料插入時設定為當下的時間。另一個例子是產生「序列數」,這在 PostgreSQL 中,通常以下列語法來表現:
這裡的 nextval() 函數會從序列物件取得下一個數字(參閱 9.16 節)。這個例子也常簡化為:
有關 SERIAL 的簡寫方式,將在 8.1.4 節中說明。
Generated column (自動欄位)是特殊的欄位,它的內容由其他欄位的內容計算得出。相對於資料表來說,就是欄位形態的 View。Generated column 有兩種:stored 和 virtual。 Stored 的自動欄位在寫入(插入或更新)時進行計算,會像正常欄位一樣佔用儲存空間。Virtual 的自動欄位則不佔用任何儲存空間,而是在讀取時會對其進行計算。因此,虛擬的自動欄位類似於檢視表(view),而儲存的自動欄位則類似於具體化檢視表(materialized view)(但會自動更新)。 PostgreSQL 目前僅實作了儲存的自動欄位。
使用 GENERATED ALWAYS AS
語法來產生自動欄位,舉例來說:
務必要加上 STORED
關鍵字以寫入自動欄位. 更多細節請參閱 CREATE TABLE
使用 INSERT
或 UPDATE
指令時,不能直接指定內容至自動欄位,但可以使用 DEFAULT
關鍵字來設定預設值。
針對「包含預設值的欄位」及 「自動欄位」進行比較:
當資料列第一次被寫入時,如果該欄位沒有提供任何值,將採用預設值寫入; 而自動欄位則是在資料列被更新時,根據其他欄位來產生對應的值,該值無法被覆寫。
「包含預設值的欄位」通常不會參考到表格的其他欄位; 而「自動欄位」通常都會參考到其他欄位。
「含預設值的欄位」在設定預設值時可以使用易變函數 (volatile function),舉例來說: random()
或者是取得當前時間的函式; 而「自動欄位」則不允許使用。
「自動欄位」和「包含自動欄位的表格」有一些限制:
自動欄位的表示式只能使用 immutable 函數,不能使用子查詢或以任何方式引用同筆資料以外的任何內容。
自動欄位的表示式不能引用另一個自動欄位。
自動欄位的表示式不能引用系統欄位(tableoid 除外)。
自動欄位不能有欄位預設值或識別定義。
自動欄位不能是分割區主鍵的一部分。
外部資料表可以具有自動欄位。有關詳細資訊,請參閱 CREATE FOREIGN TABLE。
其他注意事項適用於自動欄位的使用。
自動欄位與其一般欄位分開維護存取權限。因此,可以對其進行安排,以便設定可以從自動欄位中讀取,但不能從一般欄位中讀取的特定角色。
從概念上講,在執行事件觸發器之前,會先更新自動欄位。因此,在 BEFORE 觸發器中對基本欄位所做的更新將先反映在自動欄位中。但是相反地,不允許在觸發器之前讀取自動欄位。
資料型別是一種限制資料如何被儲存在表格中的方式。然而,對許多應用來說,這樣的限制還是不夠細膩。舉個例子,一個欄位包含了產品價格,當然它必須只能是正整數,但並沒有標準的資料型別可以只限制在正整數。另一個需求是,你可能想要限制的條件是依據其他的資料而定。舉例來說,在表格中的產品資訊,每一個產品編號都不能重覆。
所以,SQL 允許你在表格和欄位上定義額外的限制條件,它幫助你對資料有更多的控制能力。當某個使用者輸入資料時,違反了限制條件,錯誤訊息就會產生。這些限制條件也會限制預設值的設定。
使用 CHECK 是最普遍的限制條件製定方式,它可以允許你指定某個欄位必須符合某個布林條件式的判斷。舉個例子,要滿足產品價格是正數的話,你可以使用這樣的語法:
如同你所看到的,限制條件會接在資料型別之後,就像是預設值的設定一樣。預設值和限制條件的設定,在語法撰寫上沒有先後次序。檢查限制條件使用關鍵字 CHECK,然後接著是一組以括號括起來的條件式。其條件式應該要包含被限制的欄位,不然就沒有任何意義。
你也可以讓該限制條件擁有另一個名稱,這樣的好處是,當錯誤訊息發生時,你可以明確得到是哪一個限制被違反了:
如上,給予這個限制條件一個名稱,使用關鍵字 CONSTRAINT,緊接著一個限制條件的定義。(如果你沒有自行命名,系統也會自動取一個名字)
一個限制條件可以參考多個欄位。例如你設定了標準價格和優惠價格,而你需要確保優惠價格一定是比標準價格要便宜的話:
前兩個限制條件和前述很類似,而第三個是新的語法。它並不是只參考某個特定的欄位,而是以逗號分隔列出所有需要遵守的條件。欄位的定義和限制條件的定義,撰寫上沒有規定次序。
我們會說前兩個是欄位的限制,而第三個是表格的限制,因為它是獨立於其他的欄位定義的。欄位限制也可以寫成表格的限制方式,不過反過來通常就不行,因為一個欄位的限制,指的就是只參考到語法上它所接續的欄位而已。(PostgreSQL 並沒有強制這樣做,但如果你的語法與其他資料庫共用的話,最好還是依這樣的語法避免混用。)上面的例子也可以改寫成如此:
或等同於:
都可以照你所喜愛的語法撰寫。
命名表格的限制條件和欄位限制條件的命名是一樣的:
應該要注意的是,檢查限制條件是否成立,端看條件表示式在運算後是真值(true)還是空值(null)。因為當有運算元是空值時,多數的運算結果都是空值,所以可能會有空值產生在想要限制條件的欄位之中。要確保欄位中不會出現空值的話,請參閱下一段的說明。
限制無空值只要以下方的語法設定,就可以限制欄位不得存在空值的輸入:
限制無空值的語法,只能使用在欄位限制上。而限制無空值等效於以 CHECK 建立一個限制條件式為(IS NOT NULL),但在 PostgreSQL 明確使用 NOT NULL 語法的話,處理會更快速。只是它的缺點是你無法給予這樣的限制一個自訂的名稱。
當然,一個欄位可以有一個以上的限制條件。只要一個接著一個即可:
撰寫的次序沒有關係,也不需要去計較限制被檢查的次序。
NOT NULL 有一個相反的語法:NULL。這並非表示欄位裡只能是空值,如果這樣的話就完全沒用處了。其實這是一種簡化,將預設值設定為空值。NULL 語法並不是 SQL 標準的一部份,所以請不要用在可移植式的應用程式裡。(這僅是 PostgreSQL 為了相容其他資料庫而增加的功能)然而,有一些使用者喜歡使用它,因為在程序檔的撰寫上,很容易利用這個語法來切換限制條件。舉個例子,你可以先寫下:
然後在需要的時候再適時加入 NOT 關鍵字即可。
在多數資料庫設計原則上,主要欄位都應該被標示為 NOT NULL。
限制唯一性,確保在某個欄位或某一群欄位的資料,是在該表格中獨一無二的。語法如下:
這是欄位限制的語法。而:
則是表格限制的寫法。
如果想要限制一群欄位的唯一性的話,請使用表格限制的語法,欄位名稱以逗號分隔:
這表示這些欄位所包含的內容組合,在整個表格中是具有唯一性的,但任何一個欄位本身並不一定具備唯一性。
你可以命名唯一性的限制條件,語法如下:
加入唯一性的限制條件,將會自動建立一個具唯一性的 B-tree 索引,其包含的欄位就如限制條件中所條列的欄位。這樣唯一性限制的語法並不能只限制某部份列的唯一性,但如果使用「部份索引 (partial index) 」的話就可以做到。
一般來說,唯一性被違反的情況是,所限制的欄位在表格中,有超過一列的資料是相等的。不過,空值並不會被計算在內。這表示說,即使設定了唯一性的限制,在被限制的欄位中,還是有可能會有多個列的資料是空值。這個設計源自 SQL 標準,但聽說有其他的 SQL 資料庫並不是這樣的規則。所以,如果要移植這個語法到其他資料庫的話,要注意這項設計有無差異。
主鍵的意思是,某一個欄位或某一群欄位,在整個表格中,其每一列的組合都是唯一的,且有宣告唯一性的限制條件,並且也包含了非空值的條件(UNIQUE 及 NOT NULL)。所以,下面的兩種語法對資料的意義相同:
主鍵也可以包含多個欄位,語法和宣告唯一限制條件類似:
加入主鍵時,會自動建立一個具唯一性的 B-tree 索引,範圍為 PRIMARY KEY 語法所定義的欄位,並且會強制將這些欄位設定為非空值(NOT NULL)。
一個表格只能有一個主鍵。(你可以使用 UNIQUE 及 NOT NULL 設定多個同樣的限制條件,在功能上幾乎是相同的,但只能有一組條件是由 PRIMARY KEY 所定義。)關連式資料庫的理論指出,每一個表格都必須要有一個主鍵。這個規則在 PostgreSQL 中並不是強制的,但通常建議最好遵循這個理論。
主鍵在用戶端文件式的資料處理上是很有用的。舉個例子,一個圖型化介面讓使用者可以修改資料,那麼可能就需要主鍵來確認每一列的唯一性,而不致於產生混淆。也有一些用途是在資料庫系統的管理上,例如,主鍵會用於外部鍵(Foreign Keys)的處理,使其可以處理表格與表格間的資料對應問題。
外部鍵指的是某個欄位或某一群欄位的內容,必須在另一個表格相對欄位之中,存在相同內容的資料。我們會說這樣的行為是在維護兩個表格之間的關連性。
就使用我們已經使用多次的產品表格吧:
讓我們假設你有一個表格用來儲存這些產品的訂單,我們要確保這些訂單內的產品確實存在。所以我們定義一個外部鍵來關連訂單的表格和產品的表格:
這樣的話,如果 product_no 沒有出現在產品表格的話,就無法建立資料了。
我們會說像這樣的情況是,訂單表格是引用表格(referencing table),而產品表格是參考表格(referenced table)。相對地,欄位也稱為引用欄位(referencing columns)及參考欄位(referenced columns)。
你可以將這個語法簡化為:
因為在參考表格中,不在主鍵欄位組合中的欄位,就是參考欄位。
外部鍵也可以參考一組欄位。一般來說,這樣要寫成表格限制條件形式,如下:
當然,組合外部鍵的欄位數量,彼此之間必須要相等。
你可以給外部鍵一個名稱,使用語法與限制條件相同。
一個表格可以有許多個外部鍵,這用於表格之間多對多的關係。例如你有一些表格記錄了很多產品和訂單,但現在你要讓每一筆訂單也可以訂購多項產品(這在先前的語法並不允許)。你也許可以試試這個表格宣告:
注意到這裡的主鍵和外部鍵是重覆的。
我們知道外部鍵不允許沒有關連到產品的訂單,但如果企圖移除一個有訂單的產品會如何呢?SQL 有幾個選項讓你直覺進行這項操作:
不允許移除被參考到的產品
同時也刪去訂單
其他?
要描繪這些情況,讓我們建立如上需求的多對多關連的結構:當某人要移除一個有訂單的產品(以 order_items 關連)時,我們不允許執行。而如果某人移除了一筆訂單,訂單內的項目也會同步被移除:
引用和同步刪除有兩個常見的作法。用「RESTRICT」防止參考的資料被刪除;「NO ACTION」表示當限制條件被違反時,引用欄位的資料仍會留存,然後回傳錯誤訊息,如果未指定處理方式的話,這會是預設的行為(這兩個語法根本上的不同是「NO ACTION」允許延遲檢查到交易事務的最後,而「RESTRICT」則不會。);「CASCADE」指的是當參考的資料列被刪除時,引用的資料列也會同步被刪除。刪除時還有兩個其他的選項:SET NULL 和 SET DEFAULT,表示引用的資料會被更新為空值或其預設值。注意到,這並不是說你就可以違反限制條件。舉個例來說,如果使用了 SET DEFAULT,但預設值卻違反了外部鍵的限制,這個操作將會失敗。
類似的於 ON DELETE 的情況是 ON UPDATE,也就是在參考欄位的資料內容被更新時的情況。可以設定的動作關鍵字是相同的。在這個情況的 CASCADE 指的就是更新參考欄位的資料內容時,引用欄位的內容也會同步被更新為相同的內容。
一般來說,引用的資料列不需要滿足外部鍵的定義,如果其任一欄位內容為空值的話。而如果「MATCH FULL」加到宣告的語法之中的話,引用的資料列就必須要全部都是空值才不受外部鍵的限制(也就是部份空值的資料列就不受限制)。如果要避免空值使得外部鍵失效的話,就應該宣告相關欄位為 NOT NULL。
外部鍵所參考的欄位必須要是主鍵或是宣告其唯一性限制,這表示參考欄位會有索引存在,這使得檢查關連的過程會是很有效率的。因為在刪除或更新參考資料表時,需要檢查引用資料表的情況,所以在引用表格的欄位建立索引,也是常見的作法。因為這並不是一定需要,而還有許多的選擇在於如何索引,所以宣告外部鍵時並不會自行以引用欄位組合建立索引。
關於更新資料與刪除資料的細節在第 6 章。也可以在 CREATE TABLE 語法說明中,找到更多外部鍵的說明。
除外宣告要確保的是,如果任意兩個資料列在指定的欄位或表示式被比較時,用於特定的運算子,至少有一個比較會回傳假(false)或空值(null)。語法如下:
詳情請參考 CREATE TABLE 中,CONSTRAINT 到 EXCLUDE 的段落。
加入除外宣告時,將會自動建立相對應的索引。
「資料表」(table)在關連式資料庫中的角色很接近在紙上畫一個「資料表」:包含了列與欄。欄的數量與次序是固定的,而每個欄位都有一個名稱。列的數量是變動的—它表示在當下有多少資料被存在資料庫中。SQL 並不保證列在資料表中的次序。當讀取資料表的時候,除非明確要求要排序,不然列與列之間是不存在固定的次序。這些將在第 7 章中進一步說明。進一步來說,SQL 並沒有給每一列一個唯一性的識別,所以在資料表中是有可能存在有完全相同內容的列。這是 SQL 架構下的數學模型結果,通常不是理想的結果。在這章之後,我們會說明如何處理這個問題。
每一個欄位都有一個資料型別。資料型別限制了儲存於該欄位的資料內容,同時也設定了資料儲存的型態,使得該資料可以直接用於計算。舉個例子,一個被宣告為數字型別的欄位,就不能放進任何文字字串,而儲存於此欄位中的資料,可用於數學計算。相反地,一個被宣告為字元字串的欄位,可以儲存任何型能的資料,但就無法用於數學計算了,雖然也有其他操作可以進行字串串接。
PostgreSQL 擁有許多內建的資料型別,可以適應許多應用系統。使用者也可以自訂他們所需的資料型別。大多數內建的資料型別都有顯而易見的名稱與用法,所以我們打算在第 8 章再做詳細的說明。有一些常用的資料型別,像是 interger 用於整數,numeric 用於浮點數,text 用於字串,date 則是日期,time 是時間,而 timestamp 則同時包含日期和時間。
要建立一個資料表,你可以使用 CREATE TABLE 指令。這個指令你至少要指定一個名稱給新的資料表,還有每一個欄位的名稱與資料型別。例如:
這個建立一個叫作 my_first_table 的資料表,它包含了兩個欄位。第一個欄位叫作 first_column,其資料型別為 text;第二個欄位名稱為 second_column,資料型別為 integer。表格與欄位名稱的規則依 4.1.1 節中所介紹的識別字語法,但也有一些例外。注意欄位列表是用逗號分隔,並且包含於括號之中。
當然,前面的例子明顯只是做做樣子而已。一般來說,你會將你的資料表欄位以實際用途來命名,所以我們來看一下更實際的例子:
(numeric 資料型別可以儲存浮點數,用於典型的貨幣計量。)
小技巧當你建立了許多相關的資料表時,建立最好選擇一個用於命名表格及欄位的規則。舉例來說,有一個規則是使用單數或複數名詞來取名表格,兩者都有些人喜歡使用。
一個資料表中有多少欄位是有限制的,依欄位型別而定,上限通常是 250 個到 1600 個之間。不過,宣告到這麼多的欄位是非常罕見,而且應該是有問題的設定。
如果你不再需要某個資料表,你可以移除它。請使用 DROP TABLE 指令,如下所示:
企圖要移除一個不存在的資料表,會產生錯誤。不過,在 SQL 腳本中,在建立資料表前嘗試移除是很常見的,通常會忽略錯誤訊息,所以不論資料表是否已經存在,腳本都能如預期執行。(如果你需要的話,你也可以使用 DROP TABLE IF EXISTS 來避免產生錯誤訊息,但這並不是標準 SQL 語法。)
如果你需要變更資料表的結構的話,請參閱本章的 5.5 節。
到目前為止,你已經可以利用工具建立完整功能的資料表。本章接下來的部份會針對附加的功能介紹,像是確保資料完整性、安全性、或方便性。如果你現在急著要將資料存入你的資料表的話,你可以暫時跳過本章,到第 6 章繼續操作。
每一個表格都有幾個系統欄位,而它們是由資料庫系統預先定義好的,所以使用者在定義欄位名稱時,不能使用這些名字。(這些限制並不是因為它們是保留關鍵字,所以就算用引號括起來也不能使用。)但在一般使用時,你也不需要特別考慮這些欄位,只要瞭解會有這些欄位存在就好。
oid
每一個資料列會有一個 Object ID,不過這個欄位只有在建立表格時,加上 WITH OIDS 語法才能使用。或者也可以藉由參數 default_with_oids 來切換使用。這個欄位的型別是 oid(和欄位名相同)。參閱 8.18 節瞭解詳細資訊。
tableoid
每個表格也有一個 ID 也會記錄在每一個資料列中。這個欄位特別方便在取得表格的繼承結構(參閱 5.9 節),如果沒有這個欄位的話,要去找出資料列的來源就會很麻煩。tableoid 可以參考 pg_class 表格中的 oid 欄位,進一步取得表格的名稱。
xmin
這指的是資料列在插入資料的版本資訊。(每一個資料列的版本,都是一個獨立的資料狀態;每一次資料的更新,都會在邏輯層產生一個新的資料列版本。)
cmin
指令識別碼,會存在於新增資料的交易中。(從 0 開始)
xmax
刪除資料的交易版本資訊,如果是 0 的話,代表讓資料列不是刪除中的資料列版本。這通常是用來指出某個刪除的交易還未被完成,或某個刪除正在被回復。
cmax
指令識別碼,有數字的話表示一個刪除的交易指令,或是 0。
ctid
表示每一個資料列存在於該表格的實體位址。注意到的是,雖然 ctid 可以用來快速找到特定的資料列版本,但 ctid 是會改變的,如果有執行過 VACUUM FULL 的話。所以 ctid 如果要用於固定的資料定位的話,是不應該被考慮的選項。OID 或額外自訂序列數字,更適合用於分別邏輯上的資料列。
OID 是一個 32 位元的數字,以 cluster 為單位配發。在一個大型或長期使用的資料庫中,是有可能出現重覆的情況。所以,假設 OID 是唯一的識別是不正確的觀念,除非你還有搭配其他方法來確保唯一性。如果你需要識別表格中的資料列的話,使用序列數產生器是比較建議的作法。OID 也可以這樣用來得到一些額外的預防性功能:
唯一性的限制應該設定在 OID 欄位上,來確保每一個 OID 可以識別每一個資料列。當有唯一性限制存在的時候,對於已經存在的資料列就不會有重覆的 OID。(當然,這方法只能用於資料筆數在 40 億筆以下的表格。不過實務上的表格多數都少於這個數目,而且太多資料的話,效果也會變得很差。)
OID 在多個表格間就不能假設為是唯一,你應該搭配 tableoid 來識別資料庫層級的唯一性。
當然,在建立表格時必須要加入 WITH OIDS 語法。在 PostgreSQL 8.1 之前,WITHOUT OIDS 是預設值。
交易識別碼也是 32 位元的數字。在一個長期運行的資料庫中,交易識別碼也可能會重覆。只要有適當的管理機制的話,這並不會是什麼嚴重的問題,詳情請參閱第 24 章。然而,長期來說(超過 10 億個交易),假定交易識別碼的唯一性是不明智的作法。
指令識別碼也是 32 位元的數字,其絕對上限是約 40 億個指令在一個交易當中,實務上這個限制並不會是問題。注意到這個限制是 SQL 指令數量的限制,而不是處理資料的限制。只有真正有改變資料庫內容的指令才會有指令識別碼。
當一個資料庫物件被建立時,它會先指定存取權限給擁有者,而擁有者一般來說就是執行建立指令的使用者。對大多數的資料庫物件來說,其預設的狀態就是只有擁有者(或超級使用者)可以對該物件進行所有操作。要讓給其他使用者來操作的話,就必須進行授權的動作。
有很多不同種類的權限:SELECT、INSERT、UPDATE、DELETE、TRUNCATE、REFERENCES、TRIGGER、CREATE、CONNECT、TEMPORARY、EXECUTE、USAGE。這些權限對於不同物件的效果,會因為是哪一種物件而有所差別(表格、函式...等等)。要瞭解完整在 PostgreSQL 中所支援的各種物件權限,請參考 語法頁面。這裡的內容主要說明如何使用。
修改和移除一個資料庫物件,是只有擁有者才具備的權力。
要把一個物件被指派給一個新的擁有者的話,使用該物件的 ALTER 指令,例如:。超級使用者也可以做指派的動作;原來的擁有者如果它仍是該物件的管理群組一員的話,當然也可以;再來就管理群組新的成員。
要進行授權行為的話,請使用 GRANT 指令。舉例來說,如果 joe 是一個使用者,而 accounts 是一個表格,要讓他可以獲得更新表格資料的權力:
使用 ALL 的權限,就代表授權所有可設定的權限。
有一個特別的使用者是 PUBLIC,代表的是系統內的所有使用者。當資料庫內有很多使用者時,可以制定「群組(group)」來簡化管理。這部份詳細的說明請參閱。
要移除權限,請使用 REVOKE 指令:
物件擁有者的特殊權限(例如DROP、GRANT、REVOKE...等)都是和擁有者一起設定,而無法單獨授權。不過,擁有者可以選擇移除自己的權限,例如建立一個唯讀的表格,讓自己和其他人一樣。
回到前面所說的,只有物件的擁有者(或超級使用者)可以變更該物件的權限。然而,也可以使用「with grant option」讓另一個使用者可以代授權給其他使用者。不過如果這個「grant option」被移除時,所有被代授權的使用者都會同時喪失該權限。更詳細的說明請參閱 及 說明頁面。
除了透過 設定 SQL 標準的之外,資料表也可以有資料列層級的安全原則,控制每個使用者在資料查詢或變更時,所能接觸到的資料列。這個功能就稱作資料列安全原則(Row-Level Security)。預設上,資料表並不會有這些安全原則,所以只要使用者能存取該資料表,就表示他能存取所有資料列的內容。
當資料列安全原則在資料表裡被啓動後(使用 ),所有資料表的操作,就必須符合資料列安全原則的設定。(當然,資料表的擁有者並不受限於資料列安全原則。)如果資料表中未設定任何原則,那麼預設就是拒絕存取,意思就是任何資料列都不能被看見或修改。但如果是整個資料表的操作行為,像是 TRUNCATE 或 REFERENCES,就不會受到影響。
資料列安全原則可以被設定在命令,使用者角色,或兩者兼具。安全原則也可以使用 ALL 的修飾字,或具體指出是 SELECT、INSERT、UPDATE、或 DELETE。多重角色可以共用一個安全原則,一般使用者或承繼的角色都會被同步影響到。
要設定一個安全原則來指出哪些資料列可見或可修改,是以一個回傳值為布林值的表示式來決定的。這個表示式會計算每一個資料列的結果,在使用者進行任何操作之前。(這個規則唯一的例外是 leakproof 函數,用來確保沒有洩漏資訊;查詢最佳化元件會選押在確任資料列安全原則前就先執行它。)在這個表示式沒有回傳 true 的資料列,都是不能被存取的。獨立的表示式可用於提供資料列專屬的控制,判斷其是否可供讀取或修改。安全原則表示式是查詢的一部份,和使用者執行查詢時一起執行,不過,安全原則表示式是可以存取到該使用者看不到的資料。
超級使用者因為擁有 BYPASSRLS 的屬性,所以永遠可以通過安全原則檢查而存取資料表。資料表的擁有者一般來說也是可以通過檢查,但可以使用 來強制適用安全原則。
開啓或關閉資料列安全原則的權限,只屬於資料表擁有者。
使用 指令來建立安全原則;使用 指令來修改;使用 指令來移除原則。要開啓或關閉安全原則的功能,請使用 指令。
每一個安全原則都有一個名稱,而一個資料表可以定義多個安全原則。安全原則是資料表專屬的,而每一個安全原則在所屬資料表內必須有一個唯一的名稱。不同的資料表下的安全原則可以取相同的名稱。
當多個安全原則使用者某個查詢上時,可能會使用 OR 串接(開放安全原則 permissive policies,這是預設的狀態),也可能以 AND 串接(嚴格安全原則 restrictive policies)。這類似角色授權的情況。有關於開放安全原則與嚴格安全原則的細節,稍後再進行說明。
先進行一個簡單的範例,我們建立一個安全原則在資料表 account 上,它只允許 managers 的使用者可以存取資料列,並且只能存取他自己帳號的資料列:
如果沒有指定角色或使用者時,就會以 PUBLIC 替代,也就是所有使用者都適用。要允許所有使用者存取他們自己的資料列的話,就可以簡化指令為:
想要定義一個安全原則是有別於可見性權限的話,請使用 WITH CHECK 字句。例如希望讓所有人都可以看到所有資料列,但只能修改自己的資料的話:
資料列安全原則也可以透過 ALTER TABLE 指令關閉。不過關閉資料列安全原則,並不會移除任何已定義的原則,只是單純被忽略而已。然後資料表的所有資料列,就只依標準 SQL 的權限系統,決定查詢及修改的權力。
下面是一個較複雜的例子,展示這個功能如何被應用於產品等級的環境裡。資料表 passwd 模擬 Unix 的密碼檔:
對於任何的安全設定,很重要的是,你必須實際測試來確認系統的行為和你預期的相同。使用上面的例子,下面的測試表現出權限設如預期地運作。
所有的安全原則,目前來說都是開放安全原則,意思是當有多個安全原則被引用時,它們會以 OR 運算串連其結果。開放安全原則用於只允許在計畫內的環境使用的話,它會比和嚴格安全原則(把安全原則用 AND 串連起來判斷)一起使用來得簡單。基於上面的列子,我們建立一個嚴格安全原則,它限制管理者只能透過 unix socket 連線才能存取 passwd 資料表:
我們接下來就可以看到,管理者透過一般網路連線,是看不到任何資料的,因為嚴格安全原則:
資料一致性的檢查,像是唯一性、主鍵、以及外部鍵參考,都會略過資料列安全原則,以維持資料的一致性。在發展資料庫結構時應該要特別小心,以資料列安全原則避免透過一致性檢查而產生隱藏通道洩露資訊。
在上面的例子裡,安全原則表示式只引用了目前資料列中的資料。這是最簡單也是最常見的形式,可以的話,最好以這樣的方式來設計安全原則。如果需要參考其他資料列或資料表來做決定的話,那麼可以使用子查詢或函數的方式達成,也就是包含一個 SELECT 的查詢語句在表示式中。要注意到的是,這種方法可能會造成資料庫內交易競爭(race condition)的狀態,不注意的話也可能產生資訊的洩漏。像這樣的例子,試試下面的資料表設計:
現在假設 alice 想要變更"slightly secret"的資訊,但決定不讓 mallory 看到新的內容,所以她這麼做:
看起來很安全,因為沒有窗口讓 mallory 可以看到"secret from mallory",然而,這裡就存在了交易競爭的情況。如果 mallory 也在同時做了:
因為她的交易是屬於 READ COMMITTED 模式,所以她有可能會看到"secret from mallory"。這會剛好發生在,她在 alice 的交易完成前一刻。mallory 的指令會暫時擋下 alice 的提交完成,而因為 FOR UPDATE,她會取得更新後的資訊。所以她並沒有從隱含的使用者執行 SELECT 取得資訊,因為子查詢沒有 FOR UPDATE,使得其他使用者可以從快照裡取得資訊。因為安全原則是以舊的 mallory 權限允許她看見該筆資料。
這個問題有好幾個面向的解決方式。一個簡單的方式就是使用 SELECT ... FOR SHARE 在安全原則的子查詢裡。但這樣就必須要讓使用者擁有 UPDATE 的權限,可能不太合適。(但也可以用另一個安全原則來做更多的限制,又或是把子查詢封裝進另一個安全的函數裡)同時,大量的引用查詢也可能造成效能的問題,特別是更新資料的時候。另一個解決辦法,如果參考的資料表並不是很常更新的話,那麼可以在資料表更新時強制鎖定該資料表,確保沒有其他交易能在同時進行查詢,也就不會洩漏任何資訊。或是等待其他所有交易都完成後,才提交更新變更新的安全方案。
在某些情況,很重要的是要確認安全原則是否被觸發。舉例來說,當進行資料備份流程時,如果安全原則造成某些資料被備份程式忽略了,那可能就會很糟糕。在這種情況下,你可以把 這個參數設為 off。這並不是避開安全原則,而是在觸發安全原則時,會出現錯誤訊息,使得我們可以發現進而修正原則。
更多詳細,請參閱 和 。