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
列的最大值的行,则该值不会被 MyISAM
或 InnoDB
表重用。如果您使用 DELETE FROM
(没有 tbl_name
WHERE
子句)在 autocommit
模式下删除表中的所有行,则序列将针对除 InnoDB
和 MyISAM
之外的所有存储引擎重新开始。对于 InnoDB
表,这种行为有一些例外,如 第 17.6.1.6 节,“InnoDB 中的 AUTO_INCREMENT 处理” 中所述。
对于 MyISAM
表,您可以在多列键中指定 AUTO_INCREMENT
次要列。在这种情况下,即使对于 MyISAM
表,也会重用从序列顶部删除的值。请参见 第 5.6.9 节,“使用 AUTO_INCREMENT”。
修饰符
DELETE
语句支持以下修饰符
如果您指定了
LOW_PRIORITY
修饰符,则服务器会延迟执行DELETE
,直到没有其他客户端从表中读取。这仅影响仅使用表级锁的存储引擎(例如MyISAM
、MEMORY
和MERGE
)。对于
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
)可能会有所帮助
将 不 要删除的行选择到一个与原始表具有相同结构的空表中
INSERT INTO t_copy SELECT * FROM t WHERE ... ;
使用
RENAME TABLE
将原始表原子地移出位置,并将副本重命名为原始名称RENAME TABLE t TO t_old, t_copy TO t;
删除原始表
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
会导致索引中浪费空间,而这些空间无法回收。以下是一个这种情况的示例
创建一个包含已索引
AUTO_INCREMENT
列的表。向表中插入许多行。每次插入都会导致一个索引值被添加到索引的高端。
使用
DELETE QUICK
删除列范围低端的一块行。
在这种情况下,与已删除索引值关联的索引块变得填不满,但由于使用了 QUICK
,它们不会与其他索引块合并。当发生新的插入时,它们仍然填不满,因为新行没有已删除范围内的索引值。此外,即使您稍后使用 DELETE
(没有 QUICK
),它们仍然填不满,除非一些已删除的索引值碰巧位于填不满的块内部或相邻的索引块中。要回收此类情况下未使用的索引空间,请使用 OPTIMIZE TABLE
。
如果您要从表中删除许多行,则使用 DELETE QUICK
后跟 OPTIMIZE TABLE
可能更快。这将重建索引,而不是执行许多索引块合并操作。
多表删除
您可以在 DELETE
语句中指定多个表以删除一个或多个表中的行,具体取决于 WHERE
子句中的条件。您不能在多表 DELETE
中使用 ORDER BY
或 LIMIT
。 table_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;
这些语句在搜索要删除的行时使用所有三个表,但仅从表 t1
和 t2
中删除匹配的行。
前面的示例使用了 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
语句。