所以我學資料庫的方法和目前很多技術學院的學生的途徑很像,只是我在第二年又碰到一個更新鮮的玩意兒 ─ Microsoft SQL Server 6.5。這東西的後端其實是 Sybase 的 SQL Server ,但微軟把界面故意設計得和 Access 的使用邏輯很像,所以我還是很快的就上手了。只是這兩項微軟的資料庫早期對中文的支援都只有片面性,比如資料是中文沒問題,但是物件和欄位名稱就很抱歉了。在圖形界面下,以類似 E-R Diagram 的方式來設計資料庫,真的比以 SQL DDL 來定義簡單而且易學很多,所以也難怪市面上充斥著微軟資料庫產品的書籍了。如果你要設計查詢時,Access 的 QBE 界面(我本來一直以為這東西是微軟發明的,後來才知道是 IBM 發明的),可以讓我在半調子的程度下,搞出多變的查詢。我一直依賴著這些工具,一直等到 1996 年的某一天,我所任職的大學電算中心主任要我以 isql (MS SQL Server 的指令行互動界面) 和 C 搭配寫 CGI 時,我才開始真的好好熟練 SQL 這個語言。
在那個時代中,你想學 SQL,可以依賴微軟放在 Access 的範例 ─ 北風資料庫,很快的就可以學會所有工具和技巧。當我 port 到 Oracle 時,也有 Employee 資料表可以利用。但是,一旦你轉到 Open source 資料庫時,若你想從頭開始學習使用這套,你所新遇到的資料庫管理系統,那麼你所面臨的第一個問題將是:欠缺好的範例可資使用。通常大部份的人會回頭找北風資料庫來用。但是北風資料庫並不是一套很好轉移到其它 RDBMS 的資料庫範例,因為 MS Access 它所用的 SQL 是一種被微軟修改過的版本,這個版本甚至和 MS SQL Server 的 Transact-SQL 差距不小,所以在轉移時肯定會出現一些問題。所以,最好看看能不能在網路上可以搜尋到一些 SQL 範例。
我花了一個多月的時間,大概找了一下,最適合的有兩個。
第一個是中山大學黃三益老師所寫的「資料庫的核心理論與實務」的專屬網站。黃教授為了「普遍性原則」,在範本中列了 SQL 2 以及另外幾項在台灣常見的 RDBMS 的專屬範例資料庫。很不幸的,PostgreSQL 並不在其列。更糟的是,範例中的 SQL 指令錯誤不少,光是更正錯誤就花掉我快一星期的時間。等一下我會把正確的 PostgreSQL 版本放上來。他的範例的另一個缺點是:他並沒有試著故意把資料庫表格名稱和欄位名稱用中文來表現。對於「普遍性原則」來說,這樣做是對的,因為不見得所有的 RDBMS 都支援中文物件名稱或甚至欄位名稱。但是如果我今天 focus 在單一資料庫時,我也想試著看看 PostgreSQL 在 SQL 上的彈性最大可以到達何種程度。
所以我找到第二個資源。這第二個資源其實也不夠理想,因為它其實是翻譯自日文書籍的範例,所以在習慣上和我們國情上有一點點差異,而且它一樣只提供 MS SQL Server 和 Oracle 的版本。更糟的是,Oracle 版本根本不可用,因為錯誤百出,可能是翻譯者的 Oracle 程度不足,無法把其中的錯誤找出。但是我一樣花了兩天把給 SQL Server 用的 SQL 指令拿來修改,還是可以輕鬆 port 到 PostgreSQL 上。這個第二個資源是博碩文化的書:DB20060 SQL基礎講座的範例。
我們先從黃老師的範例來看。PostgreSQL 和 Oracle 的語法最為接近,大部份的 Orcale 專屬的 SQL 指令都可以在 PostgreSQL 上使用。而且 SQL2 (SQL-92) 的版本也有錯,比如:
1. Browse table 中,mId 的 default 值,不小心打了全形中文的’,應該要用英文半形的單引號 ' 才對。
2. Cart table 中,cartTime 欄位的定義中,TIMESTAMP 和 NOT NULL 中間,多了一個逗號。
所以我立刻放棄使用 SQL2 的範例,而選用 Oracle 版本來用。結果不幸的發現,當中的錯誤更多,比如:
1. member table 的 phone 欄位不夠,因為台北地區的區碼加電話再加上一條橫線,長度應該是 11 而不是 10。
2. member table 的資料第一筆應該要是 MID='b0905555',否則後面很多筆的 introducer 會參照到該筆。
3. Oracle 用的 Cart table 資料匯入檔的最後一筆,TNO 欄位空值違反了 Not NULL 的限制原則。該筆 (MID='a0910001') 的 TNO 可以參考一下 MySQL 用的資料匯入檔,應該是 TNO='91888'。
4. Oracle 用的 "Order" 資料匯入檔,table name 應該要和 Schema File 一致,也就是 O 要大寫。
還有一個 PostgreSQL 一直還沒解決的問題:本地化。黃老師的範例中,時間「上午/下午」在 PostgreSQL 的 parser 是無法理解的,所以所有的「上午/下午」都要改成 AM/PM。
在搏鬥了一個星期後,我突然覺得,若是讓初學者「抓蟲」或許也是一種很好的磨練 SQL 的機會。比如,SQL-92 中,Order 是可以用來做為物件名稱的。但是這在大部份的 RDBMS 都是違反「保留字」原則的。所以都必須用「引號」特別指出這是一個識別碼,並非使用者下達保留字所代表的「排序命令」。這裏 Oracle/MS SQL Server 和 PostgreSQL 都一樣的,以雙引號「"」將 Order 包圍起來表示它是一個物件識別碼:
CREATE TABLE "Order"
(pNo CHAR(6) NOT NULL,
mId CHAR(8) NOT NULL,
:
:
FOREIGN KEY (mId, cartTime) REFERENCES Cart(mId, cartTime));
但是,MySQL 卻是用單引號,這點很怪,但是它的設計者自有他的邏輯,在此不多做討論。
在這些修改的過程,或許可以更讓 SQL 初學者更加了解 SQL 的語法,以及遇到像 Order 這個特殊狀況時的解決方法,所以修改黃老師的範例是一個很棒的練習。當然了,如果你已經是 SQL-92 的高手,而不想浪費時間在依照我提示的地方慢慢去修改,那可以按此處下載我修改過的版本的 ZIP 檔。
至於博碩的版本,可以改自它的 SQL Server 版本,修改時有兩件事要注意:
1. SQL Server 的 Datetime 資料型態並不是標準 SQL-92 和 SQL:1999 所建議的資料型態,請改成 Timestamp 或 Date。用 Date 比較簡單,因為範例中其實只用了日期。
2. 中文物件名稱在 PostgreSQL 中就像保留字一樣,必須用雙引號包圍起來。
第二點其實才是最討厭的地方,因為幾乎每個物件名稱都要改!我把改好的兩個 SQL 檔附在:tbl_pgsql.sql 和 pg_sample.sql。
另外,我既然把人家的範例拿來改,當然要幫忙推銷人家的作品。所以初學者如果想學資料庫理論或 SQL 語法,可以去書店 (網路書店現在很方便,可以送書到你家附近的便利商店,而且可以直接到便利商店取貨時付款) 購買這兩本書來學,我在文中有相關的 URL,可以直接按進去參考。由於 PostgreSQL 緊跟著 SQL 標準,所以大部份這兩本書的內容都可以直接拿來學習。當然了,這兩本書當初作者在寫書時,腦袋中根本沒出現 PostgreSQL 這個字,所以當你遇到問題時,請自己想辦法解決。而我在這篇文章中其實也提了幾件基本的轉移原則,再有其它漏掉的原則,我會找時間另文解說。
2009/6/16 update
把範例做了一些修正,主要修正有三點:第一點是將散布授權條約改成 BSD License。 第二點是將中文資料改成 UTF-8 Unicode 編碼。第三點是在 DDL 範例中加入 DROP TABLE IF EXISTS ......;,以方便不斷重新測試使用。
所以,由於改用 BSD License,請採用時切記註明出處及儘量符合該授權條約的限制方式散布。由於採用 UTF-8 編碼方式,所以若是您的程式是在 BIG5 的預設編碼下,可能會告成亂碼的情況。但是為了儘快讓中文化轉移到世界通用的編碼方式,所以不得不做這樣的決定。由於加入 DROP TABLE IF EXISTS ......; 指令,所以一些較舊版本的 PostgreSQL(8.0 版之後的應該都沒問題) 可能會出現錯誤,請自行將該行註解掉或刪除即可。
除了這三點修正以外,我另外做了兩項小修正。第一個小修正是把黃老師的每個資料表格,都有各別的 .SQL 檔,合併另外寫一個 data.sql,將所有資料表格的輸入資料,合併在該檔中,這樣方便要重新載入資料測試時使用;但是原來各個資料表格分開的資料輸入檔依舊存在,以方便教學使用。第二個小修正是把每個 DDL 和 DML 群,都在 TRANSACTION(在 PostgreSQL 就是 BEGIN; 和 COMMIT; 兩個指令所包圍起來的 DDL 和 DML) 中進行。這其實是多餘的,純綷是為了和黃老師的範例中最後一行 COMMIT; 保持一致而加上。在 PostgreSQL 預設情況,若是你沒加上 BEGIN; 和 COMMIT; 對,則會將你的每行指令都當成個別的交易。換句話說,對於這兩個小範例而言,加和不加交易 session,結果是一樣的,過程上的差異也不是那麼重要。但是在實際的操作上,當你要把 dump 的資料 restore 回資料庫時,還是強烈建議最好仿效範例,把相關的 DDL 和 DML 以 transaction 包裹起來,以確保資料一致性。
還有,為了方便網友快速下載,我將這兩組 sample code 的下載點特別列在下方:
修改自黃三益老師的「資料庫的核心理論與實務」的範例:samples.zip
修改自博碩文化的:DB20060 SQL基礎講座的範例:tbl_pgsql.sql 和 pg_sample.sql