我用MySQL数据库用PHP编写了一个作业请求系统,但查询速度慢。
我的模式(简化)如下:
tbl_job
job_id
job_descrequester_user_id
tbl_user
user_id
user_name
tbl_workermap
workermap_id
job_id
worker_user_id
一个包含作业的表,一个可能的工作者的用户表,以及一个将工作者映射到作业的表。一份工作可以有一个或多个工人,一个工人可以有一份或多份工作。
tbl_user包含请求工作的用户和处理作业的用户,因此用户id存储在tbl_workermap中的worker_user_id和tbl_job 中的requester_user_id
当一个作业被记录时,它会在tbl_job中创建一个条目,但在tbl_workermap中不创建任何条目,直到有人专门分配了一个工人。这意味着,当我查询作业时,我使用左联接进行查询,因为tbl_workermap中没有每个作业的条目:
SELECT
job.job_id,
job.job_desc,
workermap.worker_user_id,
worker.worker_name
FROM tbl_job AS job
LEFT JOIN tbl_workermap AS workermap
ON job.job_id = workermap.job_id
LEFT JOIN tbl_user AS worker
ON workermap.worker_user_id = worker.user_id
该系统已经使用了一段时间,我现在在tbl_job中有大约8000个条目,在tbl_workermap中有7000个条目,检索所有结果需要4秒钟以上的时间。EXPLAIN查询显示tbl_workermap联接返回大约7000行和"使用where;使用联接缓冲区(块嵌套循环)"。
我能做些什么来加快速度吗?
EDIT:添加表信息
我简化了解释,但这是实际的表结构。有更多的联接,但tbl_workermap是唯一有问题的:
CREATE TABLE `tbl_job` (
`job_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`job_title` varchar(100) DEFAULT NULL,
`job_description` text,
`job_added_datetime` int(11) DEFAULT '0',
`job_due_datetime` int(11) NOT NULL DEFAULT '0',
`job_time_estimate` int(11) DEFAULT NULL,
`job_additional_fields` text,
`addedby_user_id` int(11) NOT NULL DEFAULT '0',
`requester_user_id` int(11) NOT NULL DEFAULT '0',
`worker_user_id` int(11) NOT NULL DEFAULT '0',
`job_active` tinyint(4) NOT NULL DEFAULT '1',
`site_id` tinyint(4) NOT NULL DEFAULT '1',
`status_id` int(11) NOT NULL DEFAULT '1',
`estimategroup_id` int(11) DEFAULT '1',
`brand_id` int(11) DEFAULT '1',
`job_isproject` tinyint(4) DEFAULT NULL,
PRIMARY KEY (`job_id`),
FULLTEXT KEY `job_title` (`job_title`,`job_description`,`job_additional_fields`)
) ENGINE=MyISAM AUTO_INCREMENT=8285 DEFAULT CHARSET=latin1
CREATE TABLE `tbl_user` (
`user_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`user_shortname` varchar(30) DEFAULT NULL,
`user_name` varchar(30) DEFAULT NULL,
`user_password` varchar(50) DEFAULT NULL,
`user_password_reset_uuid` varchar(50) DEFAULT NULL,
`user_email` varchar(50) DEFAULT NULL,
`user_description` text,
`user_sortorder` int(11) NOT NULL DEFAULT '0',
`user_isworker` tinyint(4) NOT NULL DEFAULT '0',
`user_active` tinyint(4) NOT NULL DEFAULT '1',
`site_id` tinyint(4) NOT NULL DEFAULT '0',
`user_avatar_file_id` int(11) DEFAULT NULL,
`user_avatar_hub_url` varchar(100) DEFAULT NULL,
PRIMARY KEY (`user_id`)
) ENGINE=MyISAM AUTO_INCREMENT=917 DEFAULT CHARSET=latin1
CREATE TABLE `tbl_workermap` (
`workermap_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`job_id` int(11) DEFAULT NULL,
`workermap_datetime_added` int(11) DEFAULT NULL,
`workermap_datetime_removed` int(11) DEFAULT NULL,
`worker_user_id` int(11) DEFAULT NULL,
`addedby_user_id` int(11) DEFAULT NULL,
`removedby_user_id` int(11) DEFAULT NULL,
`site_id` int(11) DEFAULT NULL,
`workermap_isassigned` int(11) DEFAULT NULL,
`workermap_active` int(11) NOT NULL DEFAULT '1',
PRIMARY KEY (`workermap_id`)
) ENGINE=InnoDB AUTO_INCREMENT=7145 DEFAULT CHARSET=latin1
显示索引
+---------+---+-----------+---+-----------------------+------+------+------+------+-----+----------+--+--+
| tbl_job | 0 | PRIMARY | 1 | job_id | A | 8283 | NULL | NULL | | BTREE | | |
+---------+---+-----------+---+-----------------------+------+------+------+------+-----+----------+--+--+
| tbl_job | 1 | job_title | 1 | job_title | NULL | 1 | NULL | NULL | YES | FULLTEXT | | |
| tbl_job | 1 | job_title | 2 | job_description | NULL | 1 | NULL | NULL | YES | FULLTEXT | | |
| tbl_job | 1 | job_title | 3 | job_additional_fields | NULL | 1 | NULL | NULL | YES | FULLTEXT | | |
+---------+---+-----------+---+-----------------------+------+------+------+------+-----+----------+--+--+
+----------+---+---------+---+---------+---+-----+------+------+--+-------+--+--+
| tbl_user | 0 | PRIMARY | 1 | user_id | A | 910 | NULL | NULL | | BTREE | | |
+----------+---+---------+---+---------+---+-----+------+------+--+-------+--+--+
+---------------+---+---------+---+--------------+---+------+------+------+--+-------+--+--+
| tbl_workermap | 0 | PRIMARY | 1 | workermap_id | A | 7184 | NULL | NULL | | BTREE | | |
+---------------+---+---------+---+--------------+---+------+------+------+--+-------+--+--+
EXPLAIN查询
+---+--------+----------------+--------+---------+---------+------+-------------------------------+------+----------------------------------------------------+
| 1 | SIMPLE | job | ALL | NULL | NULL | NULL | NULL | 8283 | Using where; Using temporary; Using filesort |
+---+--------+----------------+--------+---------+---------+------+-------------------------------+------+----------------------------------------------------+
| 1 | SIMPLE | estimategroup | eq_ref | PRIMARY | PRIMARY | 4 | jobq.job.estimategroup_id | 1 | Using where |
| 1 | SIMPLE | brand | eq_ref | PRIMARY | PRIMARY | 4 | jobq.job.brand_id | 1 | Using index condition |
| 1 | SIMPLE | site | eq_ref | PRIMARY | PRIMARY | 4 | jobq.job.site_id | 1 | Using where |
| 1 | SIMPLE | addedby | eq_ref | PRIMARY | PRIMARY | 4 | jobq.job.addedby_user_id | 1 | Using index condition |
| 1 | SIMPLE | requester | eq_ref | PRIMARY | PRIMARY | 4 | jobq.job.requester_user_id | 1 | Using index condition |
| 1 | SIMPLE | worker | eq_ref | PRIMARY | PRIMARY | 4 | jobq.job.worker_user_id | 1 | Using index condition |
| 1 | SIMPLE | status | ALL | PRIMARY | NULL | NULL | NULL | 6 | Using where; Using join buffer (Block Nested Loop) |
| 1 | SIMPLE | workermap | ALL | NULL | NULL | NULL | NULL | 7184 | Using where; Using join buffer (Block Nested Loop) |
| 1 | SIMPLE | user_workermap | eq_ref | PRIMARY | PRIMARY | 4 | jobq.workermap.worker_user_id | 1 | Using where |
| 1 | SIMPLE | categorymap | ALL | NULL | NULL | NULL | NULL | 1 | Using where; Using join buffer (Block Nested Loop) |
| 1 | SIMPLE | category | eq_ref | PRIMARY | PRIMARY | 4 | jobq.categorymap.category_id | 1 | Using where |
+---+--------+----------------+--------+---------+---------+------+-------------------------------+------+----------------------------------------------------+
2019年1月18日13:43目前,您需要两个索引来覆盖JOIN=需要索引的左右对象的基本规则。1.ALTER TABLE tbl_workermap ADD INDEX idx_t_w_map_job_id(job_id)2。创建后的ALTER TABLE tbl_workermap ADD INDEX idx_t_w_map_wrk_user_id(worker_user_id)运行EXPLAIN。。。。。查看新的执行计划。
如果您还没有,请通过设置主键创建聚集索引(假设表已正确规范化)。(如果你还没有,你可能也想设置外键约束。)
如果此问题涉及Microsoft SQL Server,我建议创建一个存储过程,特别是如果这是作为某种常规进程频繁运行的查询。然而,正如这个答案中所指出的,像这样的简单查询的主要性能优势将来自于表设计和索引。
"一个工作可以有一个或多个工人。"反之亦然(一个工人可以从事多个工作)?如果不是,那么您只有1:many,并且不应该用那个额外的表来实现它。
假设它真的是many:many,这里有关于优化该表的几个提示。
不要使用LEFT
,除非您希望"正确"的表缺少所需的行。
风格提示:去掉tbl_
、user_
(除了user_id
)等。也就是说,名称上的前缀是杂乱的,与上下文无关。在"用户"与"工作人员"之间保持一致。
使用两个目标命名many:many表(例如worker_job
)。然而,我现在看到的不仅仅是一个简单的many:many映射表;它更像是一个分配和跟踪谁在一段时间内处理的表?
如果您需要谁在上工作的历史和谁正在做什么的当前状态,请考虑使用两个表。历史在不断发展;"电流"不断变化。
使用适当的数据类型,如DATE
和DATETIME
。
使用InnoDB而不是MyISAM。