文档首页
MySQL 8.4 参考手册
相关文档 下载本手册
PDF (美国标准信纸) - 39.9Mb
PDF (A4) - 40.0Mb
手册页 (TGZ) - 258.5Kb
手册页 (Zip) - 365.5Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


MySQL 8.4 参考手册  /  ...  /  DELETE 语句

15.2.2 DELETE 语句

DELETE 是一个 DML 语句,用于从表中删除行。

一个 DELETE 语句可以以一个 WITH 子句开头,以定义可在 DELETE 中访问的通用表表达式。请参阅 第 15.2.20 节,“WITH (通用表表达式)”

单表语法

DELETE [LOW_PRIORITY] [QUICK] [IGNORE] FROM tbl_name [[AS] tbl_alias]
    [PARTITION (partition_name [, partition_name] ...)]
    [WHERE where_condition]
    [ORDER BY ...]
    [LIMIT row_count]

DELETE 语句从 tbl_name 删除行并返回已删除行的数量。要检查已删除行的数量,请调用 ROW_COUNT() 函数,该函数在 第 14.15 节,“信息函数” 中有描述。

主要子句

可选 WHERE 子句中的条件标识要删除的行。如果没有 WHERE 子句,则删除所有行。

where_condition 是一个表达式,对要删除的每一行都计算为 true。它的指定方式如 第 15.2.13 节,“SELECT 语句” 中所述。

如果指定了 ORDER BY 子句,则按指定的顺序删除行。 LIMIT 子句限制可以删除的行数。这些子句适用于单表删除,但不适用于多表删除。

多表语法

DELETE [LOW_PRIORITY] [QUICK] [IGNORE]
    tbl_name[.*] [, tbl_name[.*]] ...
    FROM table_references
    [WHERE where_condition]

DELETE [LOW_PRIORITY] [QUICK] [IGNORE]
    FROM tbl_name[.*] [, tbl_name[.*]] ...
    USING table_references
    [WHERE where_condition]

权限

您需要对表具有 DELETE 权限才能从该表中删除行。您只需要对仅读的任何列(例如 WHERE 子句中命名的列)具有 SELECT 权限。

性能

当您不需要知道已删除的行数时, TRUNCATE TABLE 语句是清空表比使用无 WHERE 子句的 DELETE 语句更快的清空表的方式。与 DELETE 不同, TRUNCATE TABLE 不能在事务中使用,也不能在您对表有锁的情况下使用。请参见 第 15.1.37 节,“TRUNCATE TABLE 语句”第 15.3.6 节,“LOCK TABLES 和 UNLOCK TABLES 语句”

删除操作的速度也可能受到 第 10.2.5.3 节,“优化 DELETE 语句” 中讨论的因素的影响。

为了确保给定的 DELETE 语句不会花费太多时间,MySQL 特定的 LIMIT row_count 子句用于 DELETE 指定要删除的最大行数。如果要删除的行数大于限制,则重复 DELETE 语句,直到受影响的行数小于 LIMIT 值。

子查询

您不能从表中删除并从子查询中的同一表中选择。

分区表支持

DELETE 支持使用 PARTITION 子句显式选择分区,该子句接受一个或多个要从中选择要删除行的分区或子分区(或两者)的逗号分隔名称列表。不在列表中的分区将被忽略。给定一个名为 p0 的分区的已分区表 t,执行语句 DELETE FROM t PARTITION (p0) 对表的影响与执行 ALTER TABLE t TRUNCATE PARTITION (p0) 相同;在这两种情况下,分区 p0 中的所有行都被删除。

PARTITION 可以与 WHERE 条件一起使用,在这种情况下,条件仅在列出的分区中的行上进行测试。例如, DELETE FROM t PARTITION (p0) WHERE c < 5 仅从分区 p0 中删除满足条件 c < 5 的行;任何其他分区中的行都不会被检查,因此不会受到 DELETE 的影响。

PARTITION 子句也可以在多表 DELETE 语句中使用。您最多可以在 FROM 选项中命名的每个表中使用一个这样的选项。

有关更多信息和示例,请参见 第 26.5 节,“分区选择”

自动递增列

如果您删除了包含 AUTO_INCREMENT 列的最大值的行,则该值不会被 MyISAMInnoDB 表重用。如果您使用 DELETE FROM tbl_name(没有 WHERE 子句)在 autocommit 模式下删除表中的所有行,则序列将针对除 InnoDBMyISAM 之外的所有存储引擎重新开始。对于 InnoDB 表,这种行为有一些例外,如 第 17.6.1.6 节,“InnoDB 中的 AUTO_INCREMENT 处理” 中所述。

对于 MyISAM 表,您可以在多列键中指定 AUTO_INCREMENT 次要列。在这种情况下,即使对于 MyISAM 表,也会重用从序列顶部删除的值。请参见 第 5.6.9 节,“使用 AUTO_INCREMENT”

修饰符

DELETE 语句支持以下修饰符

  • 如果您指定了 LOW_PRIORITY 修饰符,则服务器会延迟执行 DELETE,直到没有其他客户端从表中读取。这仅影响仅使用表级锁的存储引擎(例如 MyISAMMEMORYMERGE)。

  • 对于 MyISAM 表,如果您使用 QUICK 修饰符,则存储引擎在删除期间不会合并索引叶节点,这可能会加快某些类型的删除操作。

  • IGNORE 修饰符导致 MySQL 在删除行的过程中忽略可忽略的错误。(在解析阶段遇到的错误将以通常的方式处理。)由于使用 IGNORE 而被忽略的错误将作为警告返回。有关更多信息,请参见 IGNORE 对语句执行的影响

删除顺序

如果 DELETE 语句包含 ORDER BY 子句,则按子句指定的顺序删除行。这主要在与 LIMIT 结合使用时有用。例如,以下语句查找与 WHERE 子句匹配的行,按 timestamp_column 对它们进行排序,并删除第一个(最旧的)行

DELETE FROM somelog WHERE user = 'jcole'
ORDER BY timestamp_column LIMIT 1;

ORDER BY 还有助于按避免引用完整性违规所需的顺序删除行。

InnoDB 表

如果您要从大型表中删除许多行,则可能会超过 InnoDB 表的锁定表大小。为了避免这个问题,或者仅仅是为了最小化表保持锁定的时间,以下策略(根本不使用 DELETE)可能会有所帮助

  1. 要删除的行选择到一个与原始表具有相同结构的空表中

    INSERT INTO t_copy SELECT * FROM t WHERE ... ;
  2. 使用 RENAME TABLE 将原始表原子地移出位置,并将副本重命名为原始名称

    RENAME TABLE t TO t_old, t_copy TO t;
  3. 删除原始表

    DROP TABLE t_old;

在执行 RENAME TABLE 时,没有其他会话可以访问所涉及的表,因此重命名操作不受并发问题的困扰。请参见 第 15.1.36 节,“RENAME TABLE 语句”

MyISAM 表

MyISAM 表中,已删除的行将保存在一个链表中,随后的 INSERT 操作会重用旧的行位置。要回收未使用的空间并减小文件大小,请使用 OPTIMIZE TABLE 语句或 myisamchk 实用程序来重新组织表。 OPTIMIZE TABLE 更易于使用,但 myisamchk 速度更快。请参见 第 15.7.3.4 节,“OPTIMIZE TABLE 语句”第 6.6.4 节,“myisamchk — MyISAM 表维护工具”

QUICK 修饰符会影响是否合并索引叶节点以进行删除操作。 DELETE QUICK 最适合那些已删除行的索引值被来自稍后插入行的类似索引值替换的应用程序。在这种情况下,由已删除值留下的空洞将被重用。

DELETE QUICK 不适用于已删除值会导致索引块被填不满的情况,这些块跨越了一系列再次发生新插入的索引值。在这种情况下,使用 QUICK 会导致索引中浪费空间,而这些空间无法回收。以下是一个这种情况的示例

  1. 创建一个包含已索引 AUTO_INCREMENT 列的表。

  2. 向表中插入许多行。每次插入都会导致一个索引值被添加到索引的高端。

  3. 使用 DELETE QUICK 删除列范围低端的一块行。

在这种情况下,与已删除索引值关联的索引块变得填不满,但由于使用了 QUICK,它们不会与其他索引块合并。当发生新的插入时,它们仍然填不满,因为新行没有已删除范围内的索引值。此外,即使您稍后使用 DELETE(没有 QUICK),它们仍然填不满,除非一些已删除的索引值碰巧位于填不满的块内部或相邻的索引块中。要回收此类情况下未使用的索引空间,请使用 OPTIMIZE TABLE

如果您要从表中删除许多行,则使用 DELETE QUICK 后跟 OPTIMIZE TABLE 可能更快。这将重建索引,而不是执行许多索引块合并操作。

多表删除

您可以在 DELETE 语句中指定多个表以删除一个或多个表中的行,具体取决于 WHERE 子句中的条件。您不能在多表 DELETE 中使用 ORDER BYLIMITtable_references 子句列出了参与联接的表,如 第 15.2.13.2 节,“JOIN 子句” 中所述。

对于第一个多表语法,仅删除 FROM 子句之前的表中匹配的行。对于第二个多表语法,仅删除 FROM 子句(在 USING 子句之前)中列出的表中匹配的行。其效果是,您可以同时从许多表中删除行,并拥有仅用于搜索的其他表

DELETE t1, t2 FROM t1 INNER JOIN t2 INNER JOIN t3
WHERE t1.id=t2.id AND t2.id=t3.id;

DELETE FROM t1, t2 USING t1 INNER JOIN t2 INNER JOIN t3
WHERE t1.id=t2.id AND t2.id=t3.id;

这些语句在搜索要删除的行时使用所有三个表,但仅从表 t1t2 中删除匹配的行。

前面的示例使用了 INNER JOIN,但多表 DELETE 语句可以使用 SELECT 语句中允许的其他类型的联接,例如 LEFT JOIN。例如,要删除 t1 中存在但在 t2 中没有匹配项的行,请使用 LEFT JOIN

DELETE t1 FROM t1 LEFT JOIN t2 ON t1.id=t2.id WHERE t2.id IS NULL;

语法允许在每个 tbl_name 之后使用 .*,以与 Access 兼容。

如果您使用涉及 InnoDB 表的多表 DELETE 语句,而这些表存在外键约束,则 MySQL 优化器可能会以与父/子关系顺序不同的顺序处理表。在这种情况下,语句将失败并回滚。相反,您应该从单个表中删除,并依赖于 InnoDB 提供的 ON DELETE 功能,以使其他表相应地修改。

注意

如果您为表声明别名,则在引用表时必须使用该别名

DELETE t1 FROM test AS t1, test2 WHERE ...

多表 DELETE 中的表别名应仅在语句的 table_references 部分声明。在其他地方,允许别名引用,但不允许别名声明。

正确

DELETE a1, a2 FROM t1 AS a1 INNER JOIN t2 AS a2
WHERE a1.id=a2.id;

DELETE FROM a1, a2 USING t1 AS a1 INNER JOIN t2 AS a2
WHERE a1.id=a2.id;

错误

DELETE t1 AS a1, t2 AS a2 FROM t1 INNER JOIN t2
WHERE a1.id=a2.id;

DELETE FROM t1 AS a1, t2 AS a2 USING t1 INNER JOIN t2
WHERE a1.id=a2.id;

表别名也支持单表 DELETE 语句。