Rocho.J

人脑是不可靠的, 随时记录感悟并且经常重复!

 

[转]Sql Server 视图 --- http://bbs.51cto.com/thread-787942-1.html

SQL Server学习笔记15之--浅谈视图

 

视图
概念:
一个没有索引的视图其实只是一个虚拟表,没有任何物理的数据,它就是一段代码组成的东西,包含了描述它的元数据,如结构,依赖性等.
1.它可以对基础数据做些聚合之类的操作后提供使用.
2.它可以筛选数据,让客户只能访问视图以达到保护原表的目的.
3.同样,视图不能包含单独的ORDER BY .因为它被认为是一个表,表是逻辑实体,本身里面的数据时随便放的,并米有刻意按照某个顺序存储数据.

说几点关于视图的东西

1.视图中的ORDER BY


视图中的ORDER BY 绝非它本意--它不起排序的作用。
平时我们用 select top n * from tb order by col1 这里的order by 不仅排序还确定要返回哪几行
视图我们用 select top n * from tb order by col1 这里的ORDER BY 只能用来确定希望返回哪几行.
因为我们的视图最后出来的东西本质上是个表,这个时候你去查询这个视图的时候 出来的结果可不一定是按照COL1排序的

因为视图它就是个表 表是逻辑实体,本身里面的数据时随便放的

.看实例:

view plaincopy to clipboardprint?

  • create table k (a int , b int)  
  • insert k select 1,2   
  • insert k select 2,6   
  • insert k select 3,7   
  • insert k select 4,2   
  • insert k select 5,9  
  • gp  
  • create view v_showk  
  • as
  • select top 100 percent *  
  • from k   
  • order by b   
  • go  
  • select * from v_showk  
  • /*
  • a           b
  • ----------- -----------
  • 1           2
  • 2           6
  • 3           7
  • 4           2
  • 5           9
  • */

 




-------结果并没有按照你期待的B排序 这就说明视图里的order by 是没有起到排序作用的----------------

2.视图刷新的问题 sp_refreshview
有时候你会发现当你修改基础表的结构后,视图并没有跟着变,解决的方法就是修改后立即进行视图的刷新.特别是你试图出现select *的时候
  
view plaincopy to clipboardprint?

  • USE tempdb;  
  •   GO  
  •   IF OBJECT_ID('dbo.V1') IS NOT NULL  
  •     DROP VIEW dbo.V1;  
  •   GO  
  •   IF OBJECT_ID('dbo.T1') IS NOT NULL  
  •     DROP TABLE dbo.T1;  
  •   GO  
  •   CREATE TABLE dbo.T1(col1 INT, col2 INT);  
  •   INSERT INTO dbo.T1(col1, col2) VALUES(1, 2);  
  •   GO  
  •   --建立视图  
  •   CREATE VIEW dbo.V1  
  •   AS  
  •   SELECT * FROM dbo.T1;  
  •   GO  
  •   -- 查看视图  
  •   SELECT * FROM dbo.V1;  
  •   GO  
  •   /*
  •   col1        col2
  •   ----------- -----------
  •   1           2
  •   */
  •   --修改表架构  
  •   ALTER TABLE dbo.T1 ADD col3 INT;  
  •   GO  
  •   --再次查看视图  
  •   SELECT * FROM dbo.V1;  
  •   GO  
  •   /*
  •   col1        col2
  •   ----------- -----------
  •   1           2
  •   */
  •   --发现修改后的结构没有出现在眼前,刷新视图  
  •   EXEC sp_refreshview 'dbo.V1';  
  •   GO  
  •   --再次查看  
  •   SELECT * FROM dbo.V1;  
  •   GO  
  •   /*
  •   col1        col2        col3
  •   ----------- ----------- -----------
  •   1           2           NULL
  •   */



  ------------这里分享个一次刷新所有视图的代码(书上)-----------

  --刷新所有视图

view plaincopy to clipboardprint?

  • SELECT N'EXEC sp_refreshview '
  •      + QUOTENAME(VIEW_NAME, '''') + ';' AS cmd  
  • FROM (SELECT QUOTENAME(TABLE_SCHEMA)  
  •        + N'.' + QUOTENAME(TABLE_NAME) AS VIEW_NAME  
  •      FROM INFORMATION_SCHEMA.VIEWS) AS V  
  • WHERE OBJECTPROPERTY(OBJECT_ID(VIEW_NAME), 'IsSchemaBound') = 0;  
  • GO  



3.视图的模块化解题
如果一个题目解题思路写成一个语句比较长 比较难读 可以尝试用视图分开它 依次攻破 --(书上的例子,懒了下,盗用数据说明问题)

view plaincopy to clipboardprint?

  • SET NOCOUNT ON;  
  • USE tempdb;  
  • GO  
  • IF OBJECT_ID('dbo.Sales') IS NOT NULL  
  •    DROP TABLE dbo.Sales;  
  • GO  
  • CREATE TABLE dbo.Sales  
  • (  
  •    mnth DATETIME NOT NULL PRIMARY KEY,  
  •    qty  INT      NOT NULL  
  • );  
  • INSERT INTO dbo.Sales(mnth, qty) VALUES('20041201', 100);  
  • INSERT INTO dbo.Sales(mnth, qty) VALUES('20050101', 110);  
  • INSERT INTO dbo.Sales(mnth, qty) VALUES('20050201', 120);  
  • INSERT INTO dbo.Sales(mnth, qty) VALUES('20050301', 130);  
  • INSERT INTO dbo.Sales(mnth, qty) VALUES('20050401', 140);  
  • INSERT INTO dbo.Sales(mnth, qty) VALUES('20050501', 140);  
  • INSERT INTO dbo.Sales(mnth, qty) VALUES('20050601', 130);  
  • INSERT INTO dbo.Sales(mnth, qty) VALUES('20050701', 120);  
  • INSERT INTO dbo.Sales(mnth, qty) VALUES('20050801', 110);  
  • INSERT INTO dbo.Sales(mnth, qty) VALUES('20050901', 100);  
  • INSERT INTO dbo.Sales(mnth, qty) VALUES('20051001', 110);  
  • INSERT INTO dbo.Sales(mnth, qty) VALUES('20051101', 100);  
  • INSERT INTO dbo.Sales(mnth, qty) VALUES('20051201', 120);  
  • INSERT INTO dbo.Sales(mnth, qty) VALUES('20060101', 130);  
  • INSERT INTO dbo.Sales(mnth, qty) VALUES('20060201', 140);  
  • INSERT INTO dbo.Sales(mnth, qty) VALUES('20060301', 100);  
  • INSERT INTO dbo.Sales(mnth, qty) VALUES('20060401', 100);  
  • INSERT INTO dbo.Sales(mnth, qty) VALUES('20060501', 100);  
  • INSERT INTO dbo.Sales(mnth, qty) VALUES('20060601', 110);  
  • INSERT INTO dbo.Sales(mnth, qty) VALUES('20060701', 120);  
  • INSERT INTO dbo.Sales(mnth, qty) VALUES('20060801', 110);  
  • INSERT INTO dbo.Sales(mnth, qty) VALUES('20060901', 120);  
  • INSERT INTO dbo.Sales(mnth, qty) VALUES('20061001', 130);  
  • INSERT INTO dbo.Sales(mnth, qty) VALUES('20061101', 140);  
  • INSERT INTO dbo.Sales(mnth, qty) VALUES('20061201', 100);  
  • GO  



-----目标结果-----根据本月于上个月差来判断销售趋势

view plaincopy to clipboardprint?

  • /*
  • start_range end_range trend
  • ----------- --------- -------
  • 200412      200412    unknown
  • 200501      200504    up
  • 200505      200505    same
  • 200506      200509    down
  • 200510      200510    up
  • 200511      200511    down
  • 200512      200602    up
  • 200603      200603    down
  • 200604      200605    same
  • 200606      200607    up
  • 200608      200608    down
  • 200609      200611    up
  • 200612      200612    down
  • */




--VSalesRN视图给每个月上编号

view plaincopy to clipboardprint?

  • IF OBJECT_ID('dbo.VSalesRN') IS NOT NULL  
  •    DROP VIEW dbo.VSalesRN;  
  • GO  
  • CREATE VIEW dbo.VSalesRN  
  • AS  
  • SELECT mnth, qty, ROW_NUMBER() OVER(ORDER BY mnth) AS rn  
  • FROM dbo.Sales;  
  • GO  
  • --VSgn视图利用sign函数和自连接,求出sgn标识趋势  
  • IF OBJECT_ID('dbo.VSgn') IS NOT NULL  
  •   DROP VIEW dbo.VSgn;  
  • GO  
  • CREATE VIEW dbo.VSgn  
  • AS  
  • SELECT Cur.mnth, Cur.qty, isnull(SIGN(Cur.qty - Prv.qty),2) AS sgn  
  • FROM dbo.VSalesRN AS Cur   
  •    left  JOIN dbo.VSalesRN AS Prv  
  •      ON Cur.rn = Prv.rn + 1;  



  --VGrp视图根据对标识的分组,取出最后标定范围的分组因子grp


view plaincopy to clipboardprint?

  • IF OBJECT_ID('dbo.VGrp') IS NOT NULL  
  •   DROP VIEW dbo.VGrp;  
  • GO  
  • create  VIEW dbo.VGrp  
  • AS  
  • SELECT mnth, sgn,  
  •   DATEADD(month,  
  •     -ROW_NUMBER() OVER(PARTITION BY sgn ORDER BY mnth),  
  •     mnth) AS grp  
  • FROM dbo.VSgn;  
  • GO  




--VTrends视图 根据sgn 和 grp分组 求出自己范围内的最大最小值 并且标识出对应的中文表示

view plaincopy to clipboardprint?

  • IF OBJECT_ID('dbo.VTrends') IS NOT NULL  
  •    DROP VIEW dbo.VTrends;  
  • GO  
  • CREATE VIEW dbo.VTrends  
  • AS  
  • SELECT   
  •    CONVERT(VARCHAR(6), MIN(mnth), 112) AS start_range,  
  •    CONVERT(VARCHAR(6), MAX(mnth), 112) AS end_range,  
  •    CASE sgn  
  •      WHEN -1 THEN 'down'
  •      WHEN  0 THEN 'same'
  •      WHEN  1 THEN 'up'
  •      ELSE  'unknown'
  •    END AS trend  
  • FROM dbo.VGrp  
  • GROUP BY sgn, grp;  



----------检索视图------------

view plaincopy to clipboardprint?

  • SELECT start_range, end_range, trend  
  • FROM dbo.VTrends  
  • ORDER BY start_range;  





5.视图的更新 update
视图不仅可以修改,而且可以更新.因为对它操作时会影响基础表的。
这里我就说一个注意点:当你的视图是由2个表链接查询出来的,而你做的操作是更新的时候,要注意了:会出现状况:

view plaincopy to clipboardprint?

  • SET NOCOUNT ON;  
  • USE tempdb;  
  • GO  
  • IF OBJECT_ID('dbo.VCustOrders') IS NOT NULL  
  •   DROP VIEW dbo.VCustOrders;  
  • GO  
  • IF OBJECT_ID('dbo.Orders') IS NOT NULL  
  •   DROP TABLE dbo.Orders;  
  • GO  
  • IF OBJECT_ID('dbo.Customers') IS NOT NULL  
  •   DROP TABLE dbo.Customers;  
  • GO  
  • ---建Customers表  
  • CREATE TABLE dbo.Customers  
  • (  
  •   cid   INT         NOT NULL PRIMARY KEY,  
  •   cname VARCHAR(25) NOT NULL  
  • )  
  • INSERT INTO dbo.Customers(cid, cname) VALUES(1, 'Cust 1');  
  • INSERT INTO dbo.Customers(cid, cname) VALUES(2, 'Cust 2');  
  • ---建Orders 表  
  • CREATE TABLE dbo.Orders  
  • (  
  •   oid INT NOT NULL PRIMARY KEY,  
  •   cid INT NOT NULL REFERENCES dbo.Customers  
  • )  
  • INSERT INTO dbo.Orders(oid, cid) VALUES(1001, 1);  
  • INSERT INTO dbo.Orders(oid, cid) VALUES(1002, 1);  
  • INSERT INTO dbo.Orders(oid, cid) VALUES(1003, 1);  
  • INSERT INTO dbo.Orders(oid, cid) VALUES(2001, 2);  
  • INSERT INTO dbo.Orders(oid, cid) VALUES(2002, 2);  
  • INSERT INTO dbo.Orders(oid, cid) VALUES(2003, 2);  
  • GO  
  • -----创建视图VCustOrders 显示2表的链接内容  
  • CREATE VIEW dbo.VCustOrders  
  • AS  
  • SELECT C.cid, C.cname, O.oid  
  • FROM dbo.Customers AS C  
  •   JOIN dbo.Orders AS O  
  •     ON O.cid = C.cid;  
  • GO  



--显示数据
view plaincopy to clipboardprint?

  • SELECT cid, cname, oid FROM dbo.VCustOrders;  
  • GO  
  • /*
  • cid         cname                     oid
  • ----------- ------------------------- -----------
  • 1           Cust 1                    1001
  • 1           Cust 1                    1002
  • 1           Cust 1                    1003
  • 2           Cust 2                    2001
  • 2           Cust 2                    2002
  • 2           Cust 2                    2003
  • */




--更新:我们想把订单号为1001的那个公司名cname 改个名字,初期是只想改这么个订单(这是本来想做的)

view plaincopy to clipboardprint?

  • UPDATE dbo.VCustOrders  
  •    SET cname = 'Cust 42'
  • WHERE oid = 1001;  
  • GO  
  • --再次查询视图  
  • SELECT cid, cname, oid FROM dbo.VCustOrders;  
  • GO  
  • /*
  • cid         cname                     oid
  • ----------- ------------------------- -----------
  • 1           Cust 42                   1001
  • 1           Cust 42                   1002
  • 1           Cust 42                   1003
  • 2           Cust 2                    2001
  • 2           Cust 2                    2002
  • 2           Cust 2                    2003
  • */



------其实这里很好理解,修改的是表Customers 而不是视图 所以改了再查视图 视图又是从表那边链接过来 当然三个就一起变了----

6.视图的一些选项


a.ENCRYPTION

  对触犯器 视图 UDF 存储过程的创建文本可以加密.
  不过听说已经找到解密的方法http://www.360doc.com/content/050909/13/717_11438.html

b.SCHEMABINDING
  把视图绑定到一个基对象的框架上,SQL将不允许删除基对象或者修改视图里引用的列

view plaincopy to clipboardprint?

  • create table  tb (a int)  
  • insert tb values(1)   
  • insert tb values(2)   
  • IF OBJECT_ID('dbo.VCustsWithOrders') IS NOT NULL  
  •   DROP VIEW dbo.VCustsWithOrders;  
  • GO  
  • CREATE VIEW dbo.VCustsWithOrders WITH  SCHEMABINDING  
  • AS  
  • select A from dbo.tb   -----这里必要用dbo.tb来表示tb表  
  • GO  
  • --尝试修改表结构  
  • ALTER TABLE dbo.tb DROP COLUMN a;  
  • GO  
  • /*
  • 对象'VCustsWithOrders' 依赖于 列'a'。
  • */




c.check option

  使用了这个选项的视图会防止与视图的查询筛选器有冲突的INSERT 和 UPDATE 操作.举个例子:

view plaincopy to clipboardprint?

  • create table v(a int)  
  • insert v select 8   
  • insert v select 2   
  • insert v select 3   
  • insert v select 4   
  • go  
  • create  view v_v   
  • as
  • select * from v where a>2  
  • with check option  
  • go  
  • --insert操作  
  • insert v_v  select 6--成功  
  • insert v_v  select 1--失败,视图定义了check option 而规定a>2  
  • --update操作  
  • update v_v seta=1 where A=4--错误了,更新完的a值为1 小于2 所以不符合视图where条件  
  • update v_v seta=3 where A=4--成功  
  • --delete操作 肯定成功 因为它是删除掉》。。  




4.索引视图 indexed_view

一个没有索引的视图只不过是一个虚拟的表,拥有了索引,视图等于获得新生,他所包含的数据将要物理化,就要变成真正的表。
它会同步于基础表,就是说你修改基础表会同时更新索引视图,这里修改的性能会降低的.
但是索引视图同样提高了一些聚合查询和高成本的链接性能.
创建索引的限制:

a.第一个索引必须是唯一聚集索引.
b.视图必须用SCHEMABINDING,要求看上面;
c.如果对视图的查询进行了聚合操作,它的SELECT列表必须包含COUNT_BIG(*)这个聚合函数

view plaincopy to clipboardprint?

  • CREATE VIEW dbo.VEmpOrders WITH SCHEMABINDING  
  • AS  
  • SELECT O.EmployeeID, SUM(OD.Quantity) AS TotalQty, COUNT_BIG(*) AS Cnt  
  • FROM dbo.Orders AS O  
  •   JOIN dbo.[Order Details] AS OD  
  •     ON OD.OrderID = O.OrderID  
  • GROUP BY O.EmployeeID;  
  • GO  
  • CREATE UNIQUE CLUSTERED INDEX idx_uc_empid ON dbo.VEmpOrders(EmployeeID);  
  • GO  



  ---上面这个视图就是一个典型的索引视图---

  ---索引视图有个好处,比如你在外部基础表上查询,注意基础表上是没索引的,但是你可以再查询时候用到视图的索引.
  
view plaincopy to clipboardprint?

  • SELECT O.EmployeeID, SUM(OD.Quantity) AS TotalQty, AVG(OD.Quantity) AS AvgQty, COUNT_BIG(*) AS Cnt  
  •   FROM dbo.Orders AS O  
  •     JOIN dbo.[Order Details] AS OD  
  •       ON OD.OrderID = O.OrderID  
  •   GROUP BY O.EmployeeID;  


  
--这个查询就是用到了视图上的聚集索引,而且AVG(OD.Quantity)这个视图里不包含的聚合函数也用到了-----

d.还有几个SQL2005用到索引视图的情况:


  1.当外部查询的where 条件或者ON 条件的筛选区间是试图筛选器的子区间的时候,查询可以用到索引视图.
   比如:外部查询n<100 视图查询n<200

  2.外部查询使用一个与视图查询等价的逻辑筛选条件
   比如:外部 n=100 视图100=n

  3.关于NULL值插入
   你使用UNIQUE约束想约束表中非NULL值要唯一,但是NULL值可以多次插入,但是UNIQUE约束认为NULL值是相等的,所以你在表中该列
   已经存在NULL值情况下是不能再次插入的.这个时候你就可以使用索引视图解决这个问题
   
view plaincopy to clipboardprint?

  • USE tempdb;  
  •    GO  
  •    IF OBJECT_ID('dbo.V1') IS NOT NULL  
  •      DROP VIEW dbo.V1;  
  •    GO  
  •    IF OBJECT_ID('dbo.T1') IS NOT NULL  
  •      DROP TABLE dbo.T1;  
  •    GO  
  •    CREATE TABLE dbo.T1  
  •    (  
  •      keycol  INT         NULL,  
  •      datacol VARCHAR(10) NOT NULL  
  •    );  
  •    GO  
  •    CREATE VIEW dbo.V1 WITH SCHEMABINDING  
  •    AS  
  •    SELECT keycol FROM dbo.T1 WHERE keycol IS NOT NULL--注意这里的where 条件  
  •    GO  
  •    CREATE UNIQUE CLUSTERED INDEX idx_uc_keycol ON dbo.V1(keycol);  
  •    GO  
  •    -- 插入数据  
  •    INSERT INTO dbo.T1(keycol, datacol) VALUES(1,    'a');  
  •    INSERT INTO dbo.T1(keycol, datacol) VALUES(1,    'b'); -- 这条失败的  
  •    INSERT INTO dbo.T1(keycol, datacol) VALUES(NULL, 'c');  
  •    INSERT INTO dbo.T1(keycol, datacol) VALUES(NULL, 'd');  
  •    GO  
  •    --进行查询  
  •    SELECT keycol, datacol FROM dbo.T1;  
  •    GO  
  •    /*
  •    keycol      datacol
  •    ----------- ----------
  •    1           a
  •    NULL        c
  •    NULL        d
  •    */


   
----视图索引保证不准插入重复值,但是因为WHERE keycol IS NOT NULL 所以它没有限定NULL的重复性.
   ------这里也再次说明对基础表的操作又一次牵动了视图索引------------
   

 

posted on 2012-04-09 14:44  RJ  阅读(355)  评论(0)    收藏  举报

导航