使用MySQL中的SELECT更快地搜索IN语句

时间:2014-03-03 14:14:14

标签: php mysql sql

我正在对我的应用程序进行一些查询,我需要在我当前的位置上获取最近的商店并首先执行此操作我需要获取所有具有相同名称的项目然后获取它的信息并减少该信息查询。现在我使用IN语句,但由于搜索的项目也是基于列表我需要使用另一个选择这里是我的代码到目前为止:

select *
from product p,
store s,
branches b
where 1 = 1
and b.idproduct = p.idproduct
and p.store = s.idstore
and common_name IN(SELECT p.common_name
FROM shopping_list_content s, product p
WHERE 1 =1
AND s.iditem = p.idproduct
AND s.idlist =$listid)

现在它可以正常运行,但我希望它能比这更快地进行查询。目前,此查询运行速度超过3秒需要3秒以上。如果它不到一秒钟就好多了。我可以使用其他任何选项吗?

2 个答案:

答案 0 :(得分:2)

当您编写类似以下内容时,MySQL难以优化子查询:

SELECT  *
FROM    T
WHERE   T.ID (SELECT ID FROM T2);

有时会改写为

SELECT  *
FROM    T
WHERE   EXISTS
        (   SELECT  1
            FROM    T2
            WHERE   T.ID = T2.ID
        );

然后在T中每行执行一次子查询,而如果你写:

SELECT  T.*
FROM    T
        INNER JOIN
        (   SELECT  DISTINCT ID
            FROM    T2
        ) T2
            ON T2.ID = T.ID;

您的结果集将是相同的,但MySQL将首先使用子查询的结果填充内存表并将其散列在T2.ID上,然后只需要针对{{中的每一行的哈希表查找1}}。

您想要的行为实际上取决于您希望从每个表/子查询中获得多少数据。如果T中有1百万行,T2中有10行,那么填充100万行的临时表是没有意义的,只能随后只使用10次,而如果你有T中的大量行和T中的少量行实现子查询的额外成本从长远来看将是有益的。

要指出的另一件事(对性能没有影响),您使用的JOIN语法是ANSI 89语法,并且在20多年前被ANSI 92显式JOIN语法取代。虽然针对SQL Server,但我认为this article总结了很好地切换到较新的连接语法的原因。进行最终查询:

T2

N.B。如果您使用的是MySQL 5.6.5或更高版本,则上述大部分内容都不适用。在这个版本中,他们引入了更多Subquery Optimization来解决上述许多问题

答案 1 :(得分:0)

这是您的查询修复使用正确的join语法:

select *
from product p join
     store s
     on p.store = s.idstore join
     branches b
     on b.idproduct = p.idproduct
where p.common_name IN (SELECT p.common_name
                        FROM shopping_list_content slc join
                             product p
                             ON slc.iditem = p.idproduct AND
                                slc.idlist = $listid
                       );

假设同一个common_name未出现在多个产品上,shopping_list_content没有重复的行,您可以将其替换为简单的join

select *
from product p join
     store s
     on p.store = s.idstore join
     branches b
     on b.idproduct = p.idproduct join
     shopping_list_content slc
     on slc.iditem = p.idproduct and
        slc.idlist = $listid;

然而,这些假设可能并非如此。在这种情况下,将子查询更改为使用exists可能有助于提高性能:

select *
from product p join
     store s
     on p.store = s.idstore join
     branches b
     on b.idproduct = p.idproduct
where exists (SELECT 1
              FROM shopping_list_content slc join
                   product p2
                   on slc.iditem = p2.idproduct AND
                      slc.idlist = $listid
              WHERE p.common_name = p2.common_name
             );

对于后一个查询,product(common_name, idproduct)上的索引以及shopping_list_content(iditem, idlist)应该会有所帮助。