四巨頭第十一周作業 翻譯
通往T-SQL的階梯:超越基本的4級:使用視圖簡化你的查詢
系列
這篇文章是樓梯系列的一部分:T-SQL的階梯:超越基礎。
從他的階梯到T-SQL DML,格雷戈裏·拉森涵蓋了T-SQL語言的更高級的方面,比如子查詢。
在這個階梯級別,我將要討論如何使用數據庫視圖來簡化Transact-SQL (T-SQL)代碼。通過了解如何使用視圖,你將能夠更好地支持編寫T-SQL代碼以滿足復雜的業務需求。在本文中,我將討論數據庫視圖是什麽,然後提供一些示例來幫助你理解如何使用視圖來實現不同的編碼場景。
觀點是什麽?
視圖是由行和列組成的虛擬表。數據可以來自單個表,也可以來自多個表。查詢一個視圖,就像正常的表一樣。視圖是用CREATE view語句創建的,並存儲在創建它的數據庫中。
下面是一些視圖可以幫助你編寫代碼邏輯的情況:
l 你不希望將表的所有列公開給查詢該表的用戶。
l 你的數據庫模式設計非常復雜,因此您可以構建視圖來簡化用戶訪問。
l 你希望更改數據庫模式設計,但希望保持向後兼容性,因此不需要重寫現有代碼。
要更好地理解如何使用視圖,最好的方法是通過一些使用視圖的例子來滿足不同的業務需求。
樣本數據
為了演示視圖如何工作以及如何簡化T-SQL代碼,需要一些測試數據來支持這些視圖。與其創建我自己的測試數據,我的大多數示例將要使用AdventureWorks2008R2數據庫。如果你想在你的環境中跟蹤和運行我的示例,那麽你可以從這裏下載AdventureWorks2008R2數據庫:http://msftdbprodsamples.codeplex.com/releases/view/93587。
使用視圖簡化SQL代碼的示例
通過使用視圖,可以返回列的列表,這些列是表列的子集,一組來自多個表的列,一組基於某些標準的約束列,或者一些其他不同的需求。在本節中,我將提供一些不同的使用視圖來滿足不同業務需求的示例。
對於我的第一個示例,我們假設有一個要求,即不要將單個表中的所有列都呈現給應用程序或臨時查詢。對於本例,我們假設你只想從HumanResource返回非個人信息。Employee表如清單1所示。(註意這張表已經在AdventureWorks2008R2數據庫中出現了;這裏列出的定義僅供參考。
CREATE TABLE [HumanResources].[Employee](
[BusinessEntityID] [int] NOT NULL,
[NationalIDNumber] [nvarchar](15) NOT NULL,
[LoginID] [nvarchar](256) NOT NULL,
[OrganizationNode] [hierarchyid] NULL,
[OrganizationLevel] AS ([OrganizationNode].[GetLevel]()),
[JobTitle] [nvarchar](50) NOT NULL,
[BirthDate] [date] NOT NULL,
[MaritalStatus] [nchar](1) NOT NULL,
[Gender] [nchar](1) NOT NULL,
[HireDate] [date] NOT NULL,
[SalariedFlag] [dbo].[Flag] NOT NULL,
[VacationHours] [smallint] NOT NULL,
[SickLeaveHours] [smallint] NOT NULL,
[CurrentFlag] [dbo].[Flag] NOT NULL,
[rowguid] [uniqueidentifier] ROWGUIDCOL NOT NULL,
[ModifiedDate] [datetime] NOT NULL);
清單1:人力資源的表定義;員工表
應用程序和臨時用戶需要的非個人信息包括以下列:BusinessEntityId、NationalIDNumber、LoginID、OrganizationNode、OrganizationLevel、JobTitle和HireDate。
為了創建一個視圖,它只返回來自人力資源的列的子集。Employee表我將使用清單2中的代碼。
CREATE VIEW [HumanResources].[EmployeeInfo]
AS
SELECT [BusinessEntityID]
,[NationalIDNumber]
,[LoginID]
,[OrganizationNode]
,[OrganizationLevel]
,[JobTitle]
,[HireDate]
,[CurrentFlag]
FROM [HumanResources].[Employee];
清單2:從人力資源創建非個人信息視圖的腳本。員工表
通過查看清單2中的CREATE VIEW語句,你可以看到它非常簡單。視圖的代碼只是一個簡單的SELECT語句,其中包含了我希望在選擇條件中公開的視圖。一旦我創建了這個視圖,我就可以像普通表一樣查詢它。清單3中的腳本演示了兩個不同的SELECT語句,它們從人力資源中檢索數據。Employee表使用清單2中的代碼創建的視圖。
SELECT * FROM [HumanResources].[EmployeeInfo];
SELECT * FROM [HumanResources].[EmployeeInfo]
WHERE JobTitle like ‘%Manager%‘;
清單3:使用視圖返回數據的兩個SELECT語句。
通過查看清單3中的代碼,你可以看到在FROM子句後面引用的對象是我在清單2中創建的視圖的名稱。我在SELECT語句中引用了視圖,就像引用表一樣。清單3中的第一個SELECT語句返回了我的人力資源中的所有行。Employee表,但只返回我視圖中SELECT子句中的非個人列。清單3中的第二個SELECT語句表明,我可以使用WHERE語句來約束返回的行,就像在引用表時所做的那樣。
有時,數據庫設計相當復雜,這會使構建查詢變得復雜,從而訪問數據庫中所需的數據。這些復雜的設計可能需要復雜的多表連接來實際返回數據。這就是視圖可以提供幫助的地方。通過使用視圖,可以在視圖中構建復雜的多表連接,然後使用視圖來查詢數據。通過這樣做,可以簡化代碼來查詢數據庫,並在視圖中隱藏數據庫設計的復雜性。為了演示這一點,我創建了一個視圖清單4,它檢索包含在多個表中的銷售訂單數據。
CREATE VIEW SalesOrderCombined2005
AS
SELECT
OH.SalesOrderID
,OH.OrderDate
,OH.ShipDAte
,ST.Name AS TerritoryName
,BTA.City AS BillToCity
,STA.City AS ShiptToCity
,OH.TotalDue
FROM Sales.SalesOrderHeader OH
JOIN Sales.SalesTerritory ST
ON OH.TerritoryID = ST.TerritoryID
JOIN Person.Address BTA
ON OH.BillToAddressID = BTA.AddressID
JOIN Person.Address STA
ON OH.ShipToAddressID = STA.AddressID
WHERE YEAR(OH.OrderDate) = 2005;
清單4:包含多表連接的視圖。
清單4中的SalesOrderCombined2005視圖將多個表連接在一起,只返回這些表中的列的一個子集。另外,視圖有WHERE子句。WHERE子句只返回與2005年被放置的銷售訂單相關的數據。此視圖消除了理解如何使用不同的鍵列連接多個表的需要。通過執行針對SalesOrderCombined2005的SELECT語句,所有這些連接都是在不需要在SELECT語句中引用它們的情況下完成的。通過在視圖中放置復雜的連接語法,可以簡化從復雜數據庫設計中檢索數據的代碼。另外,這些類型的視圖確保對數據庫的所有查詢都使用相同的連接語法。通過提供和使用一個視圖來查詢數據,可以消除連接標準存在的可能性。
有些時候,希望隨著時間的推移發展數據庫設計,但是不希望破壞現有的代碼。視圖可以處理滿足這一業務需求。為了演示這一點,請查看清單5中的代碼。
--- Begin Old Schema
CREATE TABLE DateDimOld (
ID INT IDENTITY,
DT DATE,
DOWTitle varchar(10));
GO
-- Populate DateDimOld
INSERT INTO DateDimOld(DT, DOWTitle) VALUES
(‘12/1/2013‘,DATENAME(DW,‘12/1/2013‘)),
(‘12/2/2013‘,DATENAME(DW,‘12/2/2013‘)),
(‘12/3/2013‘,DATENAME(DW,‘12/3/2013‘));
GO
SELECT * FROM DateDimOld;
GO
--- End Old Schema
-- Begin New Schema
CREATE TABLE DOWTitle (
DowTitleID INT IDENTITY PRIMARY KEY,
DOWTitle VARCHAR(10));
GO
CREATE TABLE DateDimNew (
ID INT IDENTITY,
DT DATE,
DOWTitleID INT);
GO
ALTER TABLE DateDimNew WITH CHECK ADD CONSTRAINT [FK_DateDimNew_DOWTitle_DOWTitleID] FOREIGN KEY(DOWTitleID)
REFERENCES DOWTitle (DOWTitleID)
GO
-- Populate DOWTitle
INSERT INTO DOWTitle (DOWTitle) VALUES
(DATENAME(DW,‘12/1/2013‘)),
(DATENAME(DW,‘12/2/2013‘)),
(DATENAME(DW,‘12/3/2013‘));
GO
-- Populate DateDimNew
INSERT INTO DateDimNew (DT,DOWTitleID) VALUES
(‘12/1/2013‘, 1),
(‘12/2/2013‘, 2),
(‘12/3/2013‘, 3);
GO
-- Remove Old Schema
DROP TABLE DateDimOld
GO
-- Create view to similate Old Schema
CREATE VIEW DateDimOld AS
SELECT DDN.ID, DDN.DT, DOWT.DOWTitle
FROM DateDimNew AS DDN
JOIN DOWTitle AS DOWT
ON DDN.DOWTitleID = DOWT.DowTitleID;
GO
-- Show how VIEW and Simulate Old Schema
SELECT * FROM DateDimOld
-- End New Schema
清單5:新舊模式結構。
通過回顧清單5中的代碼,可以看到代碼有兩個不同的部分。在第一節中,我定義、填充並顯示了一些來自舊模式的數據,其中有一個名為datedimodel的表。這個表包含一個名為DT的日期列,以及一個名為DOWTitle的星期列,並將這些列關聯到ID列。在第二部分中,我定義了一個新的模式來替換第一部分中的舊模式。在第二部分中,我創建了兩個表。第一個表名為DOWTitle,其中包含DOWTitle和DOWTitleID列。第二個表名為DateDimNew。此表包含ID、DT和DOWTitleID列。DOWTitleID列是進入DOWTitle表的外鍵列。這個新模式是一個規範化的模式,而舊的模式是一個非規範化的模式。在代碼的第二部分中,我實際上刪除了在第一部分代碼中創建的表,並創建了一個同名的視圖,datedimodel。datedimodel視圖允許我查詢新的規範化模式,就像在舊模式中查詢datedimodel表一樣。這個新的視圖數據模型允許我為我可能構建的使用舊模式設計的代碼提供向後兼容性。
正如你所看到的,可以使用許多不同的方法視圖。在我的例子中,我只展示了從視圖中選擇數據。視圖也可以用來更新表。此外,在創建視圖時還可以使用其他選項。
更新視圖的基礎表
視圖也可以用來更新表中的數據。為了演示這一點,我將運行清單6中的代碼。
INSERT INTO DateDimOld (DOWTitle)
VALUES (DATENAME(DW,‘12/4/2013‘));
清單6:使用視圖將數據插入到基礎表中。
清單6中的代碼並沒有真正更新datedimodeltable(已被刪除),而是更新底層表DOWTitle,這是datedimodel的視圖定義的一部分。在運行清單6中的INSERT語句之後,在DOWTitle表中創建了一個row,其中包含DOWTitle列中的“周三”值。由於datedimodel是我的規範化日期維度表的視圖,所以我需要在表DateDimNew中放置另一行,以便查看datedimodel以顯示“周三”值。為此,我運行清單7中的代碼。
INSERT INTO DateDimNew (DT, DOWTitleID)
SELECT ‘12/4/2013‘, DOWTitleID FROM DOWTitle
WHERE DOWTitle = DATENAME(DW,‘12/4/2013‘);
清單7:向DateDimNew表添加一行。
因為列DOWTitleID不是datedimodel視圖的一部分,所以我不能使用視圖來更新DateDimNew表。相反,我必須編寫清單7中的代碼,直接引用底層視圖表。
使用視圖更新視圖的基礎表有一些限制。這裏有那些限制:
l 視圖中只有一個底層表可以被更新。
l 正在更新的列必須在視圖中直接引用,而不需要對它們進行任何計算。
l 被修改的列不能被一個組、不同的或有子句影響。
l 當使用CHECK選項時,你的視圖不包含TOP子句。
有關限制的更多信息,請參閱聯機文檔。
確保視圖不受其他表更改或更新的影響
在我已經向你展示的CREATE VIEW語句中,所創建的視圖不會限制你對底層表所做的操作。你可以對基礎表進行一些更改,以便查看可能會破壞視圖的視圖,或者返回意外的結果。一個這樣的更改會破壞一個視圖,即刪除一個視圖引用的列。有些情況下,你可能想要確保你的觀點不受這些問題的影響。當創建一個視圖時,你可以在create視圖或SELECT語句中添加一些額外的子句,以幫助消除這些惱人的潛在問題。
你可以做的第一件事是綁定你的視圖底層的表模式。通過將表綁定到底層模式,可以限制任何可能會破壞視圖的表更改。為了演示,讓我運行清單8中的代碼。
ALTER VIEW DateDimOld WITH SCHEMABINDING AS
SELECT DDN.ID, DDN.DT, DOWT.DOWTitle
FROM dbo.DateDimNew AS DDN
JOIN dbo.DOWTitle AS DOWT
ON DDN.DOWTitleID = DOWT.DowTitleID;
GO
清單8:使用模式綁定創建視圖。
在清單8中,我刪除並重新創建了datedimodel視圖。當我重新創建它時,我添加了SCHEMABINDING子句。這創建了一個模式綁定視圖。當我做出這個改變時,我還需要稍微修改一下視圖中的SELECT語句。我所做的更改是為所有表有兩個部分的名稱。建議你在引用SQL Server表時總是使用兩部分命名,而不管SQL Server在技術上是否需要它。這個要求意味著我必須添加“dbo”。在我的原始視圖的兩個表名前面。除此之外,這一觀點與最初的觀點完全一致。
為了說明模式綁定如何限制我對底層表的操作,讓我運行清單9中的代碼。
ALTER TABLE dbo.DateDimNew
ALTER COLUMN DT INT;
清單9:嘗試使用模式綁定修改表。
當運行清單9中的代碼時,我將得到報告1中顯示的錯誤。
Msg 5074, Level 16, State 1, Line 1
The object ‘DateDimOld‘ is dependent on column ‘DT‘.
Msg 4922, Level 16, State 9, Line 1
ALTER TABLE ALTER COLUMN DT failed because one or more objects access this column.
報告1:更改模式綁定視圖的列時收到的錯誤。
通過檢查報告1中的輸出,您可以看到數據庫引擎阻止了我修改DT列,它包含在視圖定義中。通過創建一個模式綁定視圖,我確保某人不會出現並修改可能影響我的datedimodel視圖的任何部分。
創建視圖的另一個選項是with CHECK選項。帶有CHECK選項的選項允許您對視圖進行約束,以確保對基礎表進行的任何更新都可以使用視圖進行選擇。為了向您展示使用CHECK選項的方式,請查看清單10中的代碼。
CREATE TABLE DayOfTheWeek(DayOfTheWeek varchar (10),
DayOfTheWeekNum int);
INSERT INTO DayOfTheWeek VALUES
(‘Monday‘,0),
(‘Tuesday‘,1),
(‘Wednesday‘,2),
(‘Thursday‘,3),
(‘Friday‘,4);
GO
CREATE VIEW DisplayDayOfTheWeek
AS
SELECT DayOfTheWeek, DayOfTheWeekNum FROM DayOfTheWeek
WHERE DayOfTheWeekNum < 5
WITH CHECK OPTION;
清單10:使用CHECK選項創建視圖。
在清單10的代碼中,您可以看到我創建了一個表並填充了一個名為DayOfTheWeek的表。我還創建了一個名為DisplayDayOfTheWeek的視圖,它限制了使用WHERE子句返回的天數,並添加了WITH CHECK選項。通過添加WITH CHECK選項,SQL Server將不允許我使用DisplayDayOfTheWeek視圖插入或更新一個行,除非weeknum值小於5。為了測試這個,我可以運行清單11中的代碼。
INSERT INTO DisplayDayOfTheWeek VALUES
(‘Saturday‘,5);
UPDATE DisplayDayOfTheWeek
SET DayOfTheWeekNum = 5
WHERE DayOfTheWeek = ‘Friday‘;
清單11:用CHECK選項測試代碼。
當清單11中的代碼嘗試插入一個值大於5的新行,或者將我現有的周五行更新為大於5的DayOfTheWeekNum值時,我得到了報告2中所示的錯誤。實際上,清單11中的代碼將兩次生成此消息,一次用於插入,一次用於更新。
The attempted insert or update failed because the target view either specifies WITH CHECK OPTION or spans a view that specifies WITH CHECK OPTION and one or more rows resulting from the operation did not qualify under the CHECK OPTION constraint.
The statement has been terminated.
報告2:用CHECK選項測試代碼。
通過查看消息,您可以看到帶有CHECK選項的消息,導致我的INSERT和UPDATE語句在清單11中失敗。如果您想實際插入或更新這些行,您有兩個選項。一個選項是用CHECK選項刪除。這將允許您通過視圖更改基礎表,但是從視圖中選擇仍然不會顯示那些符合視圖定義條件的值。如果您想要插入和更新這些行,並讓視圖顯示它們,那麽第二個選項將是更改視圖中的WHERE條件,以允許選擇新的值。(請記住,使用CHECK選項只適用於通過視圖進行的更改;is不阻止直接向底層表的更新或插入。
如果您想要控制可能影響您的視圖的語句類型,那麽您應該考慮使用模式綁定和/或WITH CHECK選項。
使用視圖時的性能考慮。
使用視圖有性能問題嗎?與大多數SQL Server問題一樣,答案是“看情況”。
視圖的性能取決於視圖在做什麽。一個簡單的視圖,它讀取一個沒有連接子句的表,它很可能與只引用單個表的查詢執行類似的操作。但是,如果您有一個視圖,它引用了一個引用視圖的視圖,而這些視圖包含多個連接子句,該怎麽辦呢?通過簡單的SELECT語句實際執行的底層查詢可能會被擴展成一個非常復雜的SELECT語句,其中包含多個連接子句,並且可能會比您預期的做更多的工作。
其他值得一提的關於視圖性能問題的東西是,當一個視圖包含多個表連接在一起時,但您只想從視圖中的單個表返回數據。在這種情況下,SQL Server仍然需要連接視圖中的所有表,以從單個表返回數據。這可能導致SQL Server的額外工作加入到視圖中所有的表中,而對於那些只希望從視圖中的單個表返回數據的查詢的響應時間較慢。如果您發現只從一個表中返回數據,並且性能很重要,那麽您最好將查詢寫在單個表上,而不是使用包含多個表連接的視圖。
CVews是一種簡化代碼並隱藏數據庫模式復雜性的好方法。但是隱藏這種復雜性會導致嚴重的性能問題。如果您打算使用視圖,請確保您知道視圖在幕後做什麽。理解查詢引擎必須執行的工作,以執行針對您的視圖的查詢將幫助您開發性能良好的代碼。
使用視圖保護數據。
人們使用視圖的另一個原因是訪問表中的某些列。假設您有一個業務需求,允許用戶對包含機密數據的表進行報告,比如社會保險號或信用卡號。您可能不希望他們訪問這些機密列。確保他們不能讀取這些機密數據列的一種方法是創建一個表的視圖,該視圖不包括那些機密列,並且不提供用戶在底層表上的選擇權限。
總結
視圖是實現安全性、簡化查詢復雜數據庫模式和/或提供向後功能的好方法。但是,如果您在不理解這可能導致的性能影響的情況下開始嵌套視圖,那麽就會有一個負面的視圖。當您看到一個給定的業務需求需要一個T-SQL解決方案時,請將視圖視為您可能能夠用於實現解決方案的眾多工具中的一個。
問答
在本節中,您可以通過回答下列問題,來回顧您對使用視圖查詢數據庫的理解程度。
問題1
一個視圖可以幫助您實現哪些好的業務需求?
需要保持應用程序或特定查詢訪問表中的底層列。
需要簡化查詢復雜數據庫結構所需的代碼。
需要提供向後兼容性。
上面所有的
問題2
你需要確保當一個列值被更新或插入時,通過視圖可以選擇它。哪個子句提供了這個功能?
創建視圖
與SCHEMABINDING
檢查選項
以上都不是
問題3
您需要在表中限制對機密數據的訪問。可以使用什麽方法來限制對這些數據的訪問?
使用WITH CHECK選項創建視圖。
創建一個使用帶有SCHEMABINDING選項的視圖。
創建一個視圖,該視圖不包含表中的機密列,並且不能被證明選擇訪問該表。
創建一個視圖,該視圖排除表中的機密列,並證明選擇訪問表。
答案
問題1
答案是d。在直接查詢中使用視圖有很多原因。a、b和c是其中的一些原因。
問題2
正確的答案是c. CREATE VIEW不是提供任何額外數據完整性檢查的子句。WITH SCHEMABINDING子句確保任何ALTER TABLE語句不會在視圖的底層表結構發生更改時引起視圖問題。它是帶有CHECK選項的,它確保您不能更新基礎表,除非更改立即可以使用視圖進行查詢。
問題3
正確答案是c.答案a和b沒有特別限制對機密列的訪問,因為他們沒有提到從視圖中排除機密列。答案d是不正確的,因為如果人們可以訪問包含機密數據的底層表,那麽他們仍然可以通過編寫直接針對表的查詢來選擇機密列。
四巨頭第十一周作業 翻譯