Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
窗函數(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。舉個例子:
繼承是一個物件導向資料庫的概念,它開啓了資料庫設計的更多可能性。
讓我們創建兩個資料表: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 的官方網站取得更多訊息。
回想一下在中的表格 weather 及 cities,思考下列問題: 你想要保證沒有另一個人可以新增在 cities 中沒有的城市資料到 weather 中。這就是所謂資料關連性的管理。在簡單的資料庫系統當中,可能會這樣實作:先檢查 cities 中是否已有對應的資料,然後再決定資料表 weather 中新增或拒絕新的天氣資料。這個辦法還有很多問題,而且很不方便,所以 PostgreSQL 可以幫助你解決這個需求。
新的資料表宣告如下所示:
現在嘗試新增一筆不合理的資料:
外部索引鍵或簡稱外部鍵(foreign key)的行為可以讓你的應用程式變得容易調整。我們在這個導覽中不會再深入這個簡單的例子了,但你可以在取得進一步的資訊。正確地使用外部索引鍵,可以改善資料庫應用程式的品質,所以強烈建議一定要好好學習它。
讓我們回到 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 是唯一能夠控制交易區塊執行流程的方式,當系統產生錯誤時,可以縮小回復的範圍,而不是只能全部回復再執行。