【问题标题】:Optimizing the SQL Query to reduce execution time优化 SQL 查询以减少执行时间
【发布时间】:2012-11-09 18:16:51
【问题描述】:

应用了所有过滤器的我的 SQL 查询返回 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')

我已经在论坛和外部搜索并使用了一些给出的提示,但几乎没有任何区别。我在上面的查询中使用的连接是左连接全部在 Primary Key 和 Foreign key 上。任何人都可以建议我如何修改此 sql 以减少执行时间 ....

【问题讨论】:

  • MySQLs explain 功能是你的帮手...
  • 请记住,关系数据库的问题之一是连接的扩展性非常差。
  • 表结构?,可能你想改一下:stackoverflow.com/questions/792875/…
  • 根据您使用数据的方式,您可能能够使用 memcached 或其他一些对象缓存机制来缓存结果。
  • 请发布explainexplain extended的输出。

标签: mysql


【解决方案1】:

这个 SQL 有很多多余的东西可能不会出现在 explain 中。

如果您需要一个字段,则它不应该位于左联接的表中 - 左联接适用于数据可能位于联接表中的情况,而不是必须存在的情况。

如果所有必填字段都在同一个表中,则它应该在您的第一个 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'。这将大大提高速度。

【讨论】:

  • 首先感谢您的回复。
  • 首先感谢您的回复。如您所见,表的命名是正确的。您给出解决方案的方式确实执行得很快,但根据我的测试数据,它缺少一些记录。此外,文本“否,合格”是一种福利状态,它与 BNFTSTTS 中的一个 nBStsSttsID 不完全匹配,因此我不得不使用更冗余的方法“喜欢”来获得结果。但是,如果可能的话,我会尝试规范化我的表结构以进一步减少执行时间。无论如何感谢您的解决方案,一旦解决,我将在此处发布我的原始查询。谢谢
  • 那么 Patricia 将布尔字段添加到 BNFTSTTS 并将其设置为您在此查询中所需的每个 nBStsSttsID 的想法将对您有所帮助,就像您可以 nBStsSttsIsLikeNoEligible = true 一样。您甚至可以使用查询来填充它。
【解决方案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%'

【讨论】:

  • 公平地说,OP 并没有说速度对他/她来说是不可接受的,对于他/她的老板来说可能是不可接受的——不管是谁对超过一百万人感兴趣福利状况...
  • 取回它需要 76 秒,不算破旧,但接收者可能需要 6 小时才能消费它。
  • 要求苛刻的用户没有任何问题。但是对于开发人员和老板来说,知道他们的要求很高是有帮助的。
【解决方案3】:

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/

【讨论】:

  • 好地方。为了快速修复,将like 行移到日期过滤器之后至少意味着更少的记录需要进行全文搜索。
  • 不幸的是(或幸运的是)查询优化器已经确定了运行过滤器的最佳顺序,因此移动like 对运行时没有影响。查看explain 输出肯定会有所帮助。
  • 我的立场是正确的。但是不给查询优化器更少的时间来节省一两毫秒吗?无论如何,经过进一步思考,我现在强烈怀疑“否,合格”文本来自 STATUS,并且在 BNFTSTTS 中已经有一个 int ID。
【解决方案4】:

您声称自己的连接基于良好且唯一的索引。所以几乎没有什么可以优化的。也许有一些提示:

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

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

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

  • 也许您可以专门为此查询创建一个辅助表,该表由后台进程填充。这样查询本身运行得更快,因为工作是在后台查询之前完成的。如果查询检索到的数据不一定与数据库中的每一次更改同步,这通常会起作用。

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

【讨论】:

  • 感谢您的回复。解释功能不适用于此查询可能是由于复杂性。尝试通过减少连接数来优化我的查询...
【解决方案5】:

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

【讨论】:

    【解决方案6】:

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

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

    【讨论】:

      【解决方案7】:

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

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

      【讨论】:

      • ... 以及许多其他专栏。
      猜你喜欢
      • 1970-01-01
      • 2020-02-10
      • 2023-03-03
      • 2021-07-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多