13.2.2 DELETE 语句

DELETE是 DML 语句,用于从 table 中删除行。

Single-Table Syntax

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

DELETE语句从* tbl_name *中删除行,并返回删除的行数。要检查已删除的行数,请调用第 12.15 节“信息功能”中描述的ROW_COUNT()函数。

Main Clauses

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

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

Multiple-Table Syntax

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]

Privileges

您需要对 table 具有DELETE特权才能从中删除行。对于仅读取的任何列,例如WHERE子句中命名的列,只需要SELECT特权。

Performance

当您不需要知道已删除的行数时,与没有WHERE子句的DELETE语句相比,TRUNCATE TABLE语句是清空 table 的更快方法。与DELETE不同,TRUNCATE TABLE不能在事务内使用或在 table 上有锁时不能使用。参见第 13.1.34 节“ TRUNCATE TABLE 语句”第 13.3.5 节“ LOCK TABLES 和 UNLOCK TABLES 语句”

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

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

Subqueries

您不能从 table 中删除,也不能从子查询的同一 table 中选择。

分区 table 支持

DELETE使用PARTITION选项支持显式分区选择,该选项采用一个或多个分区或子分区(或两者)的逗号分隔名称列 table,从中选择要删除的行。未包括在列 table 中的分区将被忽略。给定具有名为p0的分区的分区 tablet,执行语句DELETE FROM t PARTITION (p0)与执行更改 tablet 截断分区(p0)具有相同的作用;在这两种情况下,都将删除分区p0中的所有行。

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

PARTITION选项也可以在多 tableDELETE语句中使用。在FROM选项中命名的 table 中,最多可以使用一个这样的选项。

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

Auto-Increment Columns

如果删除包含AUTO_INCREMENT列最大值的行,则该值不会再用于MyISAMInnoDBtable。如果在autocommit模式下使用DELETE FROM tbl_name(不带WHERE子句)删除 table 中的所有行,则该序列将从InnoDBMyISAM以外的所有存储引擎重新开始。如第 14.6.1.6 节“ InnoDB 中的 AUTO_INCREMENT 处理”中所述,InnoDBtable 的此行为有一些 exception。

对于MyISAM个 table,您可以在多列键中指定AUTO_INCREMENT个辅助列。在这种情况下,即使对于MyISAMtable,也会重复使用从序列顶部删除的值。参见第 3.6.9 节“使用 AUTO_INCREMENT”

Modifiers

DELETE语句支持以下修饰符:

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

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

  • IGNORE修饰符使 MySQL 在删除行的过程中忽略错误。 (在解析阶段遇到的错误将以通常的方式处理.)由于使用IGNORE而被忽略的错误将作为警告返回。有关更多信息,请参见IGNORE 关键字和严格 SQL 模式的比较

删除 Sequences

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

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

ORDER BY还有助于按照避免引用完整性违规的 Sequences 删除行。

InnoDB Tables

如果要从大 table 中删除许多行,则可能会超出InnoDBtable 的锁定 table 大小。为了避免此问题,或者只是为了最小化 table 保持锁定的时间,以下策略(根本不使用DELETE)可能会有所帮助:

  • 选择要删除到与原始 table 具有相同结构的空 table 中的* not *行:
INSERT INTO t_copy SELECT * FROM t WHERE ... ;
  • 使用RENAME TABLE原子地将原始 table 移开,并将副本重命名为原始名称:
RENAME TABLE t TO t_old, t_copy TO t;
  • 删除原始 table:
DROP TABLE t_old;

RENAME TABLE执行时,没有其他会话可以访问涉及的 table,因此重命名操作不会出现并发问题。参见第 13.1.33 节“ RENAME TABLE 语句”

MyISAM Tables

MyISAMtable 中,删除的行保留在链接列 table 中,随后的INSERT操作重用旧的行位置。要回收未使用的空间并减小文件大小,请使用OPTIMIZE TABLE语句或myisamchkUtil 重新组织 table。 OPTIMIZE TABLE更易于使用,但myisamchk更快。参见第 13.7.2.4 节“ OPTIMIZE TABLE 语句”第 4.6.3 节“ myisamchk-MyISAMtable 维护 Util”

QUICK修饰符影响索引叶是否合并以进行删除操作。 DELETE QUICK对于将删除行的索引值替换为以后插入的行的相似索引值的应用程序最有用。在这种情况下,删除值留下的漏洞将被重用。

当删除的值导致索引块填充不足,该索引块跨越索引值范围而又发生新插入时,DELETE QUICK无效。在这种情况下,使用QUICK会导致索引中浪费空间而无法回收。这是这种情况的示例:

  • 创建一个包含索引的AUTO_INCREMENT列的 table。

  • 在 table 中插入许多行。每次插入都会产生一个索引值,该值将添加到索引的高端。

  • 使用DELETE QUICK删除列范围低端的行块。

在这种情况下,与已删除索引值关联的索引块将被填充不足,但由于使用QUICK而不会与其他索引块合并。当发生新的插入操作时,它们仍会填充不足,因为新行的索引值不在删除范围内。此外,即使您以后使用DELETE而不使用QUICK,它们仍会填充不足,除非某些已删除的索引值恰好位于填充不足的块内或邻近的索引块中。要在这种情况下回收未使用的索引空间,请使用OPTIMIZE TABLE

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

Multi-Table Deletes

您可以在DELETE语句中指定多个 table,以根据WHERE子句中的条件从一个或多个 table 中删除行。您不能在多 tableDELETE中使用ORDER BYLIMIT。 * table_references *子句列出了联接所涉及的 table,如第 13.2.9.2 节“ JOIN 子句”中所述。

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

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

Or:

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

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

前面的示例使用INNER JOIN,但是多 tableDELETE语句可以使用SELECT语句中允许的其他类型的连接,例如LEFT JOIN。例如,要删除t1中不匹配的行,请使用LEFT JOIN

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

该语法允许在每个* tbl_name *之后加上.*以便与 Access 兼容。

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

Note

如果为 table 声明别名,则在引用 table 时必须使用别名:

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

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

Correct:

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;

Incorrect:

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;