淺談計算欄位(Computed Column)的PERSISTED引數

相信很多情境下會在資料表的欄位設計上用到所謂的計算欄位(Computed Column),顧名思義該欄位存放的值就是經過計算的一個結果。然而計算欄位的資料內容一般常會是取出資料後即時運算並顯示結果,但有時候也會因為某些需求希望能將運算結果真實寫入在page中,而這個需求只要在建立資料欄位時加入一個關鍵字Persisted即可。

 

微軟對於關鍵字Persisted的說明是:
指定 Database Engine 會實際將計算值儲存在資料表中,以及在計算資料行所依賴的任何其他資料行有了更新時,也會更新這些值。 將計算資料行標示為 PERSISTED,可讓您在具決定性但不精確的計算資料行上建立索引。 如需詳細資訊,請參閱 計算資料行的索引。 任何用為分割資料表之分割資料行的計算資料行,都必須明確標示為 PERSISTED。 指定 PERSISTED 時,computed_column_expression 必須具決定性。

 

Rock在某一個專案中碰見一張表中有同時存在民國日期及西元日期的需求,然而AP寫入一筆資料時需寫完西元日期又轉碼成民國日期寫入著實會讓PG們抓狂。因此就想到PG寫入時只需針對西元日期欄位寫入資料,而民國日期欄位就改用計算欄位來做。然而我到底要不要將轉換出來的民國日期實際寫到page中呢? 寫與不寫在效能上會不會有明顯的差異呢? 所以我就做了一個小實驗測試看看。

如下圖所示我建了兩張資料表tb_PERSISTED及tb_NOPERSISTED,這兩張資料表有兩個計算欄位chitimeS及chitimeE,這兩個欄位就是存放西元日期轉換後的民國日期(7碼,例:1071123)。而tb_PERSISTED資料表有加入Persisted關鍵字,讓計算出的資料在insert時就寫入page。而tb_NOPERSISTED就不寫入page,所以是取資料時再即時計算顯示欄位內容。

 

接下來我在兩張資料表寫入兩筆資料,然後我們再實際到page頁面中比較差異處。

 

首先我們先看一下tb_NOPERSISTED頁面上的資料內容,如下圖紅色圈選處,該頁面一筆資料只記錄了三個欄位的內容。chitimeS及chitimeE的資料並沒有被實體寫到page中

 

 

再來我們看一下tb_PERSISTED頁面上的資料內容,該頁面一筆資料記錄了五個欄位的內容。chitimeS及chitimeE的資料被實體寫到page中了,如下圖紅色圈選處

 

接下來我們做個簡單的測試看看這兩張資料表在對於大量資料的寫入及讀取會有甚麼樣不同的表現呢?


首先我在tb_NOPERSISTED資料表一次寫入26萬筆左右的資料,然後我們看看其IO跟CPU的表現。IO為263346個邏輯IO,CPU則用了406ms

 

接下來我在tb_PERSISTED資料表一次寫入26萬筆左右的資料,然後我們看看其IO跟CPU的表現。IO為263927個邏輯IO,CPU則用了1141ms

上面測試中在大量Insert資料時tb_PERSISTED不管在IO上或CPU的使用度都高於tb_NOPERSISTED。
 

接下來我測試對兩張表做大量讀取,看看其IO跟CPU的差異又是如何。 

首先我對tb_NOPERSISTED一次讀取26萬筆資料,IO為1203個邏輯讀取,CPU耗費為375ms

 

再來我對tb_PERSISTED一次讀取26萬筆資料,IO為1784個邏輯讀取,CPU耗費為78ms

經過簡易的測試我們可以知道將計算欄位內容實際寫入page的動作,在Insert實會耗用較多的CPU資源(計算內容)及IO資源(多了需要記錄的資料),但其讀取計算欄位資料時則耗用較少的CPU資源(因為少掉了計算)

相反的沒將計算欄位內容實際寫入page的,在Insert實會耗用較少的CPU資源(不用計算)及IO資源(不用實體記錄的資料),但其讀取計算欄位資料時則耗用較多的CPU資源(因為需即時計算)

 

 

 

我是ROCK

rockchang@mails.fju.edu.tw