在SQL Server中,SQL語句的執行是依賴查詢優化器生成的執行計划,而執行計划的好壞直接關乎執行性能。
在查詢優化器生成執行計划過程中,需要參考元數據來盡可能生成高效的執行計划,因此元數據越多,則執行計划更可能會高效。所謂需要參考的元數據主要包括:索引、表結構、統計信息等,但還有一些不是很被注意的元數據,其中包括本文闡述的Check約束。
查詢優化器在生成執行計划之前有一個階段叫做代數樹優化,比如說下面這個簡單查詢:
圖1.簡單查詢
查詢優化器意識到1=2這個條件是永遠不相等的,因此不需要返回任何數據,因此也就沒有必要掃描表,從圖1執行計划可以看出僅僅掃描常量后確定了1=2永遠為false后,就可完成查詢。
那么Check約束呢
Check約束可以確保一列或多列的值符合表達式的約束。在某些時候,Check約束也可以為優化器提供信息,從而優化性能,比如看圖二的例子。
圖2.有Check約束的列提升查詢性能
圖2是一個簡單的例子,有時候在分區視圖中應用Check約束也會提升性能,測試代碼如下:
CREATE TABLE [dbo].[Test2007](
[ProductReviewID] [int] IDENTITY(1,1) NOT NULL,
[ReviewDate] [datetime] NOT NULL
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Test2007] WITH CHECK ADD CONSTRAINT [CK_Test2007] CHECK (([ReviewDate]>='2007-01-01' AND [ReviewDate]<='2007-12-31'))
GO
ALTER TABLE [dbo].[Test2007] CHECK CONSTRAINT [CK_Test2007]
GO
CREATE TABLE [dbo].[Test2008](
[ProductReviewID] [int] IDENTITY(1,1) NOT NULL,
[ReviewDate] [datetime] NOT NULL
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Test2008] WITH CHECK ADD CONSTRAINT [CK_Test2008] CHECK (([ReviewDate]>='2008-01-01' AND [ProductReviewID]<='2008-12-31'))
GO
ALTER TABLE [dbo].[Test2008] CHECK CONSTRAINT [CK_Test2008]
GO
INSERT INTO [Test2008] values('2008-05-06')
INSERT INTO [Test2007] VALUES('2007-05-06')
CREATE VIEW testPartitionView
AS
SELECT * FROM Test2007
UNION
SELECT * FROM Test2008
SELECT * FROM testPartitionView
WHERE [ReviewDate]='2007-01-01'
SELECT * FROM testPartitionView
WHERE [ReviewDate]='2008-01-01'
SELECT * FROM testPartitionView
WHERE [ReviewDate]='2010-01-01'
代碼清單1.
我們針對Test2007和Test2008兩張表結構一模一樣的表做了一個分區視圖。並對日期列做了Check約束,限制每張表包含的數據都是特定一年內的數據。當我們對視圖進行查詢並給定不同的篩選條件時,可以看到結果如圖3所示。
圖3.不同的條件產生不同的執行計划
由圖3可以看出,當篩選條件為2007年時,自動只掃描2007年的表,2008年的表也是同樣。而當查詢范圍超出了2007和2008年的Check約束后,查詢優化器自動判定結果為空,因此不做任何IO操作,從而提升了性能。
結論
在Check約束條件為簡單的情況下(指的是約束限制在單列且表達式中不包含函數),不僅可以約束數據完整性,在很多時候還能夠提供給查詢優化器信息從而提升性能。