我正在尝试在我的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
答案 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;