查找重复行的索引?

时间:2011-12-01 21:17:35

标签: mysql

我正在尝试在我的users表中找到重复项(不要问,这是很多繁文缛节),但是我在创建我创建的查询的索引时遇到了问题。该表看起来像:

+----------------+---------+------+-----+---------+----------------+
| Field          | Type    | Null | Key | Default | Extra          |
+----------------+---------+------+-----+---------+----------------+
| id             | int(10) | NO   | PRI | NULL    | auto_increment | 
| email          | text    | YES  | MUL | NULL    |                | 
| username       | text    | YES  | MUL | NULL    |                | 
| password       | text    | YES  |     | NULL    |                |
+----------------+---------+------+-----+---------+----------------+

还有其他领域,但这些是我正在寻找的。我为查找重复项而编写的查询如下:

SELECT COUNT(username) count,GROUP_CONCAT(id) ids,username,email,password
    FROM users
    GROUP BY username,email,password
    HAVING COUNT(username) > 1

我创建的索引是:

CREATE INDEX users_id_username_password_email
    ON users id,username(64),password(64),email(64));

不幸的是,describe似乎没有使用这个索引:

mysql> describe SELECT COUNT(username) count,GROUP_CONCAT(id) ids,
    -> username,email,password
    -> FROM users
    -> GROUP BY username,email,password
    -> HAVING COUNT(username) > 1\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: users
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 705418
        Extra: Using filesort

所以根本问题是,我应该创建什么样的索引才能在这样的表上找到重复的条目?

编辑:更改查询的顺序以匹配索引不执行任何操作:

mysql> describe SELECT COUNT(username) count,GROUP_CONCAT(id) ids,
    -> username,password,email
    -> FROM users
    -> GROUP BY username,password,email
    -> HAVING COUNT(username) > 1\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: users
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 705418
        Extra: Using filesort

1 个答案:

答案 0 :(得分:2)

如果RDBMS无论如何都需要读取每一行,那么它就没有必要使用索引。索引的存在无关紧要,索引中列的顺序无关紧要,如果使用FORCE INDEX则无关紧要。

通过类比,如果我要求你在书中找到“the”这个词的每一个出现,你会使用书后面的索引,还是只读封面来覆盖?

您可以编写查询的另一种方法如下:

select t1.id, t2.id from users t1 
join users t2 using (username,password,email) 
where t1.id<t2.id

这导致以下解释计划:

*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t1
         type: ALL
possible_keys: PRIMARY,users_id_username_password_email
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 16516
        Extra: 
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: t2
         type: ref
possible_keys: PRIMARY,users_id_username_password_email
          key: users_id_username_password_email
      key_len: 201
          ref: test.t1.username,test.t1.password,test.t1.email
         rows: 82
        Extra: Using where

它仍然对users表执行一次表扫描,但它不必对整个表进行排序以查找重复项。它只需要进行密钥查找。

对于它的价值,我只在(username(64),email(64),password(64))上使用索引进行了测试。不需要在索引中包含id,因为所有InnoDB索引都隐式包含主键列。


这是另一个依赖于连接来减少结果集的查询,然后按最小的id进行分组,并显示更高的id是dupes。您也可以选择返回您加入的列。

select t1.id, /* t1.username, t1.password, t1.email, */ group_concat(t2.id) as dupes
from users t1 
join users t2 
  on (t1.username,t1.password,t1.email) = (t2.username,t2.password,t2.email) 
  and t1.id < t2.id 
left outer join users t3
  on (t1.username,t1.password,t1.email) = (t3.username,t3.password,t3.email) 
  and t1.id > t3.id
where t3.id is null
group by t1.id;