T-SQL查询进阶--深入浅出视图

简介  

视图可以看作定义在SQL Server上的虚拟表.视图正如其名字的含义一样,是另一种查看数据的入口.常规视图本身并不存储实际的数据,而仅仅存储一个Select语句和所涉及表的metadata.

视图简单的理解如下:

通过视图,客户端不再需要知道底层table的表结构及其之间的关系。视图提供了一个统一访问数据的接口。

为什么要使用视图(View)

从而我们不难发现,使用视图将会得到如下好处:

  • 视图隐藏了底层的表结构,简化了数据访问操作
  • 因为隐藏了底层的表结构,所以大大加强了安全性,用户只能看到视图提供的数据
  • 使用视图,方便了权限管理,让用户对视图有权限而不是对底层表有权限进一步加强了安全性
  • 视图提供了一个用户访问的接口,当底层表改变后,改变视图的语句来进行适应,使已经建立在这个视图上客户端程序不受影响

视图(View)的分类

视图在SQL中可以分为三类

  1. 普通视图(Regular View)
  2. 索引视图(Indexed View)
  3. 分割视图(Partitioned View)

下面从这几种视图类型来谈视图

普通视图(Rugular View)

普通视图由一个Select语句所定义,视图仅仅包含其定义和被引用表的metadata.并不实际存储数据。MSDN中创建视图的模版如下:

CREATE VIEW [ schema_name . ] view_name [ (column [ ,...n ] ) ]
[ WITH <view_attribute> [ ,...n ] ]
AS select_statement
[ WITH CHECK OPTION ] [ ; ]

<view_attribute> ::=
{
    [ ENCRYPTION ]
    [ SCHEMABINDING ]
    [ VIEW_METADATA ]     } 

参数还是比较少的,现在解释一下上面的参数:

ENCRYPTION:视图是加密的,如果选上这个选项,则无法修改.创建视图的时候需要将脚本保存,否则再也不能修改了

SCHEMABINDING:和底层引用到的表进行定义绑定。这个选项选上的话,则视图所引用到的表不能随便更改构架(比如列的数据类型),如果需要更改底层表构架,则先drop或者alter在底层表之上绑定的视图.

VIEW_METADATA:这个是个很有意思的选项.正如这个选项的名称所指示,如果不选择,返回给客户端的metadata是View所引用表的metadata,如果选择了这个选项,则返回View的metadata.再通俗点解释,VIEW_METADATA可以让视图看起来貌似表一样。View的每一个列的定义等直接告诉客户端,而不是所引用底层表列的定义。

WITH Check Option:这个选项用于更新数据做限制,下面会在通过视图更新数据一节解释.

当然了,创建视图除了需要符合上面的语法规则之外,还有一些规则需要遵守:

  • 在View中,除非有TOP关键字,否则不能用Order By子句(如果你一意孤行要用Order by,这里有个hack是使用Top 100 percent…..)
  • View在每个Schema中命名必须独一无二
  • View嵌套不能超过32层(其实实际工作中谁嵌套超过两层就要被打PP了-.-)
  • Compute,compute by,INTO关键字不允许出现在View中
  • View不能建立在临时表上
  • View不能对全文索引进行查询

建立View一个简单的例子:

CREATE VIEW v_Test_View1
AS
SELECT TOP 100 * FROM HumanResources.Employee

视图建立完成后,就可以像访问表一样访问视图了:

SELECT * FROM v_Test_View1

在Management studio中,我创建视图的时候更喜欢用这样一种方法,将会便捷很多:

索引视图(Indexed View)

在谈到索引视图之前,我突然想起以前看过的一个漫画.话说咱们高端产品买不起,但是省吃俭用攒点钱买个IPhone装装高端总还是可以的吧:

其实索引视图也很类似,在普通的视图的基础上,为视图建立唯一聚集索引,这时这个视图就变成了索引视图.套用上面漫画的公式:视图+聚集索引=索引视图

索引视图可以看作是一个和表(Table)等效的对象!

SQL Server中的索引视图和Oracle中的Materialized View是一个概念.想要理解索引视图,必须先理解聚集索引。聚集索引简单来说理解成主键,数据库中中的数据按照主键的顺序物理存储在表中,就像新华字典,默认是按照ABCD….这样的方式进行内容设置。ABCD….就相当于主键.这样就避免了整表扫描从而提高了性能.因此一个表中只能有一个聚集索引。

对于索引视图也是,为一个视图加上了聚集索引后。视图就不仅仅再是select语句和表的metadata了,索引视图会将数据物理存在数据库中,索引视图所存的数据和索引视图中所涉及的底层表保持同步。

理解了索引视图的原理之后,我们可以看出,索引视图对于OLAP这种大量数据分析和查询来说,性能将会得到大幅提升。尤其是索引视图中有聚合函数,涉及大量高成本的JOIN,因为聚合函数计算的结果物理存入索引视图,所以当面对大量数据使用到了索引视图之后,并不必要每次都进行聚合运算,这无疑会大大提升性能.

而同时,每次索引视图所涉及的表进行Update,Insert,Delete操作之后,SQL Server都需要标识出改变的行,让索引视图进行数据同步.所以OLTP这类增删改很多的业务,数据库需要做大量的同步操作,这会降低性能。

谈完了索引视图的基本原理和好处与坏处之后,来看看在SQL Server中的实现:

在SQL Server中实现索引视图是一件非常,简单的事,只需要在现有的视图上加上唯一聚集索引.但SQL Server对于索引视图的限制却使很多DBA对其并不青睐:

比如:

  • 索引视图涉及的基本表必须ANSI_NULLS设置为ON
  • 索引视图必须设置ANSI_NULLS和QUOTED_INDETIFIER为ON
  • 索引视图只能引用基本表
  • SCHEMABINDING必须设置
  • 定义索引视图时必须使用Schema.ViewName这样的全名
  • 索引视图中不能有子查询
  • avg,max,min,stdev,stdevp,var,varp这些聚合函数不能用

………………

还有更多…就不一一列举了,有兴趣的请自行Google之.

下面我来通过一个例子来说明索引视图:

假设在adventureWorks数据库中,我们有一个查询:

SELECT p.Name,s.OrderQty
FROM Production.Product p
 inner join Sales.SalesOrderDetail s
ON p.ProductID=s.ProductID

这个查询的执行计划:

这时,我建立视图并在这个视图上建立唯一聚集索引:

--建立视图
CREATE VIEW v_Test_IndexedView
WITH SCHEMABINDING
AS
SELECT p.Name,s.OrderQty,s.SalesOrderDetailID
FROM Production.Product p
 inner join Sales.SalesOrderDetail s
ON p.ProductID=s.ProductID
GO
--在视图上建立索引
CREATE UNIQUE CLUSTERED INDEX indexedview_test1
ON v_Test_IndexedView(SalesOrderDetailID)

接下来,套用刘谦的台词:见证奇迹的时刻到了,我们再次执行之前的查询:

从上面这个例子中,可以体会到索引视图的强大威力,即使你的查询语句中不包含这个索引视图,查询分析器会自动选择这个视图,从而大大的提高了性能.当然,这么强力的性能,只有在SQL SERVER企业版和开发版才有哦(虽然我见过很多SQL Server的开发人员让公司掏着Enterprise版的钱,用着Express版的功能……)

分割视图(Partitioned View)

分割视图其实从微观实现方式来说,整个视图所返回的数据由几个平行表(既是几个表有相同的表结构,也就是列和数据类型,但存储的行集合不同)进行UNION连接(对于UNION连接如果不了解,请看我之前的博文)所获得的数据集.

分割视图总体上可以分为两种:

1.本地分割视图(Local Partitioned View)

2.分布式分割视图(Distributed Partitioned View)

因为本地分割视图仅仅是为了和SQL Server 2005之前的版本的一种向后兼容,所以这里仅仅对分布式分割视图进行说明.

分布式分割视图其实是将由几个由不同数据源或是相同数据源获得的平行数据集进行连接所获得的,一个简单的概念图如下:

上面的视图所获得的数据分别来自三个不同数据源的表,每一个表中只包含四行数据,最终组成了这个分割视图.

使用分布式分割视图最大的好处就是提升性能.比如上面的例子中,我仅仅想取得ContactID为8这位员工的信息,如果通过分布式视图获取的话,SQL Server可以非常智能的仅仅扫描包含ContactID为8的表2,从而避免了整表扫描。这大大减少了IO操作,从而提升了性能.

这里要注意的是,分布式分割视图所涉及的表之间的主键不能重复,比如上面的表A ContactID是1-4,则表B的ContactID不能是2-8这个样子.

还有一点要注意的是,一定要为分布式分割索引的主键加Check约束,从而让SQL Server的查询分析器知道该去扫描哪个表,下面来看个例子.

在微软示例数据库AdventureWorks数据库,我通过ContactID从前100行和100-200行的数据分别存入两个表,Employee100和Employee200,代码如下:

--create Employee100
SELECT TOP 100 * INTO Employee100
FROM HumanResources.Employee
ORDER BY EmployeeID
--create Employee200
SELECT *  INTO Employee200
FROM
(SELECT TOP 100 *
FROM HumanResources.Employee
WHERE EmployeeID NOT IN (SELECT TOP 100 EmployeeID FROM HumanResources.Employee ORDER BY EmployeeID)
ORDER BY HumanResources.Employee.EmployeeID)AS e

这时来建立分布式分割视图:

CREATE VIEW v_part_view_test
AS
SELECT * FROM Employee100
UNION
SELECT * FROM Employee200

这时我们对这个索引进行查询操作:

SELECT * FROM v_part_view_test
WHERE EmployeeID=105

下面是执行计划:

通过上图可以看出,通过这种分割的方式,执行计划仅仅是扫描Employee200,从而避免了扫描所有数据,这无疑提升了性能.

所以,当你将不同的数据表之间放到不同的服务器或是使用RAID5磁盘阵列时,分布式分割视图则进一步会提升查询性能.

使用分布式分割视图能够在所有情况下都提升性能吗?那妥妥的不可能.使用这种方式如果面对的查询包含了聚合函数,尤其是聚合函数中还包含distinct。或是不加where条件进行排序.那绝对是性能的杀手。因为聚合函数需要扫描分布式分割视图中所有的表,然后进行UNION操作后再进行运算.

通过视图(View)更新数据

通过视图更新数据是我所不推荐的.因为视图并不能接受参数.我更推荐使用存储过程来实现.

使用View更新数据和更新Table中数据的方式完全一样(前面说过,View可以看作是一个虚拟表,如果是索引视图则是具体的一张表)

通过视图来更新数据需要注意以下几点

1.视图中From子句之后至少有一个用户表

2.View的查询无论涉及多少张表,一次只能更新其中一个表的数据

3.对于表达式计算出来的列,常量列,聚合函数算出来的列无法更新

4.Group By,Having,Distinct关键字不能影响到的列不能更新

这里说一下创建View有一个WITH Check Option选项,如果选择这个选项,则通过View所更新的数据必须符合View中where子句所限定的条件,比如:

我创建一个View:

视图(View)中的几个小技巧

1.通过视图名称查到视图的定义

SELECT * FROM sys.sql_modules
WHERE object_id=OBJECT_ID(‘视图名称‘)

2.前面说过,普通视图仅仅存储的是select语句和所引用表的metadata,当底层表数据改变时,有时候视图中表的metadata并没有及时同步,可以通过如下代码进行手动同步

EXEC sp_refreshview 视图名称

视图(View)的最佳实践

这是我个人一些经验,欢迎补充

  • 一定要将View中的Select语句性能调到最优(貌似是废话,不过真理都是废话…)
  • View最好不要嵌套,如果非要嵌套,最多只嵌套一层
  • 能用存储过程和自定义函数替代View的,尽量不要使用View,存储过程会缓存执行计划,性能更优,限制更少
  • 在分割视图上,不要使用聚合函数,尤其是聚合函数还包含了Distinct
  • 在视图内,如果Where子句能加在视图内,不要加在视图外(因为调用视图会返回所有行,然后再筛选,性能杀手,如果你还加上了order by…..)

总结

文中对视图的三种类型进行了详解.每种视图都有各自的使用范围,使用得当会将性能提升一个档次,而使用不当反而会拖累性能.

我想起一句名言:“everything has price,always trade-off”…..

时间: 2024-10-13 02:54:19

T-SQL查询进阶--深入浅出视图的相关文章

oracle新建用户执行sql查询语句出现错误ORA-00942:表或视图不存在

oracle创建新用户后客户端执行SQL查询后出现错误提示如下: 执行查询语句如下: select * from sm_sales_order; ORA-00942:表或视图不存在 创建新用户并指定表空间和临时表空间 CREATE USER xxx IDENTIFIED BY xxxx DEFAULT TABLESPACE LMS TEMPORARY TABLESPACE TEMP; 授予系统权限connect grant connect to xxx; 授予对象权限,只限查询 grant se

【T-SQL进阶】02.理解SQL查询的底层原理

本系列[T-SQL]主要是针对T-SQL的总结. T-SQL基础 [T-SQL基础]01.单表查询-几道sql查询题 [T-SQL基础]02.联接查询 [T-SQL基础]03.子查询 [T-SQL基础]04.表表达式-上篇 [T-SQL基础]04.表表达式-下篇 [T-SQL基础]05.集合运算 [T-SQL基础]06.透视.逆透视.分组集 [T-SQL基础]07.数据修改 [T-SQL基础]08.事务和并发 [T-SQL基础]09.可编程对象 T-SQL进阶 [T-SQL进阶]01.好用的SQ

SQL Server-聚焦使用视图若干限制/建议、视图查询性能问题,你懵逼了?(二十五)

前言 上一节我们简单讲述了表表达式的4种类型,这一系列我们来讲讲使用视图的限制,简短的内容,深入的理解,Always to review the basics. 避免在视图中使用ORDER BY 上一节我们也讲述了使用表表达式必须满足的3个要求,其中就有一个无法保证顺序,也就是说的ORDER BY的问题,我们还是重点看看在视图中的限制.在常规查询中对于排序我们是这样做的. USE AdventureWorks2012 GO SELECT * FROM Sales.SalesOrderDetail

T-SQL查询进阶--SQL Server中的事务与锁

为什么需要锁 在任何多用户的数据库中,必须有一套用于数据修改的一致的规则,当两个不同的进程试图同时修改同一份数据时,数据库管理系统(DBMS)负责解决它们之间潜在的冲突.任何关系数据库必须支持事务的ACID属性,所以在开始了解锁之前,首先简单了解一下数据库事务和事务的ACID属性. 原子性(Atomicity):原子性意味着数据库中的事务执行是作为原子.即不可在分,整个语句要么执行,要么不执行 一致性(Consistency):在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏.(唯一约

[Z]T-SQL查询进阶--深入理解子查询

原文链接: http://www.cnblogs.com/CareySon/archive/2011/07/18/2109406.html 引言 SQL有着非常强大且灵活的查询方式,而多表连接操作往往也可以用子查询进行替代,本篇文章将会讲述子查询的方方面面. 简介 子查询本质上是嵌套进其他SELECT,UPDATE,INSERT,DELETE语句的一个被限制的SELECT语句,在子查询中,只有下面几个子句可以使用 SELECT子句(必须) FROM子句(必选) WHERE子句(可选) GROUP

深入浅出视图

简介   视图可以看作定义在SQL Server上的虚拟表.视图正如其名字的含义一样,是另一种查看数据的入口.常规视图本身并不存储实际的数据,而仅仅存储一个Select语句和所涉及表的metadata. 视图简单的理解如下: 通过视图,客户端不再需要知道底层table的表结构及其之间的关系.视图提供了一个统一访问数据的接口. 为什么要使用视图(View) 从而我们不难发现,使用视图将会得到如下好处: 视图隐藏了底层的表结构,简化了数据访问操作 因为隐藏了底层的表结构,所以大大加强了安全性,用户只

SQL基础学习_04_视图

视图 1. 视图的创建 ? ? 视图就是保存好的SELECT语句,这些SELECT语句执行之后会产生新的表,所以在SQL中,视图和表是不做差别对待的,也就是SQL也可以对视图做一些操作: ? ? 由于视图并不实际保存数据,只是保存了执行的SELECT语句,可以节约存储空间,但是对于大数据量的存储,使用视图来频繁操作的话,势必会占用较大的计算时间,也算是一种用时间换空间的方案. ? ? 通过以下语句创建视图: ? ? CREATE VIEW 视图名称 (<视图列1>,<视图列2>,&

SQL查询入门(上篇)

引言 SQL语言是一门简单易学却又功能强大的语言,它能让你快速上手并写出比较复杂的查询语句.但对于大多数开发者来说,使用SQL查询数据库并没有一个抽象的过程和一个合理的步骤,这很可能会使在写一些特定的SQL查询语句来解决特定问题时被"卡"住,本系列文章主要讲述SQL查询时一些基本的理论,以及写查询语句的抽象思路. SQL查询简介 SQL语言起源于1970年E.J.Codd发表的关系数据库理论,所以SQL是为关系数据库服务的.而对于SQL查询,是指从数据库中取得数据的子集,这句话貌似听着

如何编写高效的SQL查询语句

概述 如何编写性能比较高的SQL查询语句呢?两个方法:创建合理的索引:书写高效的SQL语句 索引的基本原理 索引分为聚集索引和非聚集索引.一个表只能创建一个聚集索引和N个非聚集索引,这句话的由来主要是由于索引的原理决定的. 数据库中的一张表不论你创建不创建索引,或者,不论你创建那种类型的索引,其在硬盘上的存储是一样的,那么,创建索引和不创建索引,或者,创建聚集索引和非聚集索引的区别在什么地方呢? 其区别是表内数据在内存的存在形式.对于没有创建索引的表,其加载到内存里时,就只有数据块:对于有聚集索