前言
上一節我們結束了Hash Match Aggregate和Stream Aggregate的講解,本系列我們來講講關於SQL Server中的計算列問題,簡短的內容,深入的理解,Always to review the basics。
初探計算列持久化(Compued Column Persisted)
在SQL Server 2005就引入了計算列,我們首先稍微看下在msdn關於計算列的定義:計算列由可以使用同一表中的其他列的表達式計算得來。表達式可以是非計算列的列名、常量、函數,也可以是用一個或多個運算符連接的上述元素的任意組合。表達式不能為子查詢。實際上就是為了定義一個列來對其他列來進行計算可以是列名、函數等,那么它的使用場景是什么呢?下面我們首先來舉個例子。當需要導出一些值時,此時這些值需要通過計算才能被導出,同時呢,有一些列還依賴於另外的一列或者更多列,如果一個列進行了更新則其依賴的列必須同步進行更新,上述場景通過對一個列或者多個列進行計算,此時我們需要定義一個將一個列或者多個列進行計算得到的值的列,這就是計算列。我們來看一個典型的例子,在一個公司上班的所有員工,在公司內部系統中會存其所有員工的信息,比如員工編號、出生日期等,如果此時我們需要導出員工的退休日期呢,假設在中國現在男性退休時間為60年后,此時我們需要通過出生日期算出60年后的日期,也就說在表中還需要定義一個退休日期列。下面我們創建表來看看計算列。
USE TSQL2012 GO CREATE TABLE Employee ( employeeNumber INT NOT NULL, --員工編號 employeeBirth DATETIME NOT NULL, --出生日期 employeeRetirement AS (DATEADD(YEAR, 60, (employeeBirth)-(1))) PERSISTED --退休日期 )
此時我們看到表中關於退休日期的設計,顯示其已經是持久化了的
接下來我們插入測試數據看看
USE TSQL2012 GO INSERT INTO dbo.Employee( employeeNumber, employeeBirth ) SELECT 305423 ,'1985-12-13' UNION ALL SELECT 587650 ,'1989-11-18' UNION ALL SELECT 221836 ,'1990-01-19' UNION ALL SELECT 746104 ,'1993-06-13' UNION ALL SELECT 139024 ,'1995-07-23'
然后我們來查詢表
USE TSQL2012 GO SELECT * FROM dbo.Employee
此時我們通過查詢雇員表得到其每個雇員的退休日期,到這里是沒什么問題的,既然我們設置它是持久化的,也就說當其他列發生改變時計算列也會對應發生改變,突然有一天編號為305423的雇員和錄入信息的同事交流,他其實是1986年出生的,上面的1985年是身份證上的,身份證搞錯了,此時我們需要更新其出生日期到1986年,如下
UPDATE dbo.Employee SET employeeBirth = '1986-12-13' WHERE employeeNumber = '305423'
接下來我們再來查詢數據看看。
此時我們發現當出生日期發生修改時,其對應的計算列也進行了同步由原來的2045更新到了2046,上述我們添加在計算列中添加了Persisted關鍵字,是不是因為添加這個關鍵字導致持久化從而當一個列進行更新時,計算列也就同步更新了呢,難道這就是Persisted持久化的作用嗎,實際情況不是這樣的,當你去掉Persisted關鍵字此時也會進行同步更新(不信你可以試試),那么Persisted關鍵字的作用是什么呢?事實情況是這樣的,當我們在列上創建了計算列時,此時計算出來的數據並沒有存在列中(至於存在哪里我也不知道),計算的數據是在運行時計算出來的,當用Persisted關鍵字標識計算列之后,這個時候才是將計算結果存在表中計算列上。繼續往下看數據存儲空間使用情況就可以得到驗證。
通過計算列持久化進一步探討數據存儲空間
下面我們來看看當未添加計算列、添加計算列、計算列持久化時表數據存儲空間情況。下面我們來創建測試表
USE TSQL2012 GO CREATE TABLE [dbo].[ComputeColumn] ( ID INT, FirstName VARCHAR(100), LastName VARCHAR(100) ) GO
在表中插入10萬條數據
INSERT INTO [ComputeColumn] (ID,FirstName,LastName) SELECT TOP 100000 ROW_NUMBER() OVER (ORDER BY a.name) RowID, 'Bob', CASE WHEN ROW_NUMBER() OVER (ORDER BY a.name)%2 = 1 THEN 'Smith' ELSE 'Brown' END FROM sys.all_objects a CROSS JOIN sys.all_objects b GO
此時我們來看看有關表存儲空間使用情況
USE TSQL2012 GO sp_spaceused '[ComputeColumn]' GO
上述我們得知存儲數據為2680KB,下面我們再來創建計算列看看。
ALTER TABLE dbo.[ComputeColumn] ADD FullName AS (FirstName+' '+LastName) GO
從這里我們可以得出當創建計算列時其數據根本沒有存在列上,我們再來看看添加持久化關鍵字時情況又是怎樣的呢
ALTER TABLE dbo.[ComputeColumn] ADD FullName_P AS (FirstName+' '+LastName) PERSISTED GO
當添加持久化關鍵字時此時表存儲數據空間變為了4784KB,到此驗證了當未添加Persisted關鍵字時,在計算列上的數據根本沒有存在列上而是在運行時進行了計算,當用Persisted關鍵字標識計算列時此時數據才存在列上。
通過計算列持久化深入探討數據存儲空間
我們知道如果對列創建索引的話肯定需要一定空間來存儲索引,上述我們對列進行了持久化,此時會增加表存儲空間,要是我們創建索引是不是會增加表數據存儲空間大小呢?我們在未創建計算列前先創建索引看看其表中各種數據空間存儲大小,即在創建的列FullName上創建索引。
USE TSQL2012
GO
CREATE NONCLUSTERED INDEX idx_comCol_FullName
ON dbo.ComputeColumn (FullName)
因為創建了索引,所以只是導致索引空間變大了,下面我們再創建計算列持久化並看看其表空間使用情況
USE TSQL2012 GO ALTER TABLE dbo.ComputeColumn ADD FullName_P AS (FirstName+' '+LastName) PERSISTED
從上我們可以看到增加索引未導致表數據大小的增加,而創建計算列持久化則需要額外的空間。分析到這里為止,我們來給出一個基本結論:
計算列分析結論:計算列的用途主要用於多個計算並且比較復雜的計算,如果對計算列進行持久化雖然能夠大大減少計算開銷但是它會額外增加磁盤空間。
總結
本節我們學習了計算列以及將其持久化的基礎內容,下一節我們講講關於計算列以及計算列持久化的性能問題,簡短的內容,深入的理解,我們下節再會。