PostgreSQL 與 Java 實戰系列:四、SQL 簡介

本系列是之前應 Newzilla 邀稿而發表刊載的文章,現在在本站重新整理後發表。本篇介紹 SQL 起源與語法、匯入及匯出資料庫,以及舊有 PostgreSQL 資料庫轉移的注意事項。

  1. 資料庫系統簡介
  2. PostgreSQL 簡介
  3. 資料庫應用實作
  4. SQL 簡介(本篇)
  5. JDBC 程式撰寫

SQL 起源與語法

表格規劃完成後,開始要建立表格和新增資料。最早關聯式資料庫處理資料的語言是在 1970 年代發展出來的,稱之為「結構化查詢語言」(Structured Query Language,以下簡稱 SQL)。SQL 可以建立及定義資料庫、表格,修改欄位資訊、資料處理等等。1986 年 ANSI 制定了一套標準的 SQL 規格,並在 1992 年推出第二個版本,也就是目前常看到的 SQL92(SQL2)標準,幾乎目前所有的關聯式資料庫都有支援此一規格,不過都沒有完整支援。在 1999 年發佈的 SQL99(SQL3)仍在發展當中,比原本的 SQL92 語法更多更豐富。

SQL 語法可分成三類:

  • 資料定義語言(Data Definition Language,簡稱 DDL)
    用來建立、修改或刪除資料庫、表格、輪廓和索引,例如 CREATEALTERDROP
  • 資料操作語言(Data Manipulation Language,簡稱 DML)
    用來新增、修改、刪除及查詢資料,例如 SELECTINSERTUPDATEDELETE
  • 資料控制語言(Data Control Language,簡稱 DCL)
    用來檢索目錄及提供安全性,例如 BEGINCOMMITROLLBACKGRANTREVOKE

在文字模式下,你可以使用之前介紹的 psql 指令進入 RDBMS 中直接輸入語法;圖形模式下請先在左側清單中點選「資料庫」中的「guestbook」(也就是早先建立好的資料庫),然後按 Alt+T 叫出「工具」選單,點選「查詢工具」項目,即可輸入 SQL 語法,並且按 F5 熱鍵執行。

下列描述的語法中,全字大寫者表關鍵字詞,全字小寫者為變數,[] 中的字詞是選擇性字詞,{} 表示群組字詞,,... 是重複性的不定量變數,另所有 SQL 語法皆以 ; 結尾,-- 則為註解。

資料庫部份

建立資料庫

由於我們在前面已先行建立名為 guestbook 的資料庫,所以此處只是列出來做為參考:

CREATE DATABASE database_name;

刪除資料庫

DROP DATABASE database_name;

表格部份

建立表格

CREATE TABLE table_name (
  { column_name type [DEFAULT default_expr] -- DEFAULT 是設定欄位預設值
    [column_constraint [, ...]] }
  [, ...]                                   -- 其他欄位
);

type:                     -- 欄位資料型態
  SMALLINT                -- -32768 ~ 32767
| INTEGER                 -- -2147483648 ~ 2147483647
| BIGINT                  -- -9223372036854775808 ~ 9223372036854775807
| NUMERIC[(prec[, sca])]  -- 精確數字,prec 為總位數,sca 為小數位數
| DECIMAL[(prec[, sca])]  -- 等效於 NUMERIC
| REAL                    -- 六位十進位浮點數
| DOUBLE PRECISION        -- 十五位十進位浮點數
| SERIAL                  -- 自動遞增序號,可配合 NOT NULL 及 UNIQUE
| CHAR(n)                 -- 固定長度字串,n 為長度,不足者補空白
| VARCHAR(n)              -- 可變長度字串,n 為長度
| TEXT                    -- 可變長度字串
| DATE                    -- 日期
| TIME                    -- 時間
| TIMESTAMP               -- 日期及時間
| BOOLEAN                 -- 布林值。TRUE, 'y', '1' 或 FALSE, 'n', '0'

column_constraint:
  NOT NULL | NULL               -- 非空值或允許空值
| UNIQUE | PRIMARY KEY          -- 獨一無二的值,或主鍵值
| REFERENCES table_name         -- 外來鍵值參考
  [MATCH FULL | MATCH PARTIAL]  -- 可選擇全部符合或部份符合

洋洋灑灑一大串,看起來頗嚇人的,不過這邊也只寫出常用的部份而已。雖然好像很複雜,但實際上我們只要依照需求來增減就可以了。先從最簡單的星座表格開始:

CREATE TABLE astro (
  astroIndex SERIAL NOT NULL PRIMARY KEY,  -- 編號:遞增、非空、主鍵
  name CHAR(3) NOT NULL);                  -- 名稱:固定長度、非空

接下來看複雜一些的會員表格:

CREATE TABLE member (
  memberIndex SERIAL NOT NULL PRIMARY KEY,   -- 編號:遞增、非空、主鍵
  login VARCHAR(15) NOT NULL UNIQUE,         -- 帳號:可變長、非空、唯一
  password VARCHAR(15) NOT NULL,             -- 密碼:可變長、非空
  sexy INTEGER DEFAULT 0,                    -- 性別:整數、預設 0 為男
  email VARCHAR(100),                        -- 電子郵件:可變長度
  astro INTEGER REFERENCES astro MATCH FULL, -- 星座代號:外來鍵值
  jointime TIMESTAMP NOT NULL,               -- 註冊時間:非空
  priv INTEGER DEFAULT 0);                   -- 權限:預設 0 為使用者

最後是留言表格:

CREATE TABLE note (
  noteIndex SERIAL NOT NULL PRIMARY KEY,      -- 編號:遞增、非空、主鍵
  member INTEGER REFERENCES member MATCH FULL,-- 會員代號:外來鍵值
  content TEXT NOT NULL,                      -- 內容:可變、非空、唯一
  posttime TIMESTAMP NOT NULL);               -- 時間:非空

刪除表格

DROP TABLE table_name;

資料處理部份

新增資料

INSERT INTO table_name [ ( column [, ...] ) ]
  VALUES ( expression [, ...] );

INSERT 子句中你可以指定欄位,那麼其後的值就需要以欄位的前後順序來填寫,以星座欄位為例:

INSERT INTO astro VALUES (1, '牧羊座');               -- 不指定欄位
INSERT INTO astro (astroIndex, name) VALUES (2, '金牛座');
INSERT INTO astro (name, astroIndex) VALUES ('雙子座', 3);  -- 換次序

INSERT INTO astro (name) VALUES ('巨蟹座');
  -- 可選擇只輸入其中幾個欄位,資料庫會自動填入剩餘欄位。本例中編號
  --   的資料型態是SERIAL,所以資料庫會自動為其遞增設值。請注意省略
  --   輸入的欄位在創建表格時需有設定 DEFAULT 子句,或是為 SERIAL。

查詢資料

查詢資料使用的是 SELECT 語法,它的變化相當豐富,在這裡我們列出幾個最基本且常用的部份:

SELECT { * | expression [ AS new_column_name ] [, ...] }
    [ FROM table_name [, ...] ]
    [ WHERE condition ]
    [ GROUP BY expression ]
    [ HAVING condition [, ...] ]
    [ ORDER BY expression [ ASC | DESC ] ]
    [ LIMIT count ]
    [ OFFSET start ];

SELECT 看起來很複雜,底下會針對各個子句一一介紹。首先是最簡易的語法,可以查詢出表格中所有的記錄,並且會將所有欄位顯示出來(* 表所有欄位):

SELECT * FROM astro;

如果要顯示特定欄位的話,可以直接指定欄位名稱:

SELECT name FROM astro;  -- name 為 astro 表格的欄位

條件查詢可以使用 WHERE 子句,後面接條件式:

SELECT * FROM astro WHERE astroIndex < 4;
  -- 顯示 astroIndex 欄位的值小於 4 的記錄
SELECT * FROM astro WHERE astroIndex >= 10;
  -- 顯示 astroIndex 欄位的值大於等於 10 的記錄
  -- 同類的運算子有 <, >, =, <=, >=, <> (不等於)
SELECT * FROM astro WHERE name like '雙%';
  -- 搜尋 name 欄位中有'雙'字開頭的記錄,% 表任意字元 (需使用 like)
SELECT * FROM astro WHERE name like '雙__';
  -- 搜尋 name 欄位中有'雙'字開頭且其後接兩個字元的記錄,_ 表單一字元
SELECT * FROM astro WHERE astroIndex between 1 and 6;
  -- 顯示 astroIndex 從 1 到 6 間的資料。用法:between 開頭 and 結尾
SELECT * FROM astro WHERE astroIndex in (1, 5, 9);
  -- 顯示 astroIndex 數值為 1, 5 和 9 的資料。用法:in (num [, ...])
SELECT * FROM astro WHERE name like '雙%' or name like '天%';
  -- 搜尋 name 欄位中有'雙'字或'天'字開頭的記錄
  -- 同類的運算子有 and, or, not

ORDER BY 子句使用在排序檢視上,其後加上欲排序的欄位名稱:

SELECT * FROM member ORDER BY login;
  -- 以 login 欄位遞增排序來顯示所有 member 表格中的記錄
SELECT * FROM note ORDER BY noteIndex DESC;
  -- 以 noteIndex 欄位遞減排序來顯示所有 note 表格中的記錄
  -- 在留言版會常用此一方法,以保持最新的記錄會最先顯示
  -- ASC 為遞增排序,DESC 為遞減排序

LIMIT 子句可以限定每次要顯示幾筆記錄,其後加上欲顯示的數目,OFFSET 則是欲先行略去的偏移筆數:

SELECT * FROM note ORDER BY noteIndex DESC LIMIT 10;
  -- 以 noteIndex 欄位遞減來顯示 note 表格中的十筆記錄
SELECT * FROM note ORDER BY noteIndex DESC LIMIT 10 OFFSET 5;
  -- 以 noteIndex 欄位遞減來顯示 note 表格中略去前五筆後的十筆記錄

GROUP BY 子句主要用在群組上,也就是其欄位中重複的值只會出現一次,而忽略其他同樣的值:

SELECT member,max(posttime) FROM note GROUP BY member
  ORDER BY max(posttime);
  -- 搜尋 note 中的會員代碼欄位,以及其相對應的最大(意即最後)發文時間
  -- 接著使用 GROUP BY 子句讓每個會員代碼只出現一次,最後再以發文時間排序
  -- 此例可找出每個會員最後發文時間
SELECT member,now() - max(posttime) FROM note GROUP BY member
  ORDER BY max(posttime);
  -- 功能同上,但這裡用 now() 找出現在時間後,減去每個會員最後發文時間
  -- 如此可得知每個會員上次發文後距離現在隔有多長時間

HAVING 子句需和 GROUP BY 子句合用,表條件查詢。WHERE 子句也同樣是在做條件查詢,但它和 WHERE 子句
所不同的地方在於,所有記錄會先交給 WHERE 子句過濾後,再交由 GROUP BY 子句處理,接下來才會輪到 HAVING
子句去做進一步的過濾:

SELECT member,now() - max(posttime) FROM note GROUP BY member
  HAVING member > 20 ORDER BY max(posttime);
  -- 功能同上,此處多加上的 HAVING 表示只顯示會員代碼大於 20 的記錄

修改資料

UPDATE 語法用來修改資料,你可以一次修改表格中所有記錄,或是使用 WHERE 去過濾欲修改的資料:

UPDATE table_name SET column = { expression | DEFAULT } [, ...]
    [ WHERE condition ];  -- 不加上 WHERE 子句的話,會將表格所有記錄做修改

例如我們要將全部 member 表格中的使用者權限皆設為 0,可以使用下列語法:

UPDATE member SET priv=0;
UPDATE member SET priv=0, email='';
  -- 你也可以使用逗號分隔的方式來同時設定多個欄位

如果只想更新「符合某些特殊條件」的資料,可以使用 WHERE 子句:

UPDATE member SET priv=1 WHERE login='Tom';
  -- 將會員 Tom 的權限調整至 1 (管理者)

刪除資料

DELETE FROM table [ WHERE condition ];
  -- 不加上 WHERE 子句的話,會將表格所有記錄刪除

將所有留言刪除:

DELETE FROM note;

刪除會員代號 10 的所有留言:

DELETE FROM note WHERE member=10;

匯入及匯出資料庫

資料庫在運行一段時間後,可能會遭遇到一些天災人禍的意外而造成資料損毀,這個時候我們只能使用預先的 備份來回復整個系統。底下介紹的就是匯出及匯入的指令,首先要來看的是匯出,使用 pg_dump 指令:

pg_dump [Option] [Database]

常用參數解說:
  -f, --file=FILENAME      輸出檔名
  -F, --format=c|t|p       輸出格式 (c 自訂, t Tar 檔, p 純文字)
  -i, --ignore-version     忽略版本差異
  -v, --verbose            多訊息顯示
  -a, --data-only          只匯出資料
  -b, --blobs              匯出時包括大型物件
  -c, --clean              先行匯出刪除語法,其後再匯出創建語法
  -d, --inserts            使用 INSERT 語法代替 COPY 語法
  -s, --schema-only        只匯出資料庫架構

你可以依照需要來增減參數。底下是我個人的使用方法,guestbook 是資料庫名稱,gb-20040910.txt 是欲匯
出的文字檔名:

pg_dump -f gb-20040910.txt -i -c -d guestbook

另外如果是要匯出全部資料庫的話,那麼可以使用 pg_dumpall 指令,常用參數和 pg_dump 的使用方法相同。

如果要將 SQL 語法的純文字檔匯入資料庫的話,我們使用的是 psql 指令:

psql -f gb-20040910.txt -U gbadmin
# -f 代表欲匯入的檔案名稱
# -U 代表指定資料庫使用者所擁有的權限來匯入資料

或者你要將 pg_dump 輸出格式為 c(自訂)或 t(Tar檔)的檔案匯入時,可以使用 pg_restore 指令將其回存至資料庫:

pg_restore [Option] [File]

常用參數解說:
  -d, --dbname=NAME        欲匯入的資料庫名稱
  -f, --file=FILENAME      輸出檔名
  -F, --format=c|t         指定輸入檔案的格式 (c 自訂, t Tar 檔)
  -i, --ignore-version     忽略版本差異
  -v, --verbose            多訊息顯示
  -a, --data-only          只匯入資料
  -c, --clean              先行匯入刪除語法,其後再匯入創建語法
  -I, --index=NAME         只匯入指定的索引
  -P, --function=NAME(arg) 只匯入指定的函式
  -s, --schema-only        只匯入資料庫架構
  -t, --table=NAME         只匯入指定表格的定義和記錄
  -T, --trigger=NAME       只匯入指定的觸發器

用法:
pg_dump -d guestbook -F t -i -c gb-20040910.tar

舊有 PostgreSQL 資料庫轉移注意事項

如果你之前曾經使用或調校過 PostgreSQL 7.x,並且想要將資料庫轉移到 PostgreSQL 8.0 的話,那麼下列事項請仔細檢查,以避免更換資料庫時失敗而造成資料出錯:

  • 組態檔參數 virtual_hosttcpip_socketlisten_addresses 取代。
  • 參數 SortMemVacuumMem 更名為 work_memmaintenance_work_mem 以反映它們的實際應用。
  • 參數 log_pidlog_timestamplog_source_port 由更具彈性的 log_line_prefix 所取代。
  • 參數 syslog 由較合理的 log_destination 參數去控制日誌輸出目的地。
  • 參數 log_statement 修改至可選擇性地去記錄資料庫變更或資料定義敘述。
  • 參數 max_expr_depthmax_stack_depth 取代。它會測量出實體堆疊尺寸而不是運算過程中的巢狀深度。這可以避免連線終端機因遞迴函式而產生的堆疊溢位。
  • 函式 length() 不會將資料型態 CHAR(n) 中字尾空白的部份列入計算了。
  • 將整數轉型為 BIT(N) 時會選擇整數右側 N 位元,而非早期的左側 N 位元。
  • 將空字串指定給 oidfloat4float8 型態時,伺服器將會發出警告。下個版本中這個項目會改為發出錯誤訊息。
  • 函式 extract() (也稱為 date_part) 現在會回傳適當的西元前日期。
  • psql 的 copy 命令現在為讀寫「查詢標準輸入/輸出」,而非 psql 程式的標準輸入/輸出。早期的動作可以經由新的 pstdin/pstdout 參數來存取。
  • JDBC 客戶端介面已從核心版本中移除,現在搬移至 https://jdbc.postgresql.org。TCL 客戶端介面也移除了。你可以在 http://gborg.postgresql.org 下找到幾個 TCL 介面。
  • 伺服器現在使用自身的時區資料庫,而不採用作業系統的內建支援。它為跨平台提供了一致的行為。在大多數情況下,應該會有一點點顯著的差異存在於時區處理中,SET/SHOW 使用的時區名稱可能會和您的平台所使提供的有所差異。
  • EXECUTE 現在會回傳符合被執行指令的完整標記。
  • 組態中的執行緒選項已無需使用者自行測試或編輯組態設定檔;執行緒選項現在已會自動偵測。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

ten − one =

返回頂端