优化SQL查询以减少执行时间

时间:2012-11-21 13:21:48

标签: mysql

应用了所有过滤器的我的SQL查询返回了10万(100万)条记录。要获得所有记录,它需要76.28秒..这是不可接受的。如何优化我的SQL查询,这应该花费更少的时间。 我正在使用的查询是:

    SELECT cDistName , cTlkName, cGpName, cVlgName , 
           cMmbName , dSrvyOn 
      FROM sspk.villages 
 LEFT JOIN gps  ON nVlgGpID = nGpID
 LEFT JOIN TALUKS ON nGpTlkID = nTlkID   
 left JOIN dists ON nTlkDistID = nDistID
 LEFT JOIN HHINFO ON nHLstGpID = nGpID
 LEFT JOIN MEMBERS ON nHLstID = nMmbHhiID
 LEFT JOIN BNFTSTTS  ON nMmbID = nBStsMmbID
 LEFT JOIN STATUS ON nBStsSttsID = nSttsID
 LEFT JOIN  SCHEMES ON  nBStsSchID = nSchID
     WHERE (
               (nMmbGndrID = 1 and nMmbAge between 18 and 60) 
           or  (nMmbGndrID = 2 and nMmbAge between 18 and 55)
           )
      AND cSttsDesc like 'No, Eligible' 
      AND DATE_FORMAT(dSrvyOn , '%m-%Y') < DATE_FORMAT('2012-08-01' , '%m-%Y' )
 GROUP BY cDistName , cTlkName, cGpName, cVlgName , 
        DATE_FORMAT(dSrvyOn , '%m-%Y')

我在论坛和外面搜索并使用了一些提示,但它几乎没有任何区别。我在上面的查询中使用的连接在主键和外键上保持连接。任何人都可以建议我如何修改这个SQL以减少执行时间......

7 个答案:

答案 0 :(得分:2)

先生,您是一个非常苛刻的MySQL用户!以您提到的速度从大量连接的结果集中检索的一百万条记录是每条记录76微秒。许多人会认为这是可接受的表现。请记住,您的客户端软件可能是具有该大小结果集的限制因素:它必须使用巨大的结果集并使用它做一些事情。

话虽如此,我看到了一些问题。

首先,重写您的查询,以便每个列名都由表名限定。你会为自己和下一个维护它的人做这件事。您可以快速查看WHERE条件需要做什么。

其次,请考虑此搜索条件。由于OR

,它需要两次搜索
 WHERE (
           (MEMBERS.nMmbGndrID = 1 and MEMBERS.nMmbAge between 18 and 60) 
       or  (MEMBERS.nMmbGndrID = 2 and MEMBERS.nMmbAge between 18 and 55)
       )

我猜这些标准与大多数人口相匹配 - 女性18-60岁,男性18-55岁(猜测)。你能把MEMBERS表放在LEFT JOIN列表中吗?或者你可以在你的表中放置一个派生列(MEMBERS.working_age = 1或其他一些)?

还要在MEMBERS上尝试复合索引(nMmbGndrID,nMmbAge)以加快速度。它可能有效也可能无效。

第三,考虑这个标准。

  AND DATE_FORMAT(dSrvyOn , '%m-%Y') < DATE_FORMAT('2012-08-01' , '%m-%Y' )

您已将功能应用于dSrvyOn列。这使得该搜索的索引失败。相反,试试这个。

  AND dSrvyOn >= '2102-08-01'
  AND dSrvyOn <  '2012-08-01' + INTERVAL 1 MONTH

如果您有dSrvyOn的索引,则会对该索引执行范围搜索。我的评论也适用于ORDER BY子句中的函数。

最后,正如其他人提到的那样,请勿使用LIKE来搜索=将要执行的操作。如果您想要可接受的性能,切勿使用column LIKE '%something%'

答案 1 :(得分:1)

您声称自己是基于好的和独特的索引的联接。所以没有什么可以优化的。也许有一些提示:

  • 尝试优化您的表格布局,也许您可​​以减少所需的联接数量。这可能带来比其他任何东西更多的性能优化。

  • 检查您的硬件(可用内存和内容)和服务器配置。

  • 使用mysqls explain功能查找瓶颈。

  • 也许你可以创建一个辅助表,特别是这个查询,由后台进程填充。这样查询本身运行得更快,因为工作是在后台查询之前完成的。如果查询检索的数据必须不必与数据库中的每个单独更改同步,那么这通常有效。

  • 检查RDBMS是否真的是正确的数据库类型。出于许多目的,图形数据库效率更高,性能更好。

答案 2 :(得分:0)

尝试向nMmbGndrID,nMmbAge和cSttsDesc添加索引,看看是否有助于您的查询。

此外,您可以在select语句之前使用“Explain”命令,为您提供有关可能做得更好的一些提示。有关解释的详细信息,请参阅MySQL Reference

答案 3 :(得分:0)

这个SQL有许多冗余,可能不会出现在explain中。

如果你需要一个字段,它不应该在LEFT JOIN中的表中 - 左连接是指数据可能在连接表中,而不是必须时。

如果所有必填字段都在同一个表中,那么它应该在您的第一个FROM中。

如果您的文本搜索是可预测的(不是来自用户输入)并且与单个已知ID相关,请使用ID而不是文本搜索(支持Patricia查找LIKE瓶颈)。

由于缺少表格提示,您的查询很难阅读,但您的字段名称似乎确实存在模式。

您需要nMmbGndrIDnMmbAge才能拥有一个值,但这些可能是在MEMBERS中,其中5个是左连接。这是一种冗余。

请记住,你可以这样做一个简单的连接:

FROM sspk.villages, gps, TALUKS, dists, HHINFO, MEMBERS [...] WHERE [...] nVlgGpID = nGpID AND nGpTlkID = nTlkID AND nTlkDistID = nDistID AND nHLstGpID = nGpID AND nHLstID = nMmbHhiID

看起来cSttsDesc来自STATUS。但是如果文本'No, Eligible'恰好与BNFTSTTS中的一个nBStsSttsID匹配,那么找出该值并使用它!如果是7,请取出LEFT JOIN STATUS ON nBStsSttsID = nSttsID并将AND cSttsDesc like 'No, Eligible'替换为AND nBStsSttsID = '7'。这将大大提高速度。

答案 4 :(得分:0)

如果连接中使用的表最少用于更新查询,那么您可以将引擎类型从INNODB更改为MyISAM。

MyISAM中的选择查询运行速度比INNODB快2倍,但MyISAM中的更新和插入查询要慢得多。

答案 5 :(得分:0)

您可以创建视图以避免长时间的查询和时间。

答案 6 :(得分:0)

您的like运营商可能会阻止您 - 使用like进行全文搜索并不是MySQL的强项。

考虑在cSttsDesc上设置全文索引(确保它首先是TEXT字段。)

ALTER TABLE articles ADD FULLTEXT(cSttsDesc);

SELECT
    *
FROM
    table_name
 WHERE MATCH(cSttsDesc) AGAINST('No, Eligible')

或者,您可以设置布尔标志而不是cSttsDesc like 'No, Eligible'

来源:http://devzone.zend.com/26/using-mysql-full-text-searching/