以下查询需要经常长时间记录以减慢日志。
有没有办法重写下面的查询,我的意思是比当前查询更好。
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;
答案 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;