Related Documentation Download this Manual
PDF (US Ltr) - 40.8Mb
PDF (A4) - 40.9Mb
Man Pages (TGZ) - 294.0Kb
Man Pages (Zip) - 409.0Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb
Excerpts from this Manual

MySQL 8.3 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 子句中的子查询,引用 FROM 子句中的表

  • 仅引用文字值(在这种情况下,没有基础表可更新)

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

  • 对基础表的任何列的多个引用(失败 INSERT,允许 UPDATEDELETE)

视图中的生成列被认为是可更新的,因为可以对其赋值。然而,如果明确更新了该列,则唯一允许的值是 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) 如果 UPDATEDELETE (和类似操作) 对视图是合法的。否则,该标志设置为 NO (false)。信息模式 VIEWS 表中的 IS_UPDATABLE 列显示该标志的状态。这意味着服务器总是知道视图是否可更新。

如果视图不可更新,则语句如 UPDATEDELETEINSERT 是非法的并被拒绝。(即使视图是可更新的,也可能无法插入到其中,如本节其他地方所述。)

视图的 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;

INSERTUPDATEDELETE 语句如下所示:

  • 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() 的值,因为将默认值插入到视图中不包含的列中对侧的影响不应该是可见的。