SQL查询运行真的很慢

时间:2014-05-24 11:17:50

标签: mysql performance

我正在运行此查询来搜索数据库:

SELECT
    IFNULL(firstname, '') AS firstname,
    IFNULL(lastname, '') AS lastname,
    IFNULL(age, ' ') AS age,
    email,
    telephone,
    comments,
    ref
FROM person
RIGHT JOIN
    order ON person.oID = order.ref
WHERE
    LOWER(firstname) LIKE LOWER ('%{$search}%') OR
    LOWER(lastname) LIKE LOWER ('%{$search}%') OR
    LOWER(email) LIKE LOWER ('%{$search}%') OR
    LOWER(telephone) LIKE LOWER ('%{$search}%') OR
    LOWER(ref) LIKE LOWER ('%{$search}%');

它正在进行大量处理,但我怎样才能更快地获得这些结果?该页面加载大约需要6-7秒,如果我在PHPMyAdmin中运行查询,则查询需要3-4秒才能运行。它不是一个庞大的数据库,3000条左右。我已为refemailfirstnamelastname列添加了索引,但这似乎没有任何区别。有人可以帮忙吗?

3 个答案:

答案 0 :(得分:1)

这个查询速度慢的原因是因为你以最慢的方式结合了MySQL的两个方便但缓慢的功能。

  1. FUNCTION(column) LIKE %matchstring%需要扫描表格;没有有序的索引可以帮助满足此搜索,因为它没有固定。

  2. conditionconditioncondition要求每个OR子句重新扫描一次表。

  3. 如果您正确设置了列排序规则,那么您也忽略了MySQL的搜索已经不区分大小写的事实。

    最后,我们不清楚您对RIGHT JOIN ed表格数据的处理方式。结果集的哪些列来自该表?如果您不需要该表中的数据,请将其删除。

    总而言之,你所拥有的是慢x多

    那么,你怎么解决这个问题呢?最重要的是让你尽可能多地摆脱这些无法扫描的扫描。如果你可以改为

     email LIKE '{$search}%' 
    

    因此可以消除LOWER()条款中的%个函数和前导LIKE,您将获得巨大的胜利。

    如果这种广播网络搜索功能对您的应用程序至关重要,您应该考虑使用MySQL全文搜索。

    或者您可以考虑在表格中创建一个新列,该列是您当前搜索的所有列的串联,因此您只需搜索一次。


    编辑以解释LIKE慢度

    如果列haystack已编入索引,则搜索haystack LIKE 'needle%'会很快运行。这是因为BTREE风格指数本身就是有序的。要以这种方式搜索,MySQL可以随机访问第一个可能的匹配,然后按顺序扫描到最后一个可能的匹配。

    但搜索haystack LIKE '%needle%'无法使用随机访问来查找索引中的第一个可能匹配项。第一场可能的比赛可能在任何地方。因此,必须逐一扫描haystack needle的所有值。

答案 1 :(得分:1)

我建议您将right join更改为inner join。您正在查找的字段看起来无论如何都来自person表,因此where子句将查询转换为内连接。

SELECT
    IFNULL(firstname, '') AS firstname,
    IFNULL(lastname, '') AS lastname,
    IFNULL(age, ' ') AS age,
    email,
    telephone,
    comments,
    ref
FROM person INNER JOIN
     order
     ON person.oID = order.ref
WHERE
    LOWER(firstname) LIKE LOWER ('%{$search}%') OR
    LOWER(lastname) LIKE LOWER ('%{$search}%') OR
    LOWER(email) LIKE LOWER ('%{$search}%') OR
    LOWER(telephone) LIKE LOWER ('%{$search}%') OR
    LOWER(ref) LIKE LOWER ('%{$search}%');

其次,在order(ref)上创建索引。这应该会大大减少where子句的搜索空间。语法是:

create index order_ref on `order`(ref);

顺便说一句,order是表的错误名称,因为它是一个SQL保留字。我建议改为orders

答案 2 :(得分:0)

为什么不使用全文搜索而不是一堆OR和LOWER?

   SELECT
IFNULL(firstname, '') AS firstname,
IFNULL(lastname, '') AS lastname,
IFNULL(age, ' ') AS age,
email,
telephone,
comments,
ref
FROM person
RIGHT JOIN
order ON person.oID = order.ref
WHERE
 MATCH (LOWER(firstname), LOWER(lastname),LOWER(email),LOWER(ref)) 
       AGAINST ('$search' IN BOOLEAN MODE)

要更快地运行,您需要添加索引。

ALTER TABLE person ADD FULLTEXT(firstname, lastname,email,ref);