MySQL左连接,分组依据,排序依据,极限=可怕的性能

时间:2010-10-16 09:37:26

标签: php mysql database performance

我目前正在开发一个应用程序,允许用户使用各种参数搜索文档数据库并返回一组分页结果。我在PHP / MySQL中构建它,这不是我通常的开发平台,但它到目前为止一直很盛大。

我遇到的问题是,为了返回一整套结果,我必须在每张桌子上使用LEFT JOIN,这完全破坏了我的表现。开发数据库的人说我正在使用的查询将返回正确的结果,这就是我必须使用的。查询如下,我绝不是SQL大师,可以使用一些帮助。

我一直在想将查询拆分成子查询可能会更好吗?以下是我目前的疑问:

    SELECT d.title, d.deposition_id, d.folio_start, d.folio_end, pl.place_id, p.surname, p.forename, p.person_type_id, pt.person_type_desc, p.age, d.manuscript_number, dt.day, dt.month, dt.year, plc.county_id, c.county_desc
 FROM deposition d 
 LEFT JOIN person AS p ON p.deposition_id = d.deposition_id 
 LEFT JOIN person_type AS pt ON p.person_type_id = pt.person_type_id 
 LEFT JOIN place_link AS pl ON pl.deposition_id = d.deposition_id 
 LEFT JOIN date AS dt ON dt.deposition_id = d.deposition_id 
 LEFT JOIN place AS plc ON pl.place_id = plc.place_id 
 LEFT JOIN county AS c ON plc.county_id = c.county_id
 WHERE 1 AND d.manuscript_number = '840' 
 GROUP BY d.deposition_id ORDER BY d.folio_start ASC
 LIMIT 0, 20

非常感谢任何帮助或指导!

沉积表:

CREATE TABLE IF NOT EXISTS `deposition` (
  `deposition_id` varchar(11) NOT NULL default '',
  `manuscript_number` int(10) NOT NULL default '0',
  `folio_start` varchar(4) NOT NULL default '0',
  `folio_end` varchar(4) default '0',
  `page` int(4) default NULL,
  `deposition_type_id` int(10) NOT NULL default '0',
  `comments` varchar(255) default '',
  `title` varchar(255) default NULL,
  PRIMARY KEY  (`deposition_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

日期表

CREATE TABLE IF NOT EXISTS `date` (
  `deposition_id` varchar(11) NOT NULL default '',
  `day` int(2) default NULL,
  `month` int(2) default NULL,
  `year` int(4) default NULL,
  PRIMARY KEY  (`deposition_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Person_Type

CREATE TABLE IF NOT EXISTS `person_type` (
  `person_type_id` int(10) NOT NULL auto_increment,
  `person_type_desc` varchar(255) NOT NULL default '',
  PRIMARY KEY  (`person_type_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=59 ;

3 个答案:

答案 0 :(得分:2)

似乎您希望每次沉积时选择一个人,地点等。

您编写的查询将返回给您,但不保证它将返回哪一个,并且查询效率低下。

试试这个:

SELECT  d.title, d.deposition_id, d.folio_start, d.folio_end, pl.place_id, p.surname, p.forename, p.person_type_id, pt.person_type_desc, p.age, d.manuscript_number, dt.day, dt.month, dt.year, plc.county_id, c.county_desc
FROM    deposition d
LEFT JOIN
        person p
ON      p.id = 
        (
        SELECT  id
        FROM    person pi
        WHERE   pi.deposition_id = d.deposition_id
        ORDER BY
                pi.deposition_id, pi.id
        LIMIT 1
        )
LEFT JOIN
        place_link AS pl
ON      pl.id = 
        (
        SELECT  id
        FROM    place_link AS pli
        WHERE   pli.deposition_id = d.deposition_id
        ORDER BY
                pli.deposition_id, pi.id
        LIMIT 1
        )
LEFT JOIN
        date AS dt
ON      dt.id = 
        (
        SELECT  id
        FROM    date AS dti
        WHERE   dti.deposition_id = d.deposition_id
        ORDER BY
                dti.deposition_id, pi.id
        LIMIT 1
        )
LEFT JOIN
        place AS plc
ON      plc.place_id = pl.place_id 
LEFT JOIN
        county AS c
ON      c.county_id = plc.county_id
WHERE   d.manuscript_number = '840' 
ORDER BY
        d.manuscript_number, d.folio_start
LIMIT   20

deposition (manuscript_number, folio_start)上创建一个索引,以便快速工作

还在(deposition_id, id)personplace_link上的date上创建综合索引。

答案 1 :(得分:1)

如果连接的表可能没有匹配的值,则只需要LEFT JOIN。您的数据库架构中person是否可能没有匹配的person_type?或depositiondate中没有匹配的行? place没有匹配的county

对于结果有意义的任何关系,您可以将LEFT JOIN更改为INNER JOIN

这些列应具有索引(如果可能,则为唯一):

person.deposition_id
date.deposition_id
place_link.deposition_id
place_link.place_id

date表看起来像一个糟糕的设计;我想不出有一个日期表的原因,而不是只在date表中放置datetime(或deposition)类型的列。并且date是表的可怕名称,因为它是SQL保留字。

答案 2 :(得分:1)

糟糕的表现几乎可以肯定是缺乏指数。您的存款表没有任何索引,这可能意味着您引用的其他表也没有任何索引。您可以首先在沉积表中添加索引。从MySQL shell或phpMyAdmin发出以下查询。

ALTER TABLE沉积ADD INDEX(deposition_id,manuscript_number);

如果在添加索引后查询执行得更快,您就知道自己处于正确的轨道上。从那里,您可能希望将索引放在引用列上的其他表上。例如,对于这部分查询“LEFT JOIN person AS p ON p.deposition_id = d.deposition_id”,您可以尝试使用。添加索引到人员表。

ALTER TABLE person ADD INDEX(deposition_id);