27.5.3 可更新和可插入视图
一些视图是可更新的,可以使用它们来指定要更新的表单在数据更改语句中。例如,您可以使用它们在语句中,如UPDATE
、DELETE
或INSERT
来更新底层表的内容。派生表和通用表达式也可以在多表UPDATE
和DELETE
语句中指定,但是只能用于读取数据以指定要更新或删除的行。通常,视图引用必须是可更新的,这意味着它们可以被合并而不是物化。复合视图有更复杂的规则。
为了使视图可更新,必须存在一个到一对关系,即视图中的行和底层表中的行之间的关系。还有一些其他的构造使视图不可更新。为了更具体地说,视图不可更新,如果它包含以下任何内容:
生成的视图列在视图中被认为是可更新的,因为可以将其分配给它。然而,如果对该列进行明确的更新,唯一允许的值是DEFAULT
。有关生成列的信息,请见第15.1.20.8节,“CREATE TABLE and Generated Columns”。
有时,多表视图可以是可更新的,假设它可以使用MERGE
算法处理。为使其工作,视图必须使用内联连接(而不是外联连接或UNION
)。此外,只能更新视图定义中的一个表,因此SET
子句必须指定视图中的一个表的列名。使用UNION ALL
的视图不允许,即使它们理论上是可更新的。
关于插入性(使用INSERT
语句),可更新的视图是可插入的,如果它还满足以下要求:
-
不能有重复的视图列名。
-
视图必须包含所有不具有默认值的基本表的列。
-
视图列必须是简单的列引用。它们不能是表达式,如这些:
3.14159 col1 + 3 UPPER(col2) col3 / col4 (subquery)
MySQL 在CREATE VIEW
时间设置一个标志,称为视图可更新标志。如果UPDATE
和DELETE
(和类似操作)是合法的,则标志被设置为YES
(true)。否则,标志被设置为NO
(false)。IS_UPDATABLE
列在信息架构VIEWS
表中显示该标志的状态。这意味着服务器总是知道视图是否可更新。
如果视图不可更新,则语句UPDATE
、DELETE
和INSERT
将被拒绝。即使视图是可更新的,也可能无法插入它,正如本节其他地方所述。
视图的可更新性可能受到updatable_views_with_limit
系统变量的影响。请见第7.1.8节,“Server System Variables”。
以下讨论假设这些表和视图存在:
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()
的值,因为插入默认值到视图中不包含的列的副作用不应该可见。