Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/github/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为简单的三表模式优化MySQL左连接查询?_Mysql_Join_Query Optimization - Fatal编程技术网

为简单的三表模式优化MySQL左连接查询?

为简单的三表模式优化MySQL左连接查询?,mysql,join,query-optimization,Mysql,Join,Query Optimization,我用PHP编写了一个带有MySQL数据库的作业请求系统,但我遇到了查询速度慢的问题 我的模式(简化)如下: tbl\U作业 作业id 工作描述 请求者\用户\ id tbl\u用户 用户id 用户名 tbl_workermap 工人映射id 作业id 工作者\用户\ id 一个包含作业的表,一个用于可能的工人的用户表,以及一个用于将工人映射到作业的表。一个工作可以有一个或多个工人,一个工人可以有一个或多个工作 tbl_user包含请求工作的用户和处理作业的用户,因此用户id存储在tbl_work

我用PHP编写了一个带有MySQL数据库的作业请求系统,但我遇到了查询速度慢的问题

我的模式(简化)如下:

tbl\U作业
作业id
工作描述 请求者\用户\ id

tbl\u用户
用户id
用户名

tbl_workermap
工人映射id
作业id
工作者\用户\ id

一个包含作业的表,一个用于可能的工人的用户表,以及一个用于将工人映射到作业的表。一个工作可以有一个或多个工人,一个工人可以有一个或多个工作

tbl_user包含请求工作的用户和处理作业的用户,因此用户id存储在tbl_workermap中的worker_user_id和tbl_job中的requester_user_id下

记录作业时,它会在tbl_作业中创建一个条目,但在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_作业中有大约8000个条目,在tbl_workermap中有7000个条目,检索所有结果需要4秒钟。EXPLAIN查询显示tbl_workermap连接返回大约7000行和“使用where;使用连接缓冲区(块嵌套循环)”

我能做些什么来加快速度吗

编辑:添加表格信息
我简化了一些解释,但这里是实际的表结构。有更多的联接,但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 |  |  |
+---------------+---+---------+---+--------------+---+------+------+------+--+-------+--+--+
解释问题

+---+--------+----------------+--------+---------+---------+------+-------------------------------+------+----------------------------------------------------+
| 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                                        |
+---+--------+----------------+--------+---------+---------+------+-------------------------------+------+----------------------------------------------------+


如果尚未创建聚集索引,请通过(假设表已正确规范化)创建聚集索引。(如果您没有,您可能也会希望这样做。)

如果这个问题涉及Microsoft SQL Server,我建议创建一个存储过程,特别是如果这是一个作为某种常规过程频繁运行的查询。但是,如中所述,像这样的简单查询的主要性能优势将来自表设计和索引。

一个作业可以有一个或多个工人。反之亦然(一个工人可以有多个作业)?如果没有,那么您只有1:many,并且不应该使用额外的表来实现它

假设它真的很多:很多,那么有一些关于优化该表的提示

不要使用
LEFT
,除非您希望“right”表缺少所需的行

风格提示:摆脱
tbl
user
(除了
user\u id
)等等。这意味着名称的前缀杂乱无章,与上下文无关。在“用户”与“工人”之间保持一致

使用两个目标命名many:many表(例如,
worker\u job
)。然而,我现在看到它不仅仅是一个简单的多:多映射表;它更像是一个用来分配和跟踪谁在做什么的表格

如果你需要一个关于谁的工作的历史和谁在做什么的当前状态,考虑两张表。历史在不断发展;“电流”不断变化

使用合适的数据类型,例如
DATE
DATETIME


使用InnoDB而不是MyISAM。

2019年1月18日13:43目前,您需要两个索引来涵盖JOIN的左右对象的基本规则=需要索引。1.更改表tbl_workermap添加索引idx_t_w_map_job_id(job_id)2。创建后更改表tbl_workermap添加索引idx_t_w_map_wrk_user_id(worker_user_id)运行解释。。。。。查看新的执行计划。

请发布显示创建表tbl_作业的文本结果;和tbl_工人;和tbl_workermap;以及显示来自tbl_作业的索引;和tbl_工人;和tbl_workermap;用于分析。另外,发布解释。。。。结果,请。@WilsonHauck我已经添加了这些信息,谢谢。目前,您需要两个索引来涵盖JOIN的左对象和右对象的基本规则=需要索引。1.更改表tbl_workermap添加索引idx_t_w_map_job_id(job_id)2。创建后更改表tbl_workermap添加索引idx_t_w_map_wrk_user_id(worker_user_id)运行解释。。。。。看到新的执行plan@WilsonHauck谢谢,我已经添加了这两个索引,这就完成了,现在就快到了。如果你能添加这些信息作为答案,我可以将其标记为已接受,这样你就可以获得学分。@WilsonHauck我很乐意这样做,而且刚刚这么做:)再次感谢。谢谢Paul。我正在使用主键,我已经编辑了我的帖子来展示如何设置这些键。@Adrian,看起来你有一个比我想象/希望的有趣得多的问题。关于我的存储过程注释,我错了。如果您使用的是SQL Server,则存储过程可能会为您省去一些麻烦,但MySQL的情况似乎并非如此。谢谢Rick,是的,一个工作人员可以有多个作业,因此它是多个:多个。一个工作可能有一个、多个或没有分配工人,因此任何给定工作的tbl_workermap中可能有条目,也可能没有条目。我编辑了我原来的帖子来澄清。我现在将阅读该链接。@Adrian-我添加了几条评论。感谢这些提示,
user\u id
worker\u user\u id
的原因是用户表同时包含worker和其他用户。分配工作的人不一定是工人。我已经使用上面WilsonHauck的建议解决了性能问题,但是如果随着数据库大小的增长,这成为一个问题,那么您建议的两个表听起来是一个不错的解决方案。这解决了问题。我在tbl_workermap上为
job_id
worker_user_id
添加了索引,并重新运行解释查询。它不再从tbl_workermap检索7000多行,查询现在几乎是即时的。非常感谢。