CREATE VIEW
创建一个虚拟表,该表以另一种方式表示一个或多个表中的数据。CREATE VIEW 必须是查询批处理中的第一条语句。
语法
CREATE VIEW [ < database_name > .] [ < owner > .] view_name [ ( column [ ,...n ] ) ]
[ WITH < view_attribute > [ ,...n ] ]
AS
select_statement
[ WITH CHECK OPTION ]
< view_attribute > ::=
{ ENCRYPTION | SCHEMABINDING | VIEW_METADATA }
参数
view_name
是视图的名称。视图名称必须符合标识符规则。可以选择是否指定视图所有者名称。
column
是视图中的列名。只有在下列情况下,才必须命名 CREATE VIEW 中的列:当列是从算术表达式、函数或常量派生的,两个或更多的列可能会具有相同的名称(通常是因为联接),视图中的某列被赋予了不同于派生来源列的名称。还可以在 SELECT 语句中指派列名。
如果未指定 column,则视图列将获得与 SELECT 语句中的列相同的名称。
说明 在视图的各列中,列名的权限在 CREATE VIEW 或 ALTER VIEW 语句间均适用,与基础数据源无关。例如,如果在 CREATE VIEW 语句中授予了 title_id 列上的权限,则 ALTER VIEW 语句可以将 title_id 列改名(例如改为 qty),但权限仍与使用 title_id 的视图上的权限相同。
n
是表示可以指定多列的占位符。
AS
是视图要执行的操作。
select_statement
是定义视图的 SELECT 语句。该语句可以使用多个表或其它视图。若要从创建视图的 SELECT 子句所引用的对象中选择,必须具有适当的权限。
视图不必是具体某个表的行和列的简单子集。可以用具有任意复杂性的 SELECT 子句,使用多个表或其它视图来创建视图。
在索引视图定义中,SELECT 语句必须是单个表的语句或带有可选聚合的多表 JOIN。
对于视图定义中的 SELECT 子句有几个限制。CREATE VIEW 语句不能:
包含 COMPUTE 或 COMPUTE BY 子句。
包含 ORDER BY 子句,除非在 SELECT 语句的选择列表中也有一个 TOP 子句。
包含 INTO 关键字。
引用临时表或表变量。
因为 select_statement 使用 SELECT 语句,所以在 FROM 子句中指定
在 select_statement 中可以使用函数。
select_statement 可使用多个由 UNION 或 UNION ALL 分隔的 SELECT 语句。
WITH CHECK OPTION
强制视图上执行的所有数据修改语句都必须符合由 select_statement 设置的准则。通过视图修改行时,WITH CHECK OPTION 可确保提交修改后,仍可通过视图看到修改的数据。
WITH ENCRYPTION
表示 SQL Server 加密包含 CREATE VIEW 语句文本的系统表列。使用 WITH ENCRYPTION 可防止将视图作为 SQL Server 复制的一部分发布。
SCHEMABINDING
将视图绑定到架构上。指定 SCHEMABINDING 时,select_statement 必须包含所引用的表、视图或用户定义函数的两部分名称 (owner.object)。
不能除去参与用架构绑定子句创建的视图中的表或视图,除非该视图已被除去或更改,不再具有架构绑定。否则,SQL Server 会产生错误。另外,如果对参与具有架构绑定的视图的表执行 ALTER TABLE 语句,而这些语句又会影响该架构绑定视图的定义,则这些语句将会失败。
VIEW_METADATA
指定为引用视图的查询请求浏览模式的元数据时,SQL Server 将向 DBLIB、ODBC 和 OLE DB API 返回有关视图的元数据信息,而不是返回基表或表。浏览模式的元数据是由 SQL Server 向客户端 DB-LIB、ODBC 和 OLE DB API 返回的附加元数据,它允许客户端 API 实现可更新的客户端游标。浏览模式的元数据包含有关结果集内的列所属的基表信息。
对于用 VIEW_METADATA 选项创建的视图,当描述结果集中视图内的列时,浏览模式的元数据返回与基表名相对的视图名。
当用 VIEW_METADATA 创建视图时,如果该视图具有 INSERT 或 UPDATE INSTEAD OF 触发器,则视图的所有列(timestamp 除外)都是可更新的。请参见本主题后面的"可更新视图"。
注释
只能在当前数据库中创建视图。视图最多可以引用 1,024 列。
通过视图进行查询时,Microsoft? SQL Server? 将检查以确定语句中任意位置引用的所有数据库对象是否都存在,这些对象在语句的上下文中是否有效,以及数据修改语句是否没有违反任何数据完整性规则。如果检查失败,将返回错误信息。如果检查成功,则将操作转换成对基础表的操作。
如果某个视图依赖于已除去的表(或视图),则当有人试图使用该视图时,SQL Server 将产生错误信息。如果创建了新表或视图(该表的结构与以前的基表没有不同之处)以替换除去的表或视图,则视图将再次可用。如果新表或视图的结构发生更改,则必须除去并重新创建该视图。
创建视图时,视图的名称存储在 sysobjects 表中。有关视图中所定义的列的信息添加到 syscolumns 表中,而有关视图相关性的信息添加到 sysdepends 表中。另外,CREATE VIEW 语句的文本添加到 syscomments 表中。这与存储过程相似;当首次执行视图时,只有其查询树存储在过程高速缓存中。每次访问视图时,都重新编译其执行计划。
在通过 numeric 或 float 表达式定义的视图上使用索引所得到的查询结果,可能不同于不在视图上使用索引的类似查询所得到的结果。这种差异可能是由对基础表进行 INSERT、DELETE 或 UPDATE 操作时的舍入错误引起的。
创建视图时,SQL Server 保存 SET QUOTED_IDENTIFIER 和 SET ANSI_NULLS 的设置。使用视图时,将还原这些最初的设置。因此,当访问视图时,将忽略 SET QUOTED_IDENTIFIER 和 SET ANSI_NULLS 的所有客户端会话设置。
说明 SQL Server 是将空字符串解释为单个空格还是真正的空字符串取决于 sp_dbcmptlevel 的设置。如果兼容级别小于或等于 65,SQL Server 就将空字符串解释为单个空格。如果兼容级别等于或大于 70,则 SQL Server 就将空字符串解释为空字符串。有关更多信息,请参见 sp_dbcmptlevel。
可更新视图
Microsoft SQL Server 2000 以两种方法增强可更新视图的类别:
INSTEAD OF 触发器:可以在视图上创建 INSTEAD OF 触发器,以使视图可更新。执行 INSTEAD OF 触发器,而不是执行定义触发器的数据修改语句。该触发器使用户得以指定一套处理数据修改语句时需要执行的操作。因此,如果在给定的数据修改语句(INSERT、UPDATE 或 DELETE)上存在视图的 INSTEAD OF 触发器,则通过该语句可更新相应的视图。有关 INSTEAD OF 触发器的更多信息,请参见设计 INSTEAD OF 触发器。
分区视图:如果视图属于称为"分区视图"的指定格式,则该视图的可更新性受限于某些限制。本主题稍后将讨论分区视图及其可更新性。
必要时,SQL Server 将"本地分区视图"辨别为所有参与表和视图都在同一 SQL Server 上的视图,而将"分布式分区视图"辨别为视图中至少有一个表驻留在不同(远程)服务器上的视图。
如果视图没有 INSTEAD OF 触发器,或者视图不是分区视图,则视图只有满足下列条件才可更新:
select_statement 在选择列表中没有聚合函数,也不包含 TOP、GROUP BY、UNION(除非视图是本主题稍后要描述的分区视图)或 DISTINCT 子句。聚合函数可以用在 FROM 子句的子查询中,只要不修改函数返回的值。有关更多信息,请参见聚合函数。
select_statement 的选择列表中没有派生列。派生列是由任何非简单列表达式(使用函数、加法或减法运算符等)所构成的结果集列。
select_statement 中的 FROM 子句至少引用一个表。select_statement 必须不仅只包含非表格格式的表达式(即不是从表派生出的表达式)。例如,以下视图是不可更新的:
CREATE VIEW NoTable AS
SELECT GETDATE() AS CurrentDate,
@@LANGUAGE AS CurrentLanguage,
CURRENT_USER AS CurrentUser
INSERT、UPDATE 和 DELETE 语句在可以引用可更新视图之前,也必须如上述条件指定的那样满足某些限制条件。只有当视图可更新,并且所编写的 UPDATE 或 INSERT 语句只修改视图的 FROM 子句引用的一个基表中的数据时,UPDATE 和 INSERT 语句才能引用视图。只有当视图在其 FROM 子句中只引用一个表时,DELETE 语句才能引用可更新的视图。
分区视图
分区视图是通过对成员表使用 UNION ALL 所定义的视图,这些成员表的结构相同,但作为多个表分别存储在同一个 SQL Server 中,或存储在称为联合 SQL Server 2000 服务器的自主 SQL Server 2000 服务器组中。
例如,如果使 Customers 表数据分布在分别位于三个服务器上的三个成员表中(Customers_33 在 Server1 上,Customers_66 在 Server2 上,Customers_99 在 Server3 上),将以下面的方法在 Server1 上定义分区视图:
--Partitioned view as defined on Server1
CREATE VIEW Customers
AS
--Select from local member table
SELECT *
FROM CompanyData.dbo.Customers_33
UNION ALL
--Select from member table on Server2
SELECT *
FROM Server2.CompanyData.dbo.Customers_66
UNION ALL
--Select from mmeber table on Server3
SELECT *
FROM Server3.CompanyData.dbo.Customers_99
一般情况下,如果视图为下列格式,则称其为分区视图:
SELECT
FROM T1
UNION ALL
SELECT
FROM T2
UNION ALL
...
SELECT
FROM Tn
分区视图的创建条件
SELECT 列表
成员表中的所有列应在视图定义的列列表中被选定。
每个 select_list 中的同一序号位置上的列应属于同一类型,包括排序规则。列仅仅属于隐性可转换类型(如 UNION 的通常情况下)是不够的。
另外,at least one 列(例如
必须以下面的格式在表 T1 上定义约束 C1:
C1 ::= < simple_interval > [ OR < simple_interval > OR ...]
< simple_interval > :: =
< col > { < | > | <= | >= | = }
| < col > BETWEEN < value1 > AND < value2 >
| < col > IN ( value_list )
| < col > { > | >= } < value1 > AND
< col > { < | <= } < value2 >
约束应使
下面是有效约束集的示例:
{ [col < 10], [col between 11 and 20] , [col > 20] }
{ [col between 11 and 20], [col between 21 and 30], [col between 31 and 100] }
在 SELECT 列表中不能多次使用同一列。
分区列
分区列是表的 PRIMARY KEY 的一部分。
不能是计算列。
如果成员表中的同一列上存在多个约束,则 SQL Server 忽略所有约束且在确定视图是否为分区视图时不考虑这些约束。为满足分区视图的条件,在分区列上应只有一个分区约束。
成员表(或基础表 T1,...,Tn)
表可以是本地表,也可以是通过由四部分构成的名称或基于 OPENDATASOURCE 或 OPENROWSET 的名称引用的其它 SQL Server 中的表。(OPENDATASOURCE 和 OPENROWSET 语法可以指定表名,但不能指定直接传递查询。)有关更多信息,请参见 OPENDATASOURCE 和 OPENROWSET。
如果一个或多个成员表是远程表,则视图称为 distributed partitioned view,并且适用附加条件。本节稍后将对其进行讨论。
在用 UNION ALL 语句组合的表集中,同一个表不能出现两次。
成员表不能在表中的计算列上创建索引。
成员表在数目相同的列上应具有所有 PRIMARY KEY 约束。
视图中的所有成员表应有相同的 ANSI 填充设置(使用 sp_configure 中 user options 选项或 SET 选项设置)。
修改分区视图的条件
只有 SQL Server 2000 开发版和企业版允许在分区视图中进行 INSERT、UPDATE 和 DELETE 操作。若要修改分区视图,语句必须满足下列条件:
INSERT 语句必须为视图中所有的列提供值,即使基础成员表具有这些列的 DEFAULT 约束或即使它们允许 NULL。对于这些具有 DEFAULT 定义的成员表列,这些语句无法显式地使用关键字 DEFAULT。
插入到分区列中的值应至少满足一个基础约束;否则,INSERT 操作将因违反约束而失败。
即使列中含有在相应成员表中定义的 DEFAULT 值,UPDATE 语句也不能指定 DEFAULT 关键字作为 SET 子句中的值。
如果成员表中有 text、ntext 或 image 列,则 PRIMARY KEY 列无法通过 UPDATE 语句进行修改。
不能通过 INSERT 或 UPDATE 语句修改视图中作为一个或多个成员表中的 IDENTITY 列的列。
如果其中的一个成员表包含 timestamp 列,则不能通过 INSERT 或 UPDATE 语句修改视图。
如果分区视图或针对该视图的 INSERT、UPDATE 和 DELETE语句中的任何一个成员表具有内联接,则不允许使用这些语句。
说明 若要更新分区视图,用户在成员表上必须具有 INSERT、UPDATE 和 DELETE 权限。
分布式分区视图的附加条件
对于分布式分区视图(当一个或多个成员表为远程表时),适用下列附加条件:
将启动分布式事务以确保更新所影响的所有节点间的原子性。
应将 XACT_ABORT SET 选项设置为 ON,以使 INSERT、UPDATE 或 DELETE 语句生效。
在分区视图引用的远程表中的任何 smallmoney 和 smalldatetime 列分别映射为 money 和 datetime。因此,本地表中相应的列(选择列表中的相同序号位置)应为 money 和 datetime。
分区视图中的任何链接服务器都不能是环回链接服务器(指向同一 SQL Server 的链接服务器)。
对于涉及可更新分区视图和远程表的 INSERT、UPDATE 和 DELETE 操作,忽略 SET ROWCOUNT 选项的设置。
当设置好成员表和分区视图的定义后,Microsoft SQL Server 2000 将生成智能的查询计划以便有效地访问成员表中的数据。通过使用 CHECK 约束定义,查询处理器在成员间映射键值的分布。当用户发出查询时,查询处理器将映射与 WHERE 子句中指定的值进行比较,然后生成使成员服务器间的数据传输量减到最少的执行计划。因此,虽然有些成员表可能位于远程服务器中,但是 SQL Server 2000 将解析分布式查询,使得必须传输的分布式数据量减到最少。有关 SQL Server 2000 如何解析分区视图查询的更多信息,请参见解析分布式分区视图。
复制的考虑事项
为在复制所包含的成员表上创建分区视图,需要考虑下列事项:
如果在更新订阅服务器时基础表包含在合并复制或事务复制中,则唯一标识符列也应包含在 SELECT 列表中。
对分区视图进行的任何 INSERT 操作都必须对唯一标识符列提供 NEWID() 值。对唯一标识符列的任何 UPDATE 操作都必须提供 NEWID() 作为该值,因为无法使用 DEFAULT 关键字。
通过视图对更新所做的复制与在两个不同的数据库之间复制表时完全相同;也就是说,这些表服务于不同的复制代理程序,而且不保证更新的顺序。
权限
CREATE VIEW 权限默认授予 db_owner 和 db_ddladmin 固定数据库角色成员。sysadmin 固定服务器角色成员和 db_owner 固定数据库角色成员可以将 CREATE VIEW 权限转让给其他用户。
若要创建视图,用户必须具有 CREATE VIEW 权限,还必须在视图所引用的表、视图和表值函数上具有 SELECT 权限,在视图所唤醒调用的标量值函数上具有 EXECUTE 权限。
另外,若要创建 WITH SCHEMABINDING 视图,用户必须在每个引用的表、视图和用户定义函数上具有 REFERENCES 权限。
示例
A. 使用简单的 CREATE VIEW
下例创建具有简单 SELECT 语句的视图。当需要频繁地查询列的某种组合时,简单视图非常有用。
USE pubs
IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS
WHERE TABLE_NAME = 'titles_view')
DROP VIEW titles_view
GO
CREATE VIEW titles_view
AS
SELECT title, type, price, pubdate
FROM titles
GO
B. 使用 WITH ENCRYPTION
下例使用 WITH ENCRYPTION 选项并显示计算列、重命名列以及多列。
USE pubs
IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS
WHERE TABLE_NAME = 'accounts')
DROP VIEW accounts
GO
CREATE VIEW accounts (title, advance, amt_due)
WITH ENCRYPTION
AS
SELECT title, advance, price * royalty * ytd_sales
FROM titles
WHERE price > $5
GO
下面是用来检索加密存储过程的标识号和文本的查询:
USE pubs
GO
SELECT c.id, c.text
FROM syscomments c, sysobjects o
WHERE c.id = o.id and o.name = 'accounts'
GO
下面是结果集:
说明 text 列的输出显示在单独一行中。执行该存储过程时,下列信息将与 id 列信息出现在同一行中。
id text
----------- ------------------------------------------------------------
661577395
???????????????????????????????????????????????????????????????????????...
(1 row(s) affected)
C. 使用 WITH CHECK OPTION
下例显示名为 CAonly 的视图,该视图使得只对加利福尼亚州的作者应用数据修改。
USE pubs
IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS
WHERE TABLE_NAME = 'CAonly')
DROP VIEW CAonly
GO
CREATE VIEW CAonly
AS
SELECT au_lname, au_fname, city, state
FROM authors
WHERE state = 'CA'
WITH CHECK OPTION
GO
D. 在视图中使用内置函数
下例显示包含内置函数的视图定义。使用函数时,必须在 CREATE VIEW 语句中为派生列指定列名。
USE pubs
IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS
WHERE TABLE_NAME = 'categories')
DROP VIEW categories
GO
CREATE VIEW categories (category, average_price)
AS
SELECT type, AVG(price)
FROM titles
GROUP BY type
GO
E. 在视图中使用 @@ROWCOUNT 函数
下例使用 @@ROWCOUNT 函数作为视图定义的一部分。
USE pubs
IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS
WHERE TABLE_NAME = 'myview')
DROP VIEW myview
GO
CREATE VIEW myview
AS
SELECT au_lname, au_fname, @@ROWCOUNT AS bar
FROM authors
WHERE state = 'UT'
GO
SELECT *
FROM myview
F. 使用分区数据
下例使用名为 SUPPLY1、SUPPLY2、SUPPLY3 和 SUPPLY4 的表,这些表对应于位于不同国家的四个办事处的供应商表。
--create the tables and insert the values
CREATE TABLE SUPPLY1 (
supplyID INT PRIMARY KEY CHECK (supplyID BETWEEN 1 and 150),
supplier CHAR(50)
)
CREATE TABLE SUPPLY2 (
supplyID INT PRIMARY KEY CHECK (supplyID BETWEEN 151 and 300),
supplier CHAR(50)
)
CREATE TABLE SUPPLY3 (
supplyID INT PRIMARY KEY CHECK (supplyID BETWEEN 301 and 450),
supplier CHAR(50)
)
CREATE TABLE SUPPLY4 (
supplyID INT PRIMARY KEY CHECK (supplyID BETWEEN 451 and 600),
supplier CHAR(50)
)
INSERT SUPPLY1 VALUES ('1', 'CaliforniaCorp')
INSERT SUPPLY1 VALUES ('5', 'BraziliaLtd')
INSERT SUPPLY2 VALUES ('231', 'FarEast')
INSERT SUPPLY2 VALUES ('280', 'NZ')
INSERT SUPPLY3 VALUES ('321', 'EuroGroup')
INSERT SUPPLY3 VALUES ('442', 'UKArchip')
INSERT SUPPLY4 VALUES ('475', 'India')
INSERT SUPPLY4 VALUES ('521', 'Afrique')
--create the view that combines all supplier tables
CREATE VIEW all_supplier_view
AS
SELECT *
FROM SUPPLY1
UNION ALL
SELECT *
FROM SUPPLY2
UNION ALL
SELECT *
FROM SUPPLY3
UNION ALL
SELECT *
FROM SUPPLY4