关于sql:用条件截断

Truncate with condition

截断->这将重置整个表,是否可以通过截断来重置特定的记录/检查条件。

例如:我想重置所有数据,并在表格中保留过去30天。

谢谢。


不,TRUNCATE是全有还是全无。您可以执行DELETE FROM

WHERE ,但这会失去TRUNCATE的速度优势。


简短的答案是否定的:MySQL不允许您在TRUNCATE语句中添加WHERE子句。这是有关TRUNCATE语句的MySQL文档。

但好消息是您可以(某种程度上)解决此限制。

使用DELETE的简单,安全,清洁但缓慢的解决方案

首先,如果表足够小,只需使用DELETE语句(必须提及):

1
2
3
1. LOCK TABLE my_table WRITE;
2. DELETE FROM my_table WHERE my_date<DATE_SUB(NOW(), INTERVAL 1 MONTH);
3. UNLOCK TABLES;

LOCKUNLOCK语句不是强制性的,但它们可以加快处理速度并避免潜在的死锁。

不幸的是,如果您的表很大,这将非常慢...并且由于您正在考虑使用TRUNCATE语句,所以我想这是因为您的表很大。

因此,这是使用TRUNCATE语句解决问题的一种方法:

使用TRUNCATE的简单,快速但不安全的解决方案

1
2
3
4
5
6
7
1. CREATE TABLE my_table_backup AS
      SELECT * FROM my_table WHERE my_date>=DATE_SUB(NOW(), INTERVAL 1 MONTH);
2. TRUNCATE my_table;
3. LOCK TABLE my_table WRITE, my_table_backup WRITE;
4. INSERT INTO my_table SELECT * FROM my_table_backup;
5. UNLOCK TABLES;
6. DROP TABLE my_table_backup;

不幸的是,如果其他进程同时在表中插入记录,则此解决方案有点不安全:

  • 在步骤1和2之间插入的任何记录都将丢失
  • TRUNCATE语句将AUTO-INCREMENT计数器重置为零。因此,在第2步和第3步之间插入的任何记录的ID都将低于旧ID,甚至可能与在第4步中插入的ID发生冲突(请注意,AUTO-INCREMENT计数器将在第4步之后恢复为正确的值) 。

不幸的是,不可能锁定表并截断它。但是我们可以(以某种方式)使用RENAME解决该限制。

使用TRUNCATE的半简单,快速,安全但嘈杂的解决方案

1
2
3
4
5
6
7
8
9
1. RENAME TABLE my_table TO my_table_work;
2. CREATE TABLE my_table_backup AS
     SELECT * FROM my_table_work WHERE my_date>DATE_SUB(NOW(), INTERVAL 1 MONTH);
3. TRUNCATE my_table_work;
4. LOCK TABLE my_table_work WRITE, my_table_backup WRITE;
5. INSERT INTO my_table_work SELECT * FROM my_table_backup;
6. UNLOCK TABLES;
7. RENAME TABLE my_table_work TO my_table;
8. DROP TABLE my_table_backup;

这应该是完全安全且相当快的。唯一的问题是其他进程将看到表my_table消失几秒钟。这可能会导致错误在所有地方的日志中显示。因此,这是一个安全的解决方案,但却是"嘈杂的"。

免责声明:我不是MySQL专家,所以这些解决方案实际上可能很糟糕。我能提供的唯一保证是它们对我来说很好。如果某些专家可以对这些解决方案发表评论,我将不胜感激。


回答您的问题:
"我想重置所有数据并在表格中保留过去30天。"

您可以创建一个事件。检查https://dev.mysql.com/doc/refman/5.7/zh-CN/event-scheduler.html

例如:

1
2
3
4
CREATE EVENT DeleteExpiredLog
ON SCHEDULE EVERY 1 DAY
DO
DELETE FROM log WHERE DATE < DATE_SUB(NOW(), INTERVAL 30 DAY);

将在您的表中进行每日清理,并保持最近30天的数据可用


您可以简单地使用datapump的查询子句导出表,然后使用table_exists_action = replace子句将其导入回来。它会删除并重新创建您的表,并且花费的时间更少。在实施之前,请先阅读有关内容。