(使用MySQL 4.1.22)
我不能让我的查询在大表(200k +行)上使用索引,它正在对它进行全表扫描。查询现在大约需要1.2秒。如果可能的话,我希望它能在不到0.2秒的时间内完成。
这是我的问题:
SELECT st_issues.issue_id, st_issues.cat_id,st_categories.name AS cat_name, st_issues.status_id,st_statuses.name AS status_name, st_issues.priority_id,st_priorities.name AS priority_name,st_priorities.color AS color, st_issues.assigned_cid,assigned_u.firstname,assigned_u.lastname,assigned_u.screenname, message, rating, created_by_email,created_by_cid,created_by_uid,by_user.firstname AS by_firstname,by_user.lastname AS by_lastname,by_user.screenname AS by_screenname, st_issues.browser,from_url,created_by_store,created,st_issues.stamp
FROM st_issues
JOIN st_categories ON (st_issues.cat_id=st_categories.cat_id)
JOIN st_statuses ON (st_issues.status_id=st_statuses.status_id)
JOIN st_priorities ON (st_issues.priority_id=st_priorities.priority_id)
LEFT JOIN users AS assigned_u ON (assigned_u.cid=st_issues.assigned_cid)
LEFT JOIN users AS by_user ON (by_user.uid=st_issues.created_by_uid)
LEFT JOIN st_issue_changes ON (st_issues.issue_id=st_issue_changes.issue_id AND change_id=0)
WHERE st_issues.assigned_cid=0
解释结果:
1, 'SIMPLE', 'st_issues', 'ALL', '', '', , '', 4, 'Using where'
1, 'SIMPLE', 'st_categories', 'eq_ref', 'PRIMARY', 'PRIMARY', 1, 'sg.st_issues.cat_id', 1, ''
1, 'SIMPLE', 'st_priorities', 'eq_ref', 'PRIMARY', 'PRIMARY', 1, 'sg.st_issues.priority_id', 1, ''
1, 'SIMPLE', 'assigned_u', 'ref', 'cid', 'cid', 8, 'sg.st_issues.assigned_cid', 1, ''
1, 'SIMPLE', 'st_statuses', 'ALL', 'PRIMARY', '', , '', 4, 'Using where'
1, 'SIMPLE', 'by_user', 'ALL', '', '', , '', 221623, ''
1, 'SIMPLE', 'st_issue_changes', 'eq_ref', 'PRIMARY', 'PRIMARY', 6, 'sg.st_issues.issue_id,const', 1, ''
显然问题在于'by_user'的加入,因为它没有使用索引。
以下是'users'表的一些定义:
CREATE TABLE `users` (
`cid` double unsigned NOT NULL auto_increment,
`uid` varchar(20) NOT NULL default '',
...
`firstname` varchar(20) default NULL,
`lastname` varchar(20) default NULL,
...
PRIMARY KEY (`uid`),
...
) ENGINE=InnoDB
任何人都知道为什么它没有在联接中使用主键?
任何人都有任何关于如何加快查询速度的想法或提示?
(如果需要/我想添加其他表的表定义)
编辑:
这是st_issues的表定义:
CREATE TABLE `st_issues` (
`issue_id` int(10) unsigned NOT NULL auto_increment,
`cat_id` tinyint(3) unsigned NOT NULL default '0',
`status_id` tinyint(3) unsigned NOT NULL default '0',
`priority_id` tinyint(3) unsigned NOT NULL default '0',
`assigned_cid` int(10) unsigned NOT NULL default '0',
`rating` tinyint(4) default NULL,
`created_by_email` varchar(255) NOT NULL default '',
`created_by_cid` int(10) unsigned NOT NULL default '0',
`created_by_uid` varchar(20) NOT NULL default '',
`created_by_store` tinyint(3) unsigned NOT NULL default '0',
`browser` varchar(255) NOT NULL default '',
`from_url` varchar(255) NOT NULL default '',
`created` datetime NOT NULL default '0000-00-00 00:00:00',
`stamp` datetime NOT NULL default '0000-00-00 00:00:00',
PRIMARY KEY (`issue_id`),
KEY `idx_create_by_cid` (`created_by_cid`),
KEY `idx_create_by_uid` (`created_by_uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
答案 0 :(得分:3)
这是用户表的整个定义吗?
因为它说:
)ENGINE = InnoDB
而st_issues说:
)ENGINE = InnoDB DEFAULT CHARSET = utf8;
如果你的两个表使用不同的排序规则,uid和created_by_uid的两个字符串数据类型是不同的,MySQL必须先进行字符集强制才能比较它们,从而打败你的索引。
最好确保对数据库中的所有文本使用相同的字符集/排序规则。
答案 1 :(得分:0)
我做了一些测试,发现以下更改有所帮助:
在st_issues.assigned_cid
上添加索引。
将users
表的主键更改为cid
而不是uid
。
更改by_user
的加入条件,使用cid
代替uid
:
LEFT JOIN users AS by_user ON (by_user.cid=st_issues.created_by_cid)
然后我收到了以下EXPLAIN
报告(虽然没有数据行):
+----+-------------+------------------+--------+---------------+--------------+---------+-------------------------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------------+--------+---------------+--------------+---------+-------------------------------+------+-------------+
| 1 | SIMPLE | st_issues | ref | assigned_cid | assigned_cid | 4 | const | 1 | |
| 1 | SIMPLE | st_categories | eq_ref | PRIMARY | PRIMARY | 1 | test.st_issues.cat_id | 1 | |
| 1 | SIMPLE | st_statuses | eq_ref | PRIMARY | PRIMARY | 1 | test.st_issues.status_id | 1 | |
| 1 | SIMPLE | st_priorities | eq_ref | PRIMARY | PRIMARY | 1 | test.st_issues.priority_id | 1 | |
| 1 | SIMPLE | assigned_u | eq_ref | PRIMARY | PRIMARY | 8 | test.st_issues.assigned_cid | 1 | |
| 1 | SIMPLE | by_user | eq_ref | PRIMARY | PRIMARY | 8 | test.st_issues.created_by_cid | 1 | |
| 1 | SIMPLE | st_issue_changes | eq_ref | PRIMARY | PRIMARY | 8 | test.st_issues.issue_id,const | 1 | Using index |
+----+-------------+------------------+--------+---------------+--------------+---------+-------------------------------+------+-------------+
这表明优化器已为每个表选择了一个索引,它在您的查询版本中没有。我不得不猜测你的查找表的定义。
我建议的另一件事是使用自然键(类别或状态的名称)定义查找表st_categories
和st_statuses
。然后从st_issues
表中引用该自然键,而不是使用tinyint
伪表示。优点是您不必执行这些连接以获取类别或状态的名称;它已经在st_issues
表中了。