一些视图是可更新的,并且可以在数据更改语句中引用它们,以指定要更新的表。也就是说,您可以在语句中使用它们,例如 UPDATE
、DELETE
或 INSERT
,以更新基础表的内容。派生表和公共表表达式也可以在多表 UPDATE
和 DELETE
语句中指定,但只能用于读取数据以指定要更新或删除的行。一般来说,视图引用必须是可更新的,即它们可以合并而不是物化。复合视图有更多复杂的规则。
为了使视图可更新,必须在视图和基础表之间存在一对一的关系。此外,还有一些构造使视图不可更新。更具体地说,视图不可更新如果它包含以下任何一个:
视图中的生成列被认为是可更新的,因为可以对其赋值。然而,如果明确更新了该列,则唯一允许的值是 DEFAULT
。有关生成列的信息,请参阅 第 15.1.20.8 节,“CREATE TABLE 和生成列”。
有时,多表视图可以是可更新的,假设它可以使用 MERGE
算法进行处理。为使其生效,视图必须使用内部连接(而不是外部连接或 UNION
)。此外,视图定义中只能更新一个表,因此 SET
子句必须仅命名来自视图中一个表的列。使用 UNION ALL
的视图不允许,即使它们理论上是可更新的。
关于可插入性(使用 INSERT
语句),可更新视图是可插入的,如果它还满足以下附加要求:
-
视图列名不得重复。
-
视图必须包含基础表中所有没有默认值的列。
-
视图列必须是简单的列引用。它们不能是表达式,例如这些:
3.14159 col1 + 3 UPPER(col2) col3 / col4 (subquery)
MySQL 设置了一个标志,称为视图 updatability 标志,在 CREATE VIEW
时间。该标志设置为 YES
(true) 如果 UPDATE
和 DELETE
(和类似操作) 对视图是合法的。否则,该标志设置为 NO
(false)。信息模式 VIEWS
表中的 IS_UPDATABLE
列显示该标志的状态。这意味着服务器总是知道视图是否可更新。
如果视图不可更新,则语句如 UPDATE
、DELETE
和 INSERT
是非法的并被拒绝。(即使视图是可更新的,也可能无法插入到其中,如本节其他地方所述。)
视图的 updatability 可能受到 updatable_views_with_limit
系统变量的影响。见 第 7.1.8 节,“服务器系统变量”。
以下讨论假设存在以下表和视图:
CREATE TABLE t1 (x INTEGER);
CREATE TABLE t2 (c INTEGER);
CREATE VIEW vmat AS SELECT SUM(x) AS s FROM t1;
CREATE VIEW vup AS SELECT * FROM t2;
CREATE VIEW vjoin AS SELECT * FROM vmat JOIN vup ON vmat.s=vup.c;
INSERT
、UPDATE
和 DELETE
语句如下所示:
-
INSERT
:插入表可能是一个合并的视图引用。如果视图是一个连接视图,则所有组件必须是可更新的(不是物化的)。对于多表可更新视图,INSERT
可以工作,如果它插入到单个表中。该语句无效,因为连接视图的一个组件是不可更新的:
INSERT INTO vjoin (c) VALUES (1);
该语句有效;视图不包含物化组件:
INSERT INTO vup (c) VALUES (1);
-
UPDATE
:要更新的表或表在UPDATE
语句中可能是一个合并的视图引用。如果视图是一个连接视图,则至少一个组件必须是可更新的(这与INSERT
不同)。在多表
UPDATE
语句中,语句的更新表引用必须是基本表或可更新视图引用。未更新的表引用可能是物化视图或派生表。该语句有效;列
c
来自连接视图的可更新部分:UPDATE vjoin SET c=c+1;
该语句无效;列
x
来自连接视图的不可更新部分:UPDATE vjoin SET x=x+1;
该语句有效;多表
UPDATE
语句的更新表引用是一个可更新视图 (vup
):UPDATE vup JOIN (SELECT SUM(x) AS s FROM t1) AS dt ON ... SET c=c+1;
该语句无效;它尝试更新一个物化派生表:
UPDATE vup JOIN (SELECT SUM(x) AS s FROM t1) AS dt ON ... SET s=s+1;
-
DELETE
:要删除的表或表在DELETE
语句中必须是合并视图。连接视图不允许(这与INSERT
和UPDATE
不同)。该语句无效,因为视图是一个连接视图:
DELETE vjoin WHERE ...;
该语句有效,因为视图是一个合并(可更新)视图:
DELETE vup WHERE ...;
该语句有效,因为它从一个合并(可更新)视图中删除:
DELETE vup FROM vup JOIN (SELECT SUM(x) AS s FROM t1) AS dt ON ...;
以下是更多讨论和示例。
本节早些讨论指出,视图不可插入,如果不是所有列都是简单列引用(例如,如果它包含表达式或复合表达式)。虽然这样的视图不可插入,但它可以是可更新的,如果您只更新不包含表达式的列。考虑以下视图:
CREATE VIEW v AS SELECT col1, 1 AS col2 FROM t;
该视图不可插入,因为 col2
是一个表达式。但是,如果更新不尝试更新 col2
,则该视图是可更新的。以下更新是允许的:
UPDATE v SET col1 = 0;
以下更新是不允许的,因为它尝试更新一个表达式列:
UPDATE v SET col2 = 0;
如果一个表包含一个 AUTO_INCREMENT
列,将数据插入到不包含 AUTO_INCREMENT
列的可插入视图中,不会改变 LAST_INSERT_ID()
的值,因为将默认值插入到视图中不包含的列中对侧的影响不应该是可见的。