使用 5 个左连接提高 MySQL 查询的速度

Improve speed of MySQL query with 5 left joins

正在开发支持票务系统,但票数不多(~3,000)。要获取票证信息的摘要网格,自定义字段 table (j25_field_value) 上有五个 LEFT JOIN 语句,包含大约 10,000 条记录。查询 运行 太长(~10 秒)并且在带有 WHERE 子句的情况下,它 运行 甚至更长(最多 ~30 秒或更多)。

关于改进查询以将时间减少到 运行 的任何建议?

四个table:

还有,运行这个:

    SELECT LENGTH(field_value) len FROM j25_support_field_value ORDER BY len DESC LIMIT 1
    note:  the result = 38

查询:

SELECT DISTINCT t.id as ID
, (select p.title from j25_support_priorities p where p.id = t.priority_id) as Priority
, (select s.title from j25_support_statuses   s where s.id = t.status_id)   as Status
,     t.subject       as Subject
,     t.email         as SubmittedByEmail
,  type.field_value   AS IssueType
,   ver.field_value   AS Version
, utype.field_value   AS UserType
,  cust.field_value   AS Company
, refno.field_value   AS RefNo
, t.modified_date     as Modified
    FROM j25_support_tickets AS t
LEFT JOIN j25_support_field_value AS type  ON t.id = type.ticket_id  AND  type.field_id =1
LEFT JOIN j25_support_field_value AS ver   ON t.id = ver.ticket_id   AND   ver.field_id =2
LEFT JOIN j25_support_field_value AS utype ON t.id = utype.ticket_id AND utype.field_id =3
LEFT JOIN j25_support_field_value AS cust  ON t.id = cust.ticket_id  AND  cust.field_id =4
LEFT JOIN j25_support_field_value AS refno ON t.id = refno.ticket_id AND refno.field_id =5

您可以取消 starters 的子查询,只从另一个连接中获取它们。您可以添加索引 j25_support_field_value

alter table j25_support_field_value add key(id, field_type);

我假设 j25_support_tickets 中的 id 有一个索引 - 如果没有并且它们是唯一的,请添加一个唯一索引 alter table j25_support_tickets add unique key(id); 如果它们不是唯一的,请从中删除唯一这个词声明。

在 MySQL 中,联接通常需要您用于联接的字段的索引。这将适用于巨大的表(100m+)并产生非常合理的结果,如果你遵循这个规则,你就不会出错。

j25_support_tickets 中的 ID 是否唯一?如果他们是你可以取消不同 - 如果不是,或者如果你在每一行中得到精确的重复,仍然取消不同并添加一个 t.id 到这个末尾的组:

SELECT t.id as ID
, p.title  as Priority
, s.title as Status
,     t.subject       as Subject
,     t.email         as SubmittedByEmail
,  type.field_value   AS IssueType
,   ver.field_value   AS Version
, utype.field_value   AS UserType
,  cust.field_value   AS Company
, refno.field_value   AS RefNo
, t.modified_date     as Modified
    FROM  j25_support_tickets AS t
LEFT JOIN j25_support_field_value AS type  ON t.id = type.ticket_id  AND  type.field_id =1
LEFT JOIN j25_support_field_value AS ver   ON t.id = ver.ticket_id   AND   ver.field_id =2
LEFT JOIN j25_support_field_value AS utype ON t.id = utype.ticket_id AND utype.field_id =3
LEFT JOIN j25_support_field_value AS cust  ON t.id = cust.ticket_id  AND  cust.field_id =4
LEFT JOIN j25_support_field_value AS refno ON t.id = refno.ticket_id AND refno.field_id =5
LEFT JOIN j25_support_priorities p ON p.id = t.priority_id
LEFT JOIN j25_support_statuses  s ON s.id = t.status_id;
ALTER TABLE j25_support_field_value
ADD INDEX (`ticket_id`,`field_id`,`field_value`(50))

该索引将作为您查询的覆盖索引。它将允许连接仅使用该索引来查找值。它的执行速度应该比没有此索引快得多,因为目前您的查询必须读取 table 中的每一行才能找到与 ticket_idfield_id.[=14= 的每个组合相匹配的内容]

我还建议将您的 table 转换为 InnoDB 引擎,除非您有非常明确的理由使用 MyISAM。

ALTER TABLE tablename ENGINE=InnoDB

同上 - 更好的索引会有所帮助。然后,您可能也可以将查询简化为类似的内容(仅加入 table 一次):

SELECT t.id as ID
, p.title  as Priority
, s.title as Status
,     t.subject       as Subject
,     t.email         as SubmittedByEmail
, case when v.field_id=1 then v.field_value else null end as IssueType
, case when v.field_id=2 then v.field_value else null end as Version
, case when v.field_id=3 then v.field_value else null end as UserType
, case when v.field_id=4 then v.field_value else null end as Company
, case when v.field_id=5 then v.field_value else null end as RefNo
 , t.modified_date     as Modified
    FROM  j25_support_tickets AS t
LEFT JOIN j25_support_field_value v ON t.id = v.ticket_id
LEFT JOIN j25_support_priorities p ON p.id = t.priority_id
LEFT JOIN j25_support_statuses  s ON s.id = t.status_id;
  • 切换到 InnoDB。
  • 切换到 InnoDB 后,使 j25_support_field_valuePRIMARY KEY(ticket_id, field_id)(如果 id 则删除) . (攻击 field_value(50) 会很痛, 不会 有帮助。)
  • 一个 PRIMARY KEY 是一个 UNIQUE KEY,所以不要两者都有。
  • 使用 VARCHAR(255) 而不是几乎等价的 TINYTEXT
  • EAV 架构糟透了。 My ran on EAV.