SQL中視圖整理

一、視圖:

從表中抽出來的邏輯上相關的數據集合
視圖其實就是一條查詢SQL語句,用於顯示一個或多個表或其它視圖中相關數據。
視圖將查詢的結果作爲一個表來使用,因此視圖可以被看作是存儲的查詢或一個虛擬表
視圖來源於表,所有對視圖數據的修改最終都會被反映到視圖的基表中,這些修改必須服從基表的完整性約束,並同樣會觸發定義
在基表上的觸發器。(Oracle支持在視圖上顯式的定義觸發器和定義一些邏輯約束)

使用視圖的好處:
可把複雜的SQL語句簡單化
可保證數據的安全性,限制對數據的訪問,因爲它對錶中的一些字段是隱藏的
可使相同的數據以不同形式出現在不同的視圖中

視圖分類:
簡單視圖
只從一個表中獲取數據
不包含函數
不包含分組數據
可通過該視圖進行DML操作

複雜視圖
從多個表中獲取數據
包含函數
包含分組數據
不一定能通過視圖進行DML操作

創建視圖:
CREATE [OR REPLACE] [FORCE |NOFORCE ]VIEW view_name [alias [,alias]...]
AS subquery
[WITH CHECK OPTION]
[WITH READ ONLY]

OR REPLACE 如果視圖存在,重建、修改這個視圖
FORCE 不管引用的表是否存在,都創建這個視圖
NOFORCE 只有當表存在的時候,才能創建這個視圖(默認方式)
WITH CHECK OPTION 只有子查詢能夠檢索出的行才能夠被插入,修改,或刪除。默認情況下對此不作檢查
沒有指定約束名,系統會自動爲約束命名,形式爲SYS_Cn。
WITH READ ONLY 只讀,不可對視圖做DML操作

需要注意的是,在子查詢中不能包含ORDER BY ,子查詢可以是複雜的SELECT語句


修改視圖
使用CREATE OR REPLACE VIEW 子句修改視圖

刪除視圖:
DROP VIEW view_name

視圖中使用DML的規定:
當視圖定義中含有以下元素之一不能使用INSERT
組函數
GROUP BY、ORDER BY
DISTINCT
ROWNUM
列的定義爲表達式
表中非空的列,在視圖定義中未包括

視圖定義含有以下元素不能使用UPDATE
組函數
GROUP BY、ORDER BY
DISTINCT
ROWNUM
列的定義爲表達式

視圖包含以下元素不能DELETE
組函數
GROUP BY、ORDER BY
DISTINCT
ROWNUM


與視圖有關的數據字典:
DBA_VIEWS
USER_VIEWS

--演示創建視圖
SQL> create or replace view vw_emp as
2 select empno,ename,sal from emp where sal >2500;

View created.

SQL> select * from vw_emp;

EMPNO ENAME SAL
---------- ---------- ----------
7566 JONES 2975
7698 BLAKE 2850
7788 SCOTT 3000
7839 KING 5000
7902 FORD 3000

SQL> update vw_emp set sal = 3500 where ename = 'FORD'; --可以更新並且直接修改了基表

1 row updated.

SQL> select * from emp where ename = 'FORD';

EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
7902 FORD ANALYST 7566 03-DEC-81 3500 20

SQL> insert into vw_emp select 9999,'Robinson',6000 from dual; --可以插入並且直接修改了基表

1 row created.

SQL> select * from emp where empno = 9999;

EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
9999 Robinson 6000

1 rows selected.

SQL> delete from emp where ename = 'Robinson'; --可以刪除並且直接修改了基表

1 row deleted.

SQL> select * from vw_emp;

EMPNO ENAME SAL
---------- ---------- ----------
7566 JONES 2975
7698 BLAKE 2850
7788 SCOTT 3000
7839 KING 5000
7902 FORD 3500

SQL> rollback;

Rollback complete.


--使用with check option
--使用WITH CHECK OPTION 子句確保DML只能在特定的範圍內執行,任何違反
--WITH CHECK OPTION 約束的請求都會失敗

SQL> create or replace view vw_emp
2 as
3 select empno,ename,sal from emp where sal > 2500
4 with check option;

View created.

SQL> insert into vw_emp select 9999,'Robinson',2000 from dual; --不滿足條件sal > 2500 不可插入
insert into vw_emp select 9999,'Robinson',2000 from dual
*
ERROR at line 1:
ORA-01402: view WITH CHECK OPTION where-clause violation


SQL> select * from vw_emp;

EMPNO ENAME SAL
---------- ---------- ----------
7566 JONES 2975
7698 BLAKE 2850
7788 SCOTT 3000
7839 KING 5000
7902 FORD 3000

SQL> delete from vw_emp where empno = 7902; -- 滿足條件sal > 2500 可以刪除,並且修改了基表

1 row deleted.

SQL> select * from vw_emp;

EMPNO ENAME SAL
---------- ---------- ----------
7566 JONES 2975
7698 BLAKE 2850
7788 SCOTT 3000
7839 KING 5000

SQL> select * from emp;

EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
7369 SMITH CLERK 7902 17-DEC-80 800 20
7499 ALLEN SALESMAN 7698 20-FEB-81 1600 300 30
7521 WARD SALESMAN 7698 22-FEB-81 1250 500 30
7566 JONES MANAGER 7839 02-APR-81 2975 20
7654 MARTIN SALESMAN 7698 28-SEP-81 1250 1400 30
7698 BLAKE MANAGER 7839 01-MAY-81 2850 30
7782 CLARK MANAGER 7839 09-JUN-81 2450 10
7788 SCOTT ANALYST 7566 19-APR-87 3000 20
7839 KING PRESIDENT 17-NOV-81 5000 10
7844 TURNER SALESMAN 7698 08-SEP-81 1500 0 30
7876 ADAMS CLERK 7788 23-MAY-87 1100 20

EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
7900 JAMES CLERK 7698 03-DEC-81 950 30
7934 MILLER CLERK 7782 23-JAN-82 1300 10

13 rows selected.

SQL> rollback;

Rollback complete.

SQL> select * from vw_emp;

EMPNO ENAME SAL
---------- ---------- ----------
7566 JONES 2975
7698 BLAKE 2850
7788 SCOTT 3000
7839 KING 5000
7902 FORD 3000

SQL> update vw_emp set sal = 2000 where empno = 7499; --不滿足條件,不能更新

0 rows updated.

--WITH READ ONLY 屏蔽DML 操作
--可以使用WITH READ ONLY 選項屏蔽對視圖的DML操作
--任何DML 操作都會返回一個Oracle server 錯誤

SQL> create or replace view vw_emp
2 as
3 select empno,ename,sal from emp where sal > 2500
4 with read only;

View created.

SQL> insert into vw_emp select 9901,'Robinson',3000 from dual;
insert into vw_emp select 9901,'Robinson',3000 from dual
*
ERROR at line 1:
ORA-01733: virtual column not allowed here


SQL> select * from vw_emp;

EMPNO ENAME SAL
---------- ---------- ----------
7566 JONES 2975
7698 BLAKE 2850
7788 SCOTT 3000
7839 KING 5000
7902 FORD 3000

SQL> update vw_emp set sal = 8000 where empno = 7902;
update vw_emp set sal = 8000 where empno = 7902
*
ERROR at line 1:
ORA-01733: virtual column not allowed here


SQL> delete from vw_emp where empno = 7566;
delete from vw_emp where empno = 7566
*
ERROR at line 1:
ORA-01752: cannot delete from view without exactly one key-preserved table

--創建複雜視圖
SQL> create view vw_sum_emp(name,minsal,maxsal,avgsal)
2 as
3 select dname,min(e.sal),max(e.sal),avg(e.sal)
4 from emp e
5 join dept d
6 on e.deptno = d.deptno
7 group by dname;

View created.

SQL> select * from vw_sum_emp;

NAME MINSAL MAXSAL AVGSAL
-------------- ---------- ---------- ----------
ACCOUNTING 1300 5000 2916.66667
RESEARCH 800 3000 2175
SALES 950 2850 1566.66667

--查詢與視圖有關的數據字典
SQL> select view_name,text from user_views;

VIEW_NAME TEXT
------------------------------ --------------------------------------------------------------------------------
VW_SUM_EMP select dname,min(e.sal),max(e.sal),avg(e.sal)
from emp e
join dept d
on e.deptno = d.deptno
group by dname

VW_EMP select empno,ename,sal from emp where sal > 2500
with read only

更詳細的創建視圖的語法:
http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/statements_8004.htm#SQLRF01504

更多參考:

Oracle 數據庫實例啓動關閉過程

Oracle 10g SGA 的自動化管理

使用OEM,SQL*Plus,iSQL*Plus 管理Oracle實例

Oracle實例和Oracle數據庫(Oracle體系結構)

SQL 基礎-->常用函數

SQL基礎-->過濾和排序

SQL 基礎-->SELECT 查詢


二、在sql中進行增刪改

1. 創建修改視圖的例子
SQL> CREATE OR REPLACE VIEW salvu41 AS SELECT id, first_name FIRST,
   last_name LAST, salary MONTHLY_SALARY
    FROM s_emp WHERE dept_id = 41;
SQL> CREATE VIEW dept_sum_vu (name, minsal, maxsal, avgsal) AS SELECT d.name, MIN(e.salary), MAX(e.salary),
AVG(e.salary) FROM s_emp e, s_dept d WHERE e.dept_id = d.id GROUP BY d.name;
注意: 如果用select * from table_name創建的視圖
   table_name的結構改變後 view要重建或compile後才能顯示新的字段內容
2. 查詢視圖的數據字典
SQL> set   long   20000 
set linesize 800
set pagesize 100
SQL> select view_name,text from user_views;
說明: 可以根據視圖text_length來設置set long 數字;
User_updatable_columns視圖能查詢視圖裏能被修改的字段

  Lesktop開源IM發佈以後,有一些網友問及如何在嵌入IM後與自己網站的用戶系統整合(即如何讓嵌入的IM直接使用網站原有的用戶數據庫,而不需要將已有的用戶數據導入到IM的數據庫中)。Lesktop對Users表(存儲用戶登錄名,暱稱,密碼等信息的表)都是在存儲過程中進行增刪改的,顯然,如果直接去改Users表相關的存儲過程是比較麻煩的,本文將介紹一種較爲簡單的方法,在不需要修改存儲過程和源代碼的情況下整合用戶系統。

  爲實現這個目的,先介紹一下在SQL SERVER中,如何對視圖進行增刪改。假使用戶有Name,Remark兩項信息,但是沒有存放在同一張表中,而是分開存儲在兩個表UserBase(ID, Name),UserExtent(ID, Remark)中。

image

  爲使用方便,建立一個視圖Users,用於表示用戶的完整信息,其定義如下:

CREATE VIEW [dbo].[Users]
as
SELECT b.ID as ID, b.Name as Name, e.Remark as Remark
FROM UserBase b, UserExtent e
WHERE b.ID = e.ID;

  現在,我們希望通過Users視圖進行增刪改實現對UserBase,UserExtent表進行修改。顯然,如果對Users直接執行insert,update,delete是不可能的,執行時會發生以下錯誤:

image

  在SQL Server中,對視圖增刪改可以通過觸發器來實現,例如我們可以創建一個INSERT觸發器,當在視圖Users上執行INSERT時,在觸發器中實現對UserBase,UserExtent的INSERT操作。在觸發器中,可以通過名稱爲inserted的表,獲取到新插入的行,具體代碼如下:

CREATE TRIGGER [dbo].[Users_Insert] ON [dbo].[Users] INSTEAD OF INSERT
as
declare @name nvarchar(32), @remark nvarchar(32)
declare ins_cursor cursor
for
select Name, Remark from inserted
open ins_cursor
fetch next from ins_cursor into @name, @remark;
while(@@fetch_status = 0)
begin
--讀取所有行,並插入
insert into UserBase (Name) values (@name);
insert into UserExtent(ID, Remark) values (@@identity, @remark);
fetch next from ins_cursor into @name, @remark;
end
close ins_cursor

  下面我們通過插入兩行數據測試觸發器:

--清空所有數據
delete from UserExtent;
delete from UserBase;

create table #temp(
name
nvarchar(32),
remark
nvarchar(32)
)
insert #temp (name,remark) values (N'user1', N'1');
insert #temp (name,remark) values (N'user2', N'2');

--插入兩行數據
insert Users(name, remark)
select name,remark from #temp

drop table #temp

select * from Users;
select * from UserBase;
select * from UserExtent;

  執行結果如下:

image

  創建更新觸發器,與INSERT觸發器類似,受影響的行會保存在inserted中,可以從inserted表中獲取受影響的行,並更新UserBase,UserExtent,具體代碼如下:

CREATE TRIGGER [dbo].[Users_Update] ON [dbo].[Users] INSTEAD OF UPDATE
as
update UserExtent
set UserExtent.Remark=ins.Remark
from inserted ins
where UserExtent.ID = ins.ID;

update UserBase
set UserBase.Name=ins.Name
from inserted ins
where UserBase.ID = ins.ID;

  測試代碼:

--清空所有數據
delete from UserExtent;
delete from UserBase;
--插入兩行數據
insert Users (name,remark) values (N'user1', N'1');
insert Users (name,remark) values (N'user2', N'2');
insert Users (name,remark) values (N'user3', N'2');
--修改後兩行數據
UPDATE Users set Remark = N'3' where Remark = N'2'
--輸出數據
select * from Users;
select * from UserBase;
select * from UserExtent;

  測試結果:

image

  創建刪除觸發器,在刪除的觸發器中,可以通過deleted表,獲取被刪除的行,具體代碼如下:

CREATE TRIGGER [dbo].[Users_Delete] ON [dbo].[Users] INSTEAD OF DELETE
as
delete from UserExtent where ID in (select ID from deleted)
delete from UserBase where ID in (select ID from deleted)

  測試代碼:

--清空所有數據
delete from UserExtent;
delete from UserBase;
--插入兩行數據
insert Users (name,remark) values (N'user1', N'1');
insert Users (name,remark) values (N'user2', N'2');
insert Users (name,remark) values (N'user3', N'2');
--刪除後兩行數據
delete from Users where Remark = N'2'
--輸出數據
select * from Users;
select * from UserBase;
select * from UserExtent;

  運行結果:

image

  上文已介紹瞭如何對視圖進行增刪改,接下來將介紹如何通過建立視圖並添加增刪改觸發器實現Lesktop開源IM用戶系統的整合。首先介紹一下Lesktop開源IM數據庫中Users表的結構:

image

  假使您的網站的用戶表(假使名稱爲MyUserTable)只有Name,Nickname:

image

  那麼,您可以建立一張擴展表(假使名稱爲UserExtentIM),用於存儲其他信息:

image

  接下來,您只需要把Users表刪掉,重新建立一個名稱爲Users的視圖,然後用上文處理Users,UserBase,UserExtent的方法,在Users視圖上建好觸發器,在觸發器中對MyUserTable,UserExtentIM表進行增刪改即可,Lesktop的存儲過程對User進行讀取和增刪改時,將通過觸發器自動轉換成對MyUserTable,UserExtentIM的操作,因此不需要修改任何存儲過程和源代碼,當然也不會對你原有的數據庫造成影響

三、SQL Server視圖管理的四大限制條件
SQL Server視圖其優點是非常明顯的,但是視圖並不是萬能的,在使用視圖的時候,需要遵守一些限制,下面就讓我們一起來了解一下。
SQL Server中通過SQL Server視圖來訪問數據,其優點是非常明顯的,如可以起到數據保密、保證數據的邏輯獨立性、簡化查詢操作等等。
但是,話說回來,SQL Server數據庫中的視圖並不是萬能的,他跟表這個基本對象還是有重大的區別。在使用視圖的時候,需要遵守四大限制。


限制條件一:SQL Server視圖數據的更改

當用戶更新視圖中的數據時,其實更改的是其對應的數據表的數據。無論是對視圖中的數據進行更改,還是在視圖中插入或者刪除數據,都是類似的道理。但是,不是所有視圖都可以進行更改。如下面的這些視圖,在SQL Server數據庫中就不能夠直接對其內容進行更新,否則,系統會拒絕這種非法的操作。

如在一個視圖中,若採用Group By子句,對視圖中的內容進行了彙總。則用戶就不能夠對這張視圖進行更新。這主要是因爲採用Group By子句對查詢結果進行彙總在後,視圖中就會丟失這條紀錄的物理存儲位置。如此,系統就無法找到需要更新的紀錄。若用戶想要在視圖中更改數據,則數據庫管理員就不能夠在視圖中添加這個Group BY分組語句。

如不能夠使用Distinct關鍵字。這個關鍵字的用途就是去除重複的紀錄。如沒有添加這個關鍵字的時候,視圖查詢出來的紀錄有250條。添加了這個關鍵字後,數據庫就會剔除重複的紀錄,只顯示不重複的50條紀錄。此時,若用戶要改變其中一個數據,則數據庫就不知道其到底需要更改哪條紀錄。因爲視圖中看起來只有一條紀錄,而在基礎表中可能對有的紀錄有幾十條。爲此,若在視圖中採用了Distinct關鍵字的話,就無法對視圖中的內容進行更改。


如果在SQL Server視圖中有AVG、MAX等函數,則也不能夠對其進行更新。如在一張視圖中,其採用了SUN函數來彙總員工的工資時,此時,就不能夠對這張表進行更新。這是數據庫爲了保障數據一致性所添加的限制條件。


可見,試圖雖然方便、安全,但是,其仍然不能夠代替表的地位。當需要對一些表中的數據進行更新時,我們往往更多的通過對錶的操作來完成。因爲對視圖內容進行直接更改的話,需要遵守一些限制條件。在實際工作中,更多的處理規則是通過前臺程序直接更改後臺基礎表。至於這些表中數據的安全性,則要依靠前臺應用程序來保護。確保更改的準確性、合法性。

限制條件二:定義視圖的查詢語句中不能夠使用某些關鍵字

我們都知道,視圖其實就是一組查詢語句組成。或者說,視圖是封裝查詢語句的一個工具。在查詢語句中,我們可以通過一些關鍵字來格式化顯示的結果。如我們在平時工作中,經常會需要把某張表中的數據跟另外一張表進行合併。此時,數據庫管理員就可以利用Select Into語句來完成。先把數據從某個表中查詢出來,然後再添加到某個表中。

當經常需要類似的操作時,我們是否可以把它製作成一張視圖。每次有需要的時候,只需要運行這個視圖即可,而不用每次都進行重新書寫SQL代碼。不過可惜的是,結果是否定的。在SQL Server數據庫的視圖中,是不能夠帶有Into關鍵字。如果要實現類似的功能,只有通過函數或者過程來實現。

另外,跟Oracle數據庫不同的是,在微軟的SQLServer數據庫中創建視圖的時候,還有一個額外的限制。就是不能夠在創建視圖的查詢語句中,使用order by排序語句。這是一個很特殊的規定。一些Oracle的數據庫管理員,在使用SQL Server數據庫創建視圖的時候,經常會犯類似的錯誤。他們就搞不明白,爲什麼Oracle數據庫中可行,但是在微軟的數據庫中則行不通呢?這恐怕只有微軟數據庫產品的設計者才能夠回答的問題。總之我們要記住的就是,在SQLServer數據庫中,建立視圖時,查詢語句中不能夠包含Order By語句。

限制條件三:要對某些列取別名,並保證列名的唯一

在表關聯查詢的時候,當不同表的列名相同時,只需要加上表的前綴即可。不需要對列另外進行命名。但是,在創建視圖時就會出現問題,數據庫會提示 “duplicate column name”的錯誤提示,警告用戶有重複的列名。有時候,用戶利用Select語句連接多個來自不同表的列,若擁有相同的名字,則這個語句仍然可以執行。但是,若把它複製到創建視圖的窗口,創建視圖時,就會不成功。

查詢語句跟創建SQL Server視圖的查詢語句還有很多類似的差異。如有時候,我們在查詢語句中,可能會比較頻繁的採用一些算術表達式;或者在查詢語句中使用函數等等。在查詢的時候,我們可以不給這個列“取名”。數據庫在查詢的時候,會自動給其命名。但是,在創建視圖時,數據庫系統就會給你出難題。系統會提醒你爲列取別名。

從以上兩個例子中,我們可以看出,雖然視圖是對SQL語句的封裝,但是,兩者仍然有差異。創建視圖的查詢語句必須要遵守一定的限制。如要保證視圖的各個列名的唯一;如果自阿視圖中某一列是一個算術表達式、函數或者常數的時候,要給其取名字,等等。

限制條件四:權限上的雙重限制

爲了保障基礎表數據的安全性,在視圖創建的時候,其權限控制比較嚴格。

一方面,若用戶需要創建SQL Server視圖,則必須要有數據庫視圖創建的權限。這是視圖建立時必須遵循的一個基本條件。如有些數據庫管理員雖然具有表的創建、修改權限;但是,這並不表示這個數據庫管理員就有建立視圖的權限。恰恰相反,在大型數據庫設計中,往往會對數據庫管理員進行分工。建立基礎表的就只管建立基礎表;負責創建視圖的就只有創建視圖的權限。

其次,在具有創建視圖權限的同時,用戶還必須具有訪問對應表的權限。如某個數據庫管理員,已經有了創建視圖的權限。此時,若其需要創建一張員工工資信息的視圖,還不一定會成功。這還要這個數據庫管理員有美譽跟工資信息相關的基礎表的訪問權限。如建立員工工資信息這張視圖一共涉及到五張表,則這個數據庫管理員就需要擁有者每張表的查詢權限。若沒有的話,則建立這張視圖就會以失敗告終。

第三,就是視圖權限的繼承問題。如上面的例子中,這個數據庫管理員不是基礎表的所有者。但是經過所有者的授權,他就可以對這個基礎表進行訪問,就可以以此爲基礎建立視圖。但是,這個數據庫管理員有沒有把對這個基礎表的訪問權限再授權給其他人呢?如他能否授權給A用戶訪問員工考勤信息表呢?答案是不一定。默認情況下,數據庫管理員不能夠再對其他用戶進行授權。但是,若基礎表的所有者,把這個權利給了數據庫管理員之後,則他就可以對用戶進行重新授權。讓數據庫管理員可以給A用戶進行授權,讓其可以進行相關的操作。

可見,視圖雖然靈活,安全,方便,但是其仍然有比較多的限制條件。根據筆者的經驗,一般在報表、表單等等工作上,採用視圖會更加的合理。因爲其 SQL語句可以重複使用。而在基礎表更新上,包括紀錄的更改、刪除或者插入上,往往是直接對基礎表進行更新。對於一些表的約束,可以通過觸發器、規則等等來實現;甚至可以通過前臺SQL語句直接實現約束。作爲數據庫管理員,要有這個能力,能夠判斷在什麼時候使用視圖,什麼時候直接調用基礎表。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章