PostgreSQL 內建的命令列工具 psql 既能寫腳本也能互動使用,功能強大:自動補全、readline 支援(歷史搜尋、現代快捷鍵)、輸入輸出重導、格式化輸出等。
PostgreSQL 新手常想找進階的視覺化查詢編輯工具,聽到答案是 psql 時一臉困惑;而多數進階使用者與專家想都不想,就是用 psql。本章教你真正欣賞這個小工具。
psql 入門#
psql 實作的是 REPL(read-eval-print loop)——學習與嘗試新事物時和電腦互動的最佳方式之一:探索 schema、探索資料集、或琢磨一條查詢。
我們通常只看到 SQL 查詢的最終完成形,很少看到通往它的過程。程式碼也一樣:你看到的是成品,不是作者反覆嘗試、逐步釐清問題的中間步驟。寫出完整而高效 SQL 的過程與寫程式相同——從最簡單的切入點開始迭代,而 REPL 環境讓你能輕鬆地在前一步的基礎上疊加。
psqlrc 設定#
先給出完整設定,本章其餘部分再逐項回頭解釋。存進 ~/.psqlrc(psql 啟動時讀取),你就有一個立即可用的環境:
\set PROMPT1 '%~%x%# '
\x auto
\set ON_ERROR_STOP on
\set ON_ERROR_ROLLBACK interactive
\pset null '¤'
\pset linestyle 'unicode'
\pset unicode_border_linestyle single
\pset unicode_column_linestyle single
\pset unicode_header_linestyle double
set intervalstyle to 'postgres_verbose';
\setenv LESS '-iMFXSx4R'
\setenv EDITOR '/Applications/Emacs.app/Contents/MacOS/bin/emacsclient -nw'這裡用到三種設定指令:
\set [name [value ...]]:設定 psql 變數;多個值會串接;不給值則設為空值;用\unset取消。\setenv name [value]:設定環境變數(不給值則取消)。此處用來設定 psql 所需的環境變數,例如LESS——每個結果集都交給分頁器,但只在必要時才接管畫面。\pset [option [value]]:控制查詢結果表格的輸出選項;value 的語意依選項而異,省略時多為切換或顯示目前設定。
交易與 psql 行為#
幾個改變 psql 行為的關鍵變數:
\set ON_ERROR_STOP on:如其名——前一條指令出錯時不再繼續執行後續指令。主要用於腳本(也可從命令列設定),但因為互動中也常用\i、\ir執行腳本,此選項仍然有用。\set ON_ERROR_ROLLBACK interactive:改變 psql 的交易行為。設為on時,交易區塊內的語句出錯會被忽略、交易繼續;設為interactive時只在互動階段如此,讀腳本檔時不適用。其原理是 psql 在交易區塊內的每條指令前自動發出隱含的SAVEPOINT,失敗時自動 rollback 到該 savepoint。
搭配 \set PROMPT1 '%~%x%# ',提示符會在交易進行中顯示一顆小星號,提醒你要收尾;反過來說,要做任何有副作用的操作(改資料或改 schema)時,沒看到星號就知道要先打 BEGIN。
比較兩種行為。ON_ERROR_ROLLBACK 為預設(off)時:
f1db# begin;
BEGIN
f1db*# select 1/0;
ERROR: division by zero
f1db!# select 1+1;
ERROR: current transaction is aborted, commands ignored until end of transaction block
f1db!# rollback;
ROLLBACK出錯後星號變成驚嘆號,交易已被標記無效,PostgreSQL 只接受 commit 或 rollback——而且兩者的結果都是 ROLLBACK。設為 interactive 之後:
f1db# begin;
BEGIN
f1db*# select 1/0;
ERROR: division by zero
f1db*# select 1+1;
?column?
══════════
2
(1 row)
f1db*# commit;
COMMIT錯誤之後不但能繼續送出指令、仍在交易中,最後還能成功 COMMIT。
報表工具#
psql 有兩大用途:互動工具、以及腳本與報表工具。後者提供進階的格式化能力——透過 \pset format 可以把查詢結果直接輸出成 asciidoc 或 HTML。例如查詢某位車手姓氏的前 N 佳績,設定動態變數、只顯示資料列、輸出 HTML:
~ psql --tuples-only \
--set n=1 \
--set name=Alesi \
--no-psqlrc \
-P format=html \
-d f1db \
-f report.sql<table border="1">
<tr valign="top">
<td align="left">Alesi</td>
<td align="left">Canadian Grand Prix</td>
<td align="right">1995</td>
<td align="right">1</td>
</tr>
</table>連線參數可用環境變數,也可直接用與應用程式碼相同的連線字串(copy/paste 即可測試,不必轉成 -d dbname -h hostname ... 語法):
~ psql -d postgresql://dim@localhost:5432/f1db
~ psql -d "user=dim host=localhost port=5432 dbname=f1db"report.sql 中使用 :'name' 變數語法——:name 會少掉字面值的引號,:'' 則能正確處理含空白的值;psql 也支援 :"variable" 雙引號記法,用於識別字(欄名、表名)當參數的動態 SQL:
select surname, races.name, races.year, results.position
from results
join drivers using(driverid)
join races using(raceid)
where drivers.surname = :'name'
and position between 1 and 3
order by position
limit :n;跑報表時用
--no-psqlrc確保不載入互動用設定(UTF-8 花樣與ON_ERROR_ROLLBACK等)——批次或報表腳本通常不想要那些;倒是可以考慮設ON_ERROR_STOP。
探索 Schema#
回到互動功能。psql 除了送出 SQL、顯示結果與伺服器通知(notification)、錯誤訊息之外,還提供一整組以反斜線開頭的客戶端命令,多數用於探索資料庫 schema——它們全都是對伺服器執行一或多條系統目錄(catalog)查詢實作的。也就是說,你可以藉由觀察這些查詢,學會怎麼查 PostgreSQL 的 catalog。
例如想報告資料庫大小卻不知道去哪找:文件說 \l+ 可以做到,打開 ECHO_HIDDEN 就能看到它背後的 SQL:
~# \set ECHO_HIDDEN true
~# \l+
********* QUERY **********
SELECT d.datname as "Name",
pg_catalog.pg_get_userbyid(d.datdba) as "Owner",
...
CASE WHEN pg_catalog.has_database_privilege(d.datname, 'CONNECT')
THEN pg_catalog.pg_size_pretty(pg_catalog.pg_database_size(d.datname))
ELSE 'No Access'
END as "Size",
...
FROM pg_catalog.pg_database d
JOIN pg_catalog.pg_tablespace t on d.dattablespace = t.oid
ORDER BY 1;
**************************於是「只要資料庫名稱與磁碟大小」就簡單了:
SELECT datname,
pg_database_size(datname) as bytes
FROM pg_database
ORDER BY bytes desc;書裡不重印公開文件——去把 psql 的手冊整頁讀完,裡面有很多你不知道的寶藏。
互動式查詢編輯器#
前面設定的 EDITOR 環境變數,就是 psql 執行 \e 等視覺編輯命令時呼叫的程式:它會把上一條查詢(或空白內容)放進暫存檔用你的編輯器打開,編輯結束後立即執行該查詢。用 emacs 或 vim 的人在終端機裡會非常開心;psql 在本機執行時,也可以把 EDITOR 設成你慣用的 IDE。