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

时间:2016-06-07 23:00:21

标签: mysql left-join query-optimization

在支持票务系统上工作,票数不多(约3,000)。要获取故障单信息的摘要网格,自定义字段表(j25_field_value)上有五个LEFT JOIN语句,其中包含大约10,000条记录。查询运行时间过长(约10秒),如果使用WHERE子句,则运行时间更长(最多约30秒或更长)。

有关改进查询以减少运行时间的任何建议吗?

四张桌子:

  • j25_support_tickets

    CREATE TABLE `j25_support_tickets` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `category_id` int(11) NOT NULL DEFAULT '0',
    `user_id` int(11) DEFAULT NULL,
    `email` varchar(50) DEFAULT NULL,
    `subject` varchar(255) DEFAULT NULL,
    `message` text,
    `modified_date` datetime DEFAULT NULL,
    `priority_id` tinyint(3) unsigned DEFAULT NULL,
    `status_id` tinyint(3) unsigned DEFAULT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `id` (`id`)
    ) ENGINE=MyISAM AUTO_INCREMENT=3868 DEFAULT CHARSET=utf8
    
  • j25_support_priorities

    CREATE TABLE `j25_support_priorities` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `title` varchar(100) DEFAULT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `id` (`id`)
    ) ENGINE=MyISAM AUTO_INCREMENT=14 DEFAULT CHARSET=utf8
    
  • j25_support_statuses

    CREATE TABLE `j25_support_statuses` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `title` varchar(255) DEFAULT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `id` (`id`)
    ) ENGINE=MyISAM AUTO_INCREMENT=7 DEFAULT CHARSET=utf8
    
  • j25_field_value(id,ticket_id,field_id,field_value)

    CREATE TABLE `j25_support_field_value` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
    `ticket_id` int(11) DEFAULT NULL,
    `field_id` int(11) DEFAULT NULL,
    `field_value` tinytext,
    PRIMARY KEY (`id`)
    ) ENGINE=MyISAM AUTO_INCREMENT=10889 DEFAULT CHARSET=utf8
    

另外,跑了这个:

    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

4 个答案:

答案 0 :(得分:2)

ALTER TABLE j25_support_field_value
ADD INDEX (`ticket_id`,`field_id`,`field_value`(50))

此索引将作为查询的覆盖索引。它将允许联接仅使用此索引来查找值。它应该比没有此索引的速度快得多,因为目前您的查询必须读取表格中的每一行,以找出与ticket_idfield_id的每个组合匹配的内容。

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

ALTER TABLE tablename ENGINE=InnoDB

答案 1 :(得分:1)

如上所述 - 更好的指数会有所帮助。然后,你可以将查询简化为类似的东西(仅加入表格一次):

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;

答案 2 :(得分:0)

您可以取消初学者的子查询,并从另一个联接中获取它们。您可以向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中,连接通常需要您用于加入的字段的索引。这样可以保持并产生非常合理的结果,使用巨大的桌子(100米+),如果遵循该规则,你就不会出错。

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;

答案 3 :(得分:0)

  • 切换到InnoDB。
  • 切换到InnoDB之后,将PRIMARY KEY的{​​{1}}设为j25_support_field_value(如果(ticket_id, field_id)则删除)。 (引发id会伤害,帮助。)
  • field_value(50)PRIMARY KEY,因此不要同时使用。{/ li>
  • 使用UNIQUE KEY代替几乎等效的VARCHAR(255)
  • EAV架构很糟糕。 My ran on EAV