查询优化 - '在哪里不在'自我

时间:2012-08-29 13:08:50

标签: mysql

我正在尝试开发一个查询,该查询将删除数据库中除最近添加的行之外的所有行。这基于Timestamp字段,存储为字符串和存储为字符串的User ID字段..

table.Timestamp -> text field
table.Retrieving_User -> text field

这是我开发的查询。我们在这个数据库中有大约50K的记录,运行速度非常慢。我希望它不是因为我正在进行的字符串转换,因为这需要完成。

DELETE 
FROM `table` main
WHERE (main.Retrieving_User, STR_To_DATE( main.Timestamp , '%a %b %d %H:%i:%s CST %Y' )) NOT IN 
    (SELECT  sub.Retrieving_User, MAX( STR_To_DATE( sub.Timestamp , '%a %b %d %H:%i:%s CST %Y' )) 
    FROM `table` sub
    WHERE sub.Retrieving_User = 'userID'
    GROUP BY sub.Retrieving_User )
AND main.Retrieving_User = 'userID'

有没有人知道我正在尝试做的更有效的方式?

3 个答案:

答案 0 :(得分:1)

这样的事情可能会更快,因为它不会使用可能在内存表中反复循环的IN语句。备份并尝试

DELETE 
FROM `table` main
WHERE STR_To_DATE( main.Timestamp , '%a %b %d %H:%i:%s CST %Y' )<
  (SELECT  MAX( STR_To_DATE( sub.Timestamp , '%a %b %d %H:%i:%s CST %Y' ) 
   FROM `table` sub
   WHERE sub.Retrieving_User = main.Retrieving_User )
AND main.Retrieving_User = 'userID'

答案 1 :(得分:1)

每当你删除很多行并且你保留的行数比你要删除的行数要少得多时,来自MySQL documentation的这个技巧非常有效:

  

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

Select the rows not to be deleted into an empty table that has the same structure as the original table:

INSERT INTO t_copy SELECT * FROM t WHERE ... ;

Use RENAME TABLE to atomically move the original table out of the way and rename the copy to the original name:

RENAME TABLE t TO t_old, t_copy TO t;

Drop the original table:

DROP TABLE t_old;

使用MyISAM改善删除时间的另一种方法是使用DELETE QUICK,然后使用OPTIMIZE TABLE,也可以使用MySQL文档:

  

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

此处IvoTops answer进行了优化。我们只是将日期转换回字符串,因此我们不必在外部查询中再次进行转换:

DELETE 
FROM `table` main
WHERE main.Timestamp <>
  (SELECT DATE_FORMAT(MAX(STR_To_DATE( sub.Timestamp , '%a %b %d %H:%i:%s CST %Y'), '%a %b %d %H:%i:%s CST %Y') 
   FROM `table` sub
   WHERE sub.Retrieving_User = main.Retrieving_User )
AND main.Retrieving_User = 'userID'

答案 2 :(得分:0)

我认为您的性能问题与NOT IN语句有关。

你可能会更好
DELETE `table`
FROM `table` main,
     (SELECT  sub.Retrieving_User, MAX( STR_To_DATE( sub.Timestamp , '%a %b %d %H:%i:%s CST %Y' )) maxTime
    WHERE sub.Retrieving_User = 'userID'
    GROUP BY sub.Retrieving_User) sub
WHERE STR_To_DATE( main.Timestamp , '%a %b %d %H:%i:%s CST %Y' ) < sub.maxTime
  AND main.Retrieving_User = 'userID';