如果它具有索引,我如何优化MySQL查询,事件

时间:2015-10-26 09:54:33

标签: php mysql

这是我的查询,需要17.9397 sec时间来获得回复:

SELECT allbar.iBarID AS iBarID,
       allbar.vName AS vName,
       allbar.tAddress AS tAddress,
       allbar.tDescription AS tDescription,

  (SELECT COUNT(*)
   FROM tbl_post p
   WHERE p.vBarIDs = allbar.iBarID) AS `total_post`,
       allbar.bar_usbg AS bar_usbg,
       allbar.bar_enhance AS bar_enhance,

  (SELECT count(*)
   FROM tbl_user
   WHERE FIND_IN_SET(allbar.iBarID,vBarIDs)
     AND (eType = 'Bartender'
          OR eType = 'Bar Manager'
          OR eType = 'Bar Owner')) AS countAss,
       allbar.eStatus AS eStatus
FROM
  (SELECT DISTINCT b.iBarID AS iBarID,
                   b.vName AS vName,
                   b.tAddress AS tAddress,
                   (CASE LENGTH(b.tDescription) WHEN 0 THEN '' WHEN LENGTH(b.tDescription) > 0
                    AND LENGTH(b.tDescription) < 50 THEN CONCAT(LEFT(b.tDescription, 50),'...') ELSE b.tDescription END) AS tDescription,
                   b.usbg AS bar_usbg,
                   b.enhance AS bar_enhance,
                   b.eStatus AS eStatus
   FROM tbl_bar b,
        tbl_user u
   WHERE b.iBarID <> '-10') AS allbar

我试过EXPLAIN,结果如下:

enter image description here

有人能解释一下这个EXPLAIN结果吗?

1 个答案:

答案 0 :(得分:5)

你应该完全重写那个查询,这完全是胡说八道。

在这部分

  (SELECT DISTINCT b.<whatever>
   FROM tbl_bar b,
        tbl_user u
   WHERE b.iBarID <> '-10') AS allbar

你基本上做的是将表tbl_bar中的每一行与tbl_user中的每一行连接起来。然后过滤tbl_bar,当选择所有内容时(也许MySQL必须在执行此操作之前将所有内容写入临时表中)返回结果集而不重复。你不想做那件事。特别是当你甚至不从tbl_user中选择任何东西时。如果有连接,请指定它。如果没有,请不要加入这些表或创建连接。我不知道你的表是否或如何连接,但它应该是这样的:

  (SELECT DISTINCT b.<whatever>
   FROM tbl_bar b
   JOIN tbl_user u ON b.user_id = u.id /*or whatever the connection is*/
   WHERE b.iBarID <> '-10') AS allbar

然后你有这个丑陋的子查询。

  (SELECT COUNT(*)
   FROM tbl_post p
   WHERE p.vBarIDs = allbar.iBarID) AS `total_post`,
       allbar.bar_usbg AS bar_usbg,
       allbar.bar_enhance AS bar_enhance,

顺便说一下依赖(参见你的解释输出)。这意味着,对于外部查询的每一行执行此子查询(是的,如上所述,具有交叉连接的那一行)。而不是此子查询,请加入外部查询中的表并使用GROUP BY

到目前为止,查询应该如下所示:

SELECT 
b.iBarID AS iBarID,
b.vName AS vName,
b.tAddress AS tAddress,
b.tDescription AS tDescription,

COUNT(*) AS `total_post`,
allbar.bar_usbg AS bar_usbg,
allbar.bar_enhance AS bar_enhance

FROM
tbl_bar b
JOIN tbl_user u ON b.user_id = u.id
JOIN tbl_post p ON p.vBarIDs = b.iBarID
WHERE b.iBarID <> '-10'
GROUP BY b.iBarID 

(事实上,这是不对的。规则是,SELECT子句中的每一列都应该在GROUP BY子句中,或者具有聚合函数(如{ {1}}或count()已应用于此。否则会显示每个组的随机行。但这只是一个示例。您必须计算详细信息。)

现在是最糟糕的部分。

max()

使用 (SELECT count(*) FROM tbl_user WHERE FIND_IN_SET(allbar.iBarID,vBarIDs) AND (eType = 'Bartender' OR eType = 'Bar Manager' OR eType = 'Bar Owner')) AS countAss, allbar.eStatus AS eStatus 表示您在一列中存储了多个值。再一次,你永远不想那样做。请阅读此answer to Is storing a delimited list in a database column really that bad?,然后重新设计您的数据库。我不会帮助你解决这个问题,因为这显然是针对一个单独问题的东西。

所有这些并没有真正解释FIND_IN_SET()结果。对于这个问题,我将不得不写一个完整的教程,我不会这样做,因为一切都在manual,一如既往。