1.定义视图
视图是从一个或几个基本表(或视图)导出的表。它与基本表不同,是一个虚表。- 数据库中只存放视图的定义,而不存放视图对应的数据,这些数据仍存放在原来的基本表中。
- 视图一经定义,就可以和基本表一样被查询、被删除。也可以在视图之上再定义新的视图。但增删改有一定的限制。
创建语法格式:
CREATE VIEW 视图名称 AS 子查询
实例:创建 sales_by_client 视图
代码如下:
CREATE VIEW sales_by_client AS
SELECT
c.client_id,
c.name,
SUM(invoice_total) AS total_sales
FROM clients c
JOIN invoices i USING (client_id)
GROUP BY client_id
2.查询视图
- 视图定义后,用户可以像对基本表一样对视图进行查询
实例: 查看上方创建视图表的total_sales大于800的姓名
SELECT name
FROM sales_by_client
WHERE total_sales > 800
3.更新或删除视图
删除视图语法格式:
DREP VIEW 视图名称
实例:删除clients_balance视图
代码如下:
-- 若不存在这个视图,直接 DROP 会报错,所以要加上 IF EXISTS 先检测有没有这个视图
DROP VIEW IF EXISTS clients_balance;
- 视图作为虚拟表/衍生表,除了可用在查询语句SELECT中,也可以用在增删改(INSERT DELETE UPDATE)语句中,但后者有一定的前提条件。
如果一个视图的原始查询语句中没有如下元素:
- DISTINCT 去重
- GROUP BY/HAVING/聚合函数 (后两个通常是伴随着 GROUP BY 分组出现的)
- UNION 纵向连接
则该视图是可更新视图(Updatable Views),可以增删改,否则只能查。
另外,增(INSERT)还要满足附加条件:视图必须包含底层原表的所有必须字段
总之,一般通过原表修改数据,但当出于安全考虑或其他原因没有某表的直接权限时,可以通过视图来修改底层数据(?) ,前提是视图是可更新的。
实例:创建视图(新虚拟表)invoices_with_balance(带差额的发票记录表)
代码如下:
CREATE OR REPLACE VIEW invoices_with_balance AS
SELECT
invoice_id,
number,
client_id,
invoice_total,
payment_total,
invoice_date,
invoice_total - payment_total AS balance, -- 新增列
due_date,
payment_date
FROM invoices
WHERE (invoice_total - payment_total) > 0
/* 这里不能用列别名balance,会报错说不存在,
必须用原列名的表达式,这还是执行顺序的问题
之前讲WHERE和HAVING作为事前筛选和事后筛选的区别时提到过 */
删:删掉id为1的发票记录
DELETE FROM invoices_with_balance
WHERE invoice_id = 1
改:将2号发票记录的期限延后两天
UPDATE invoices_with_balance
SET due_date = DATE_ADD(due_date, INTERVAL 2 DAY)
WHERE invoice_id = 2
增:
- 在视图中用INSERT新增记录的话还有另一个前提,即视图必须包含其底层所有原始表的所有必须字段
- 例如,若这个 invoices_with_balance 视图里没有 invoice_date 字段(invoices 中的必须字段),那就无法通过该视图向 invoices 表新增记录,因为 invoices 表不会接受 invoice_date 字段为空的记录
WITH CHECK OPTION 子句
- 在视图的原始查询语句最后加上
WITH CHECK OPTION可以防止执行那些会让视图中某些行(记录)消失的修改语句
实例:接前面的 invoices_with_balance 视图的例子,该视图与原始的 orders 表相比增加了balance(invouce_total - payment_total) 列,且只显示 balance 大于0的行(记录),若将某记录(如2号订单)的 payment_total 改为和 invouce_total 相等,则 balance 为0,该记录会从视图中消失。
UPDATE invoices_with_balance
SET payment_total = invoice_total
WHERE invoice_id = 2
更新后会发现invoices_with_balance视图里2号订单消失。
但在视图原始查询语句最后加入 WITH CHECK OPTION 后,对3号订单执行类似上面的语句后会报错
UPDATE invoices_with_balance
SET payment_total = invoice_total
WHERE invoice_id = 3
-- Error Code: 1369. CHECK OPTION failed 'sql_invoicing.invoices_with_balance'
4.视图的作用
- 简化查询
- 增加抽象层,减少变化的影响:视图给表增加了一个抽象层(模块化),这样如果数据库设计改变了(如一个字段从一个表转移到了另一个表),只需修改视图的查询语句使其能保持原有查询结果即可,不需要修改使用这个视图的那几十个查询。相反,如果没有视图这一层的话,所有查询将直接使用指向原表的原始查询语句,这样一旦更改原表设计,就要相应地更改所有的这些查询。
- 限制对原数据的访问权限:在视图中可以对原表的行和列进行筛选,这样如果你禁止了对原始表的访问权限,用户只能通过视图来修改数据,他们就无法修改视图中未返回的那些字段和记录。但注意这通常并不简单,需要良好的规划,否则最后可能搞得一团乱。