MySQL 8.4 Reference Manual  /  ...  /  Updatable and Insertable Views

27.5.3 可更新和可插入视图

一些视图是可更新的,可以使用它们来指定要更新的表单在数据更改语句中。例如,您可以使用它们在语句中,如UPDATEDELETEINSERT来更新底层表的内容。派生表和通用表达式也可以在多表UPDATEDELETE语句中指定,但是只能用于读取数据以指定要更新或删除的行。通常,视图引用必须是可更新的,这意味着它们可以被合并而不是物化。复合视图有更复杂的规则。

为了使视图可更新,必须存在一个到一对关系,即视图中的行和底层表中的行之间的关系。还有一些其他的构造使视图不可更新。为了更具体地说,视图不可更新,如果它包含以下任何内容:

  • 聚合函数或窗口函数(SUM()MIN()MAX()COUNT()等)

  • DISTINCT

  • GROUP BY

  • HAVING

  • UNIONUNION ALL

  • 子查询在选择列表中

    非依赖子查询在选择列表中失败,但是在INSERT语句中可以在UPDATEDELETE语句中使用。对于依赖子查询在选择列表中,不允许任何数据更改语句。

  • 某些连接(请参阅后续讨论)

  • 引用不可更新视图的FROM子句

  • 子查询在WHERE子句中引用底层表

  • 只引用字面值(在这种情况下,没有底层表可以更新)

  • ALGORITHM = TEMPTABLE(使用临时表总是使视图不可更新)

  • 对任何基础表的多个引用(失败在INSERT语句中,但是在UPDATEDELETE语句中可以使用)

生成的视图列在视图中被认为是可更新的,因为可以将其分配给它。然而,如果对该列进行明确的更新,唯一允许的值是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时间设置一个标志,称为视图可更新标志。如果UPDATEDELETE(和类似操作)是合法的,则标志被设置为YES(true)。否则,标志被设置为NO(false)。IS_UPDATABLE列在信息架构VIEWS表中显示该标志的状态。这意味着服务器总是知道视图是否可更新。

如果视图不可更新,则语句UPDATEDELETEINSERT将被拒绝。即使视图是可更新的,也可能无法插入它,正如本节其他地方所述。

视图的可更新性可能受到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 语句中要删除的表或表必须是合并的视图。连接视图不允许(这与INSERTUPDATE 不同)。

    这条语句无效,因为视图是一个连接视图:

    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()的值,因为插入默认值到视图中不包含的列的副作用不应该可见。