MySQL筆記(四)——視圖

視圖

視圖是一種虛擬存在的,並不在數據庫中實際存在,它的列和數據行都是對原查詢中使用的表的列和數據行的映射。由於視圖是虛擬的表,它並沒有存放任何數據,只有在使用視圖時,纔會從原表中動態地生成數據。對於用戶來說,視圖真實使用的表基本上是透明的。

視圖操作

視圖的操作主要是創建、修改、更新、刪除和查詢,語句都和表的操作類似。筆記中以MySQL官方的demo庫sakila來做練習。

創建

創建視圖的用戶需要擁有create view的權限,並且對於視圖中查詢語句涉及的表擁有select權限。
create view view_name [(col_list)] as select_statement [with [cascaded | local] check option];
例子:查詢city表中所有country_id爲23的城市id、名稱,對這一查詢創建視圖。
create view city_list(city_id, city_name) as
select city_id, city
from
sakila.city
where
country_id=23;

使用這個視圖的方式和查表類似,因爲對於用戶而言,視圖就相當於表:
select * from sakila.city_list limit 10;
±--------±----------+
| city_id | city |
±--------±----------+
| 46 | Baicheng |
| 47 | Baiyin |
| 80 | Binzhou |
| 109 | Changzhou |
| 136 | Datong |
| 139 | Daxian |
| 145 | Dongying |
| 157 | Emeishan |
| 159 | Enshi |
| 166 | Ezhou |
±--------±----------+
10 rows in set (0.00 sec)

修改

修改視圖和修改表類似,使用alter view語句。
例子:修改上文的視圖city_list,在原視圖的基礎上增加一列,查詢城市對應的國家名稱。
alter view city_list(city_id, city_name, country_name) as
select
city.city_id, city.city, country.country
from
sakila.city
join
sakila.country
on city.country_id=country.country_id
where
city.country_id=23;

再來查詢一下:
select * from sakila.city_list limit 10;
±--------±----------±--------+
| city_id | city | country |
±--------±----------±--------+
| 46 | Baicheng | China |
| 47 | Baiyin | China |
| 80 | Binzhou | China |
| 109 | Changzhou | China |
| 136 | Datong | China |
| 139 | Daxian | China |
| 145 | Dongying | China |
| 157 | Emeishan | China |
| 159 | Enshi | China |
| 166 | Ezhou | China |
±--------±----------±--------+
10 rows in set (0.00 sec)

更新

更新視圖,是指通過視圖來插入、更新或刪除表中的數據,其過程實質上是對視圖更新數據,再由視圖通過映射將數據寫入真實的表中,視圖相當於是用戶與真實表之間的橋樑。
例子:建立用於查詢payment表中所有amount>5的數據行的視圖,查詢結果集包括payment_id、customer_id、amount,並使用這個視圖來更新數據。
先建立視圖:
create view payment_list(payment_id, customer_id, amount) as
select
payment_id, customer_id, amount
from
payment
where
amount > 5;

查詢前10列:
select * from sakila.payment_list limit 10;
±-----------±------------±-------+
| payment_id | customer_id | amount |
±-----------±------------±-------+
| 3 | 1 | 5.99 |
| 5 | 1 | 9.99 |
| 10 | 1 | 5.99 |
| 11 | 1 | 5.99 |
| 14 | 1 | 7.99 |
| 32 | 1 | 5.99 |
| 36 | 2 | 6.99 |
| 38 | 2 | 5.99 |
| 39 | 2 | 5.99 |
| 40 | 2 | 5.99 |
±-----------±------------±-------+
10 rows in set (0.00 sec)
接下來更新視圖數據,令payment_id=3對應的行customer_id爲2:
update payment_list set customer_id=2 where payment_id=3;
查詢驗證:
select * from sakila.payment_list where payment_id=3;
±-----------±------------±-------+
| payment_id | customer_id | amount |
±-----------±------------±-------+
| 3 | 2 | 5.99 |
±-----------±------------±-------+
1 row in set (0.00 sec)
上文說過,視圖中是不含有數據的,更新視圖最終更新的還是查詢的表中的數據,爲了驗證我們還可以查一下原表:
select * from sakila.payment where payment_id=3;
±-----------±------------±---------±----------±-------±--------------------±--------------------+
| payment_id | customer_id | staff_id | rental_id | amount | payment_date | last_update |
±-----------±------------±---------±----------±-------±--------------------±--------------------+
| 3 | 2 | 1 | 1185 | 5.99 | 2005-06-15 00:54:12 | 2020-02-08 11:27:46 |
±-----------±------------±---------±----------±-------±--------------------±--------------------+
需要注意,有些情況下視圖是不可更新的:
1、查詢語句中包含聚合函數(sum、min、max、count等)、distinct、group by、having、union或者union all。
很容易理解,視圖對應的SQL語句中如果包含聚合函數,那麼視圖中的這一列並不對應任何表中的列,它只是一個動態計算出的值,自然不能更新這樣的視圖。通過一個例子來看一下:
create view test_customer_list(customer_id, payment_count) as
select
customer_id, count(customer_id)
from
payment
group by
customer_id;

update sakila.test_customer_list set payment_count=10 where customer_id=1;
ERROR 1288 (HY000): The target table test_customer_list of the UPDATE is not updatable
2、常量視圖
3、from一個不可更新的視圖。例如用上述視圖test_customer_list再創建一個視圖:
create view test_customer_list_2 as select * from test_customer_list;

update sakila.test_customer_list_2 set payment_count=10 where customer_id=1;
ERROR 1288 (HY000): The target table test_customer_list_2 of the UPDATE is not updatable
4、select中包含子查詢
5、對單表查詢創建的視圖,視圖沒有包含主鍵
6、對於多層視圖,創建視圖時的with [cascaded | local] check option也會影響視圖是否可以更新。
local滿足本視圖條件即可更新
以上文的視圖payment_list爲例,再對它創建一個視圖:
create view sakila.payment_list2 as select * from sakila.payment_list where amount < 10 with local check option;
嘗試更新視圖payment_list2,使payment_id=5對應記錄的amount=10。這顯然不滿足本表的條件,更新不會生效:
update sakila.payment_list2 set amount=10 where payment_id=5;
ERROR 1369 (HY000): CHECK OPTION failed ‘sakila.payment_list2’
再使payment_id=5對應記錄的amount=5,這雖然不滿足payment_list的條件,但由於payment_list2使用的是local選項,更新會生效:
update sakila.payment_list2 set amount=5 where payment_id=5;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0

cascaded必須滿足所有相關視圖的條件纔可更新
create view sakila.payment_list3 as select * from sakila.payment_list where amount < 10 with cascaded check option;
嘗試更新視圖payment_list3,使payment_id=3對應記錄的amount=3。這顯然不滿足payment_list的條件,更新不會生效:
update sakila.payment_list3 set amount=5 where payment_id=3;
ERROR 1369 (HY000): CHECK OPTION failed ‘sakila.payment_list3’

另外,還需要注意的是,由於視圖只是一張虛擬表,視圖中的數據行只是對查詢語句結果集的映射,利用視圖更新數據本質上是將數據寫回表中,但是在有的情況下,直接寫回可能會對錶造成預期外的影響,舉個例子,先將上面例子中的payment_list改寫一下,在原有的基礎上還要查詢customer的名字:
alter view payment_list(payment_id, customer_id, first_name, amount) as
select
payment.payment_id, payment.customer_id, customer.first_name, payment.amount
from
sakila.payment
join
sakila.customer
on payment.customer_id=customer.customer_id
where
amount > 5;

對於這個視圖,我們可以試想一下,視圖中payment_id、customer_id、amount這幾列來自於payment表,而first_name這一列來自於customer表,它們之間的關聯條件是customer_id。現在如果我們想更新視圖,使payment_id=5的那一行數據中的first_name爲‘Jack’:
update payment_list set first_name=‘Jack’ where payment_id=5;
如果這條更新語句生效的話,會發生什麼——可以查到payment_id=5這一行數據對應的customer_id爲1,於是customer表中的customer_id=1所對應的那一行的first_name列將被改爲‘Jack’;payment表中,還有很多customer_id=1的記錄,這一改動將導致所有的這些記錄對應的付款人名字都被改爲‘Jack’。這顯然不是我們的初衷,對於表操作,我們需要做的其實是修改payment_id=5所對應記錄的customer_id。因此在對視圖進行更新時,一定要慎重。

刪除視圖

用戶可以一次刪除多個視圖,語句和刪除表類似。用戶必須擁有drop權限。
drop view [if exists] view_name [, view_name] …;
刪除視圖payment_list2、payment_list3、test_customer_list:
drop view payment_list2, payment_list3, test_customer_list;

查看視圖

查看視圖基本信息

使用
desc view_name;
或者是
show table status [from db_name] [like ‘pattern’];

查看視圖創建語句

show create view view_name;

information_schema

所有數據庫的視圖信息都存放在information_schema庫的views表中,可以直接查詢:
select * from information_schema.views;

視圖的優勢

通過上文的例子,很容易總結出視圖相對於普通的表具有如下優勢:
1、簡單:用戶無需關心詳細的表結構、關聯條件和篩選條件,對用戶而言,視圖就是一個已經做好了篩選的結果集;
2、安全:表無法將用戶的訪問權限限制到某一行某一列,而對視圖而言,用戶只能訪問他們被允許訪問的結果集;
3、數據獨立:由視圖作爲用戶和實際查詢的表之間的橋樑,視圖相當於編程中所說的接口層,爲用戶提供封裝好的統一語句,使表結構的變化幾乎不對用戶造成影響,即實現表結構對一般用戶透明。

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