翻译10

 到T-SQL的楼梯:等级4的基础知识:使用视图简化查询

本系列本文是楼梯系列的一部分:楼梯到T-SQL:超越基础,从他的楼梯到T-SQL DML。Gregory Larsen涵盖了T-SQL语言的更高级的术语,如子查询。

在这个层次上,我将讨论如何使用数据库视图来简化Transact-SQL(T-SQL)代码,通过了解如何使用视图,您将能够更好地支持编写T-SQL代码以满足复杂的业务需求。在本文中,我将讨论什么是数据库视图,然后提供一些示例来帮助您理解如何使用视图实现不同的编码方案。

视图是什么

视图是由行和列组成的虚拟表。数据可以来自一个单个表,也可以来自多个表。查询视图就像查询普通表一样使用CREATEVIEW语句创建视图,并将其存储在创建视图的数据库中

这是一些情况,其中视图可以帮助您的编码逻辑:

·您不希望向查询表的用户公开表的所有列

·您的数据库架构设计很复杂,因此您可以构建视图以简化用户访问。

·您希望更改数据库架构设计,但希望保持向后兼容性,这样就不必重写现有代码。

最好的方式来更好地理解如何使用视图是通过一些视图来满足不同的业务隐退。

使用视图来执行sql代码的分类示例

通过使用视图,您可以返回作为表列子集的列表、来自多个表的一组列、基于某些WHERE条件的一组受约束的列,或许多其他不同的要求。在本节中,我将为您提供许多不同的示例,说明如何使用视图来满足不同的业务需求。

对于我的第一个示例,让我们假设您有一个要求,即不将单个表中的所有列呈现给应用程序或临时查询。对于这个例子,让我们假设您只想从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:HumanResources.Employee表的表定义

应用程序和临时用户需要的非个人信息如下:BusinessEntityld、NationaIIDNumber、LoginID。为了创建一个只从HumanResources.Employee表返回列子集的视图,我将使用清单2中的代码。

CREATE VIEW [HumanResources].[EmployeeInfo]

AS

SELECT [BusinessEntityID]

      ,[NationalIDNumber]  

      ,[LoginID]  

      ,[OrganizationNode]  

      ,[OrganizationLevel]

      ,[JobTitle]

      ,[HireDate]  

      ,[CurrentFlag]  

    FROM [HumanResources].[Employee];

清单2:从HumanResources.Employee表创建非个性化信息视图的脚本

通过查看清单2中的CREATEVIEW语句,您可以看到它非常简单。视图的代码只是一个简单的SELECT语句,它包含了我希望视图在选择条件中公开的列。一旦我创建了这个视图,我就可以像普通的表一样查询它。清单3中的脚本演示了两个不同的SELECT语句,它们使用我用清单2中的代码创建的视图从HumanResources.Employee表中检索数据。

SELECT * FROM [HumanResources].[EmployeeInfo];

SELECT * FROM [HumanResources].[EmployeeInfo]

WHERE JobTitle like '%Manager%';

 清单3:返回数据使用视图的两个SELECT语句

通过查看清单3中的代码,您可以看到在FROM子句之后引用的对象是我在清单2中创建的视图的名称。我引用了SELECT语句中的视图,就像引用表一样清单3中的第一个SELECT语句返回了HumanResources.Employee表中的所有行,但只返回了我视图中SELECT子句中的那些非个人列清单3中的第二个SELECT语句演示了我可以使用WHERE语句约束返回的行,就像引用时一样有时数据库设计相当复杂,这可能会使构建查询变得复杂,从而访问数据库中所需的数据。

这些复杂的设计可能需要复杂的多表连接才能真正返回数据。这是一个视图可以帮助你的地方。通过使用视图,您可以在视图中构建复杂的多表连接,然后使用视图查询数据,这样可以简化查询数据库的代码,并在视图中隐藏数据库设计的复杂性,以演示我已经创建了一个视图清单4,它检索了包含在Multiole表中的销售订单数据。

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中的SalesOrderCombined 2005视图将多个表连接在一起,并且只从这些表返回一个列的子集。此外,视图还有WHERE子句。WHERCCEASE只返回与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中的代码,您可以看到,我定义的代码节有两个不同的部分,它们填充并显示了一个旧模式中的一些数据,该旧模式只有一个名为DateDimold的选项卡。此表包含名为DT的日期列和名为DOWTitle的“一周中的一天”列,并将这些列关联到ID列。在第二节中,我定义了一个新模式,以取代第一节中的旧模式。在第二节中,我创建了两个表。第一个表名为DOWT梭尔,它包含DOWTitle和DOWTitlelD列。第二个表名为DateDimNew。此表包含ID、DT和DOWTitleID列。DOWTitleID列是DOWTitle表中的外键列。这个新模式是一个规范化的模式,而OTD模式是一个非规范化的模式-我实际上删除了在tne第一段代码中创建的表,并创建了一个同名为DateDimold的视图。DateDimold视图允许我查询新的规范化模式,就像我会查询旧模式的DateDimOtd选项卡一样-新视图DateDimold允许我为可能使用旧模式设计构建的任何代码提供向后兼容性。

正如您所看到的,在我的示例中可以使用视图的许多不同方式,我只向您展示了从视图中选择数据。视图也可以用于更新表。此外,在创建视图时还可以使用其他的输出。

 

更新视图的基础表

视图也可以用于更新表中的数据。为了证明这一点,我将运行6中的代码。

INSERT INTO DateDimOld (DOWTitle)

VALUES (DATENAME(DW,'12/4/2013'));

 

6中的代码并不真正更新 DateDimOld表,而是更新 DOWTitle基础表这是的DateDimOld视图定义 的一部分。运行6中的INSERT语句后,在DOWTitle中包含值“星期三”的列。自从DateDimOld是规范化日期维表的视图,我还需要在表中放置另一行日期更新为了DateDimOld视图显示“星期三”值。为此,我运行7中的代码。

INSERT INTO DateDimNew (DT, DOWTitleID)

   SELECT '12/4/2013', DOWTitleID FROM DOWTitle

       WHERE DOWTitle = DATENAME(DW,'12/4/2013');

 

因为专栏同标题不是约会视图无法使用视图更新日期更新表。相反,我必须编写7中的代码来直接引用底层视图表。

使用视图更新视图的基础表有一些限制。以下是这些限制:

只能更新视图中的单个基础表

必须在视图中直接引用要更新的列,而不对它们进行任何计算

要修改的列不得受GROUP BY子句、DISTINCT子句或HAVING子句的影响

使用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中,我删除并重新创建了DateDimOld视图。当我重新创建它时,我添加了带SCHEMABINDING子句。这创建了架构绑定视图。进行更改时,我还必须在视图中稍微修改SELECT语句。我所做的更改是所有表都有两个部分名称。建议在引用SQL Server表时始终使用两部分命名,而不管SQL Server在技术上是否需要。这个要求意味着我必须添加“dbo”。“前面两张桌子的名字在我原来看来。除此之外,这种观点与原来完全一样。

为了说明模式绑定如何限制对基础表的操作,让我运行9中的代码。

ALTER TABLE dbo.DateDimNew

  ALTER COLUMN DT INT;

 

当9中的代码运行时,我得到了Report 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中的输出,您可以看到数据库引擎阻止我修改视图定义中包含的DT列。通过创建模式绑定视图,我确保不会有人出现并修改可能影响我的约会查看。

创建视图的另一个可用选项是“带检查”选项。WITH CHECK选项允许您对视图设置约束,以确保使用视图可以选择对基础表所做的任何更新。要显示如何使用WITH 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中的代码中,你可以看到我创建了一个表并填充了一个名为DayOfTheWeek.。我还创建了一个名为的视图显示日期和时间这限制了使用WHERE子句返回的日期,我添加了带检查选项。通过添加WITH CHECK选项,SQL Server将不允许我使用插入或更新行显示日期和时间除非大日和值小于5。为了测试这一点,我可以运行11中的代码。

INSERT INTO  DisplayDayOfTheWeek VALUES

    ('Saturday',5);

UPDATE DisplayDayOfTheWeek

SET DayOfTheWeekNum = 5

WHERE DayOfTheWeek = 'Friday'

 

当11中的代码试图插入一个值大于5的新行,或将我现有的星期五行更新为DayOfTheWeekNum数量值大于5时,我得到报告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.

 

通过查看该消息,你可以看到WITH CHECK选项导致11中的my INSERT和UPDATE语句失败。如果要实际插入或更新这些行,您有两个选项。其中一个选项是删除“带检查”选项。这将允许您通过视图更改基础表,但从视图中选择仍不会显示满足视图定义中条件的值。如果要插入和更新这些行并让视图显示它们,则第二个选项是更改视图中的WHERE条件,以允许选择新值。(请记住,WITH CHECK选项仅适用于通过视图所做的更改;不阻止直接对基础表进行更新或插入。

如果要控制可能影响视图的语句类型,则应考虑使用模式绑定和/或WITH CHECK选项。

 

使用视图时的性能注意事项

使用视图是否存在性能问题?与大多数SQL Server问题一样,答案是“取决于”。

视图的性能将取决于视图正在执行的操作。读取不带联接子句的单个表的简单视图很可能与仅引用单个表的查询执行非常相似。但是,如果您有一个视图引用了一个视图,并且这些视图包含多个联接子句,该怎么办?引用视图的简单SELECT语句实际执行的底层查询可能会分解为具有多个联接子句的非常复杂的SELECT语句,最终可能会执行比预期更多的工作。

关于视图的性能问题,还有一点值得一提的是,当视图包含多个连接在一起的表,但你只希望从视图中的单个表返回数据时。在这种情况下,SQL Server仍然必须连接视图中的所有表才能从单个表返回数据。这可能会导致SQL Server加入视图中所有这些表的额外工作,以及那些只希望从视图中的单个表返回数据的查询的响应时间变慢。如果发现只从视图中的单个表返回数据,并且性能很重要,则最好编写查询以与单个表相对应,而不是使用包含多个表的联接的视图。

CVews是一种简化代码和隐藏数据库模式复杂性的好方法。但是隐藏这种复杂性会导致严重的性能问题。如果您计划使用视图,请确保您知道视图在幕后执行的操作。了解查询引擎必须执行的针对视图的查询工作将有助于开发性能良好的代码。

 

使用视图保护数据

人们使用视图的另一个原因是保护对表中某些列的访问。假设您有一项业务要求,允许用户针对包含机密数据的表(如社会保险号或信用卡号)进行报告。您可能不希望他们访问这些机密列。确保他们无法读取这些机密数据列的一种方法是创建排除这些机密列的表视图,并且不向用户提供对基础表的SELECT权限。

 

摘要

视图是实现安全性、简化查询复杂数据库模式和/或提供向后功能的好方法。但是,如果您开始嵌套视图而不了解这可能导致的性能影响,则视图有一个邪恶的一面。当您查看需要T - SQL解决方案的给定业务需求时,请将视图视为可以用来实现解决方案的众多工具之一。

 

问答

在本节中,您可以通过回答以下问题来查看使用视图查询数据库的理解程度。

问题1 :

视图可以帮助您实现哪些良好的业务需求?

A.需要阻止应用程序或临时查询访问表中的基础列。

B.需要简化查询复杂数据库结构所需的代码。

C.需要提供向后兼容性。

D.上述全部

问题2 :

你需要确保在通过视图更新或插入列值时,可以通过视图选择该列值。哪个子句提供此功能?

A.创建视图

B. 绑定舍曼

C.带检查选项

D.以上都不是

问题3 :

您需要限制对表中机密数据的访问。可以使用什么方法来限制对此数据的访问?

A.使用“带检查”选项创建视图

B.创建使用带SCHEMABINDING选项的视图

C.创建一个视图,该视图排除表中的机密列,但未证明对表的选择访问权限。

D.创建一个视图,排除表中的机密列并证明对表的选择访问权限。

回答:

问题1 :

答案是D。在直接查询上使用视图有很多原因。A、B和C是其中的一些原因。

 

问题2 :

正确答案是C。CREATE视图不是提供任何其他数据完整性检查的子句。WITH SCHEMABINDING子句确保在更改视图的基础表结构时,任何ALTER TABLE语句都不会导致视图出现问题。它是WITH CHECK选项,可确保您无法更新基础表,除非使用视图可以立即查询更改。

 

问题3 :

正确答案是C。答案A和B并不特别限制对机密列的访问,因为它们没有提到从视图中排除机密列。答案D不正确,因为如果人们可以访问包含机密数据的基础表,那么他们仍然可以通过编写直接与该表相对应的查询来选择机密列。

猜你喜欢

转载自blog.csdn.net/xtt_3170707038/article/details/80386052
今日推荐