有没有可能重写此查询以获得最佳性能的方法?

时间:2014-02-25 11:22:23

标签: mysql sql

以下查询需要经常长时间记录以减慢日志。

有没有办法重写下面的查询,我的意思是比当前查询更好。

select 
    p . *,
    pt.pretty_name,
    pt.seo_name,
    pt.description,
    pt.short_description,
    pt.short_description_2
from
    cat_products p,
    cat_product_catalog_map pcm,
    cat_current_product_prices cpp,
    cat_product_text pt
where
    pcm.catalog_id = 2
        and pcm.product_id = p.product_id
        and p.owner_catalog_id = 2
        and cpp.product_id = p.product_id
        and cpp.currency = 'GBP'
        and cpp.catalog_id = 2
        and cpp.state <> 'unavail'
        and pt.product_id = p.product_id
        and pt.language_id = 'EN'
        and p.product_id not in (select distinct
            product_id
        from
            cat_product_detail_map
        where
            short_value in ('ft_section' , 'ft_product'))
order by pt.pretty_name
limit 200 , 200;

3 个答案:

答案 0 :(得分:2)

首先,我会切换到ANSI 92显式连接语法,而不是您使用的ANSI 89隐式连接语法,顾名思义,这已经过时了20多年:

select  ...
from    cat_products p
        INNER JOIN cat_product_catalog_map pcm
            ON pcm.product_id=p.product_id  
        INNER JOIN cat_current_product_prices cpp
            ON cpp.product_id = p.product_id 
        INNER JOIN cat_product_text pt 
            ON pt.product_id=p.product_id 
WHERE ....

这不会影响性能,但会使您的查询更易读,并且不易发生意外交叉连接。 Aaron Bertrand写了一篇值得阅读的good article on the reasons to switch(它针对的是SQL Server,但许多原则都是通用的)。然后我会删除NOT IN (Subquery) MySQL不会像这样优化子查询。它会将其重写为:

AND NOT EXISTS (SELECT 1
                FROM    cat_product_detail_map 
                WHERE   short_value in ('ft_section','ft_product')
                AND     cat_product_detail_map.product_id = p.product_id
                )

然后它会为每一行执行一次该子查询。此方案的反面(文章Optimizing Subqueries with EXISTS Strategy中描述了WHERE <expression> IN (Subquery)

您可以使用LEFT JOIN/IS NULL方法排除这些product_id,这种方法在MySQL中表现更好,因为它完全避免了子查询:

SELECT  ...
FROM    cat_products p
        LEFT JOIN cat_product_detail_map exc
            ON exc.product_id = p.product_id
            AND exc.short_value in ('ft_section','ft_product')
WHERE   exc.product_id IS NULL

这样可以更好地使用索引,这意味着您不必为外部查询中的每一行执行子查询。

因此,您的完整查询将是:

SELECT  p.*,
        pt.pretty_name, 
        pt.seo_name, 
        pt.description, 
        pt.short_description, 
        pt.short_description_2 
FROM    cat_products p
        INNER JOIN cat_product_catalog_map pcm
            ON pcm.product_id = p.product_id  
        INNER JOIN cat_current_product_prices cpp
            ON cpp.product_id = p.product_id 
        INNER JOIN cat_product_text pt 
            ON pt.product_id = p.product_id 
        LEFT JOIN cat_product_detail_map exc
            ON exc.product_id = p.product_id
            AND exc.short_value in ('ft_section','ft_product')
WHERE   exc.product_id IS NULL
AND     pcm.catalog_id = 2 
AND     p.owner_catalog_id = 2  
AND     cpp.currency = 'GBP' 
AND     cpp.catalog_id = 2  
AND     cpp.state <> 'unavail'  
AND     pt.language_id = 'EN' 
ORDER BY pt.pretty_name limit 200,200;

最后要看的是你桌面上的索引,我不知道你已经拥有什么,但我建议你在每个表上的product_id索引作为最低限度,也许在列上你正在过滤。

答案 1 :(得分:0)

查询的性能取决于以下因素:

  • 指标的影响。
  • 查询本身的结构。

对于第一种情况,您说正确的列中有索引,但请确保product_id列是其中之一。

关于查询,您可以使用JOIN运算符来简化它:

select 
    p.*,
    pt.pretty_name,
    pt.seo_name,
    pt.description,
    pt.short_description,
    pt.short_description_2
from
    cat_products p
    join cat_product_catalog_map pcm on p.product_id = pcm.product_id
    join cat_current_product_prices cpp on p.product_id = cpp.product_id
    join cat_product_text pt on p.product_id = pt.product_id
    join (select distinct product_id from cat_product_detail_map
    where short_value NOT in ('ft_section' , 'ft_product') otherProdId on otherProdId.product_id = p.product_id
where
    pcm.catalog_id = 2
        and p.owner_catalog_id = 2
        and cpp.currency = 'GBP'
        and cpp.catalog_id = 2
        and cpp.state <> 'unavail'
        and pt.language_id = 'EN'
order by pt.pretty_name
limit 200,200;

答案 2 :(得分:0)

首先:您的陈述不具备应有的可读性。由于您不使用ANSI连接语法,因此可以查看表的相关性。我的第一个猜测是你将表连接到table以从cat_products链接到cat_product_text。结果证明是错误的。

您所做的只是从cat_products和cat_product_text中选择字段。那么为什么要加入其他两个表呢?您是否试图查看这些表中的记录是否存在?然后使用EXISTS子句。

当连接目录id上的表时,显示这个链接你的表的链接(pcm.catalog_id = p.owner_catalog_id),而不是看起来它们是不相关的(pcm.catalog_id = 2,p.owner_catalog_id = 2 )。

您不必将DISTINCT用于IN查询。 dbms应该自行决定是否有利于删除该集合中的重复项。

以下查询不一定会产生与原始查询相同的结果。这取决于加入表cat_product_catalog_map和cat_current_product_prices的目的。我想,这只是对存在的检查。因此查询可以重写如下。它应该更快,因为您不必连接不向结果添加字段的表。但即使这取决于桌子大小等。

SELECT 
  p.*,
  pt.pretty_name,
    pt.seo_name,
    pt.description,
    pt.short_description,
    pt.short_description_2
FROM cat_products p
JOIN cat_product_text pt ON pt.product_id = p.product_id and pt.language_id = 'EN'
WHERE p.owner_catalog_id = 2 
AND p.product_id NOT IN 
(
  SELECT product_id
  FROM cat_product_detail_map
  WHERE short_value in ('ft_section','ft_product')
)
AND EXISTS
(
  SELECT *
  FROM cat_product_catalog_map pcm 
  WHERE pcm.product_id = p.product_id
  AND pcm.catalog_id = p.owner_catalog_id
)
AND EXISTS
(
  SELECT *
  FROM cat_current_product_prices cpp 
  WHERE cpp.product_id = p.product_id 
  AND cpp.catalog_id = p.owner_catalog_id
  AND cpp.currency = 'GBP'
  AND cpp.state <> 'unavail'
)
ORDER BY pt.pretty_name LIMIT 200,200;