LEFT JOIN 查询的执行时间太长

Too long time execution for LEFT JOIN query

我有 2 table 人要加入: "product"table,这个table包含257613行 这是结构:

id int(11) primary key autoincrement
id_category varchar(100)
name_category varchar(500)
name varchar(1000)
name_translated varchar(1000)
reference varchar(100)
link varchar(1000)
original_price varchar(45)
resell_price varchar(45)
active int(11)
ean varchar(16)
json_detail text
date_add date
date_update date

"stores_product"table,这个table包含181142行 这是结构:

id int(11) primary key autoincrement
reference varchar(128)
id_product int(11)
id_image_product int(11)
id_stock_product
id_store int(11)

这是有罪的慢查询:

SELECT * FROM product AS p 
LEFT JOIN stores_product AS sp ON p.reference = sp.reference 
WHERE sp.id_store = 3

此查询未得到回复,我已在 35 分钟后阻止执行但没有结果。 要处理的行太多?或者我在查询中出错了?

好吧,因为您将 257613 行与 181142 行连接起来,所以只需要时间。 查询没问题,除了升级你,恐怕你真的无法提高性能 mysql-server。 35 分钟似乎非常长,即使对于那么多的数据也是如此。

您可能还不想添加主键、索引和缓存:

关于这个有几点需要注意:

  1. 当您在联接字段上有 non-null 条件时(在您的情况下为 sp.id_store = 3),进行外部联接没有任何好处。由于外部连接比内部连接成本更高,因此在这种情况下使用后者:inner join。结果是一样的,但可能更快。

  2. 如果另一方面您希望通过外部连接列出 所有 产品,那么您的查询是不正确的。然后,您必须将条件从 where 子句移到 on 子句中,如下所示:

    LEFT JOIN stores_product AS sp
           ON p.reference = sp.reference 
          AND sp.id_store = 3
    
  3. 连接条件与预期不符。通常,您会期望 sp.id_product = p.id。但是在评论中你解释这两个领域是不相关的。这是一种非常混乱的命名方式。您应该考虑在 product table 中存储引用主键的外键。

  4. 根据您的数据分布方式,您将从以下两个索引之一中获益——您需要创建它们:stores_product(id_store,参考)stores_product(参考,id_store)

  5. 显然product(id)应该是主键。

创建缺少的索引,并用explain select ...查看执行计划,看看实际使用了哪些。

索引是您的性能救星;使用它们。

SELECT * FROM product AS p 
LEFT JOIN stores_product AS sp
   ON p.reference = sp.reference   -- `p` needs INDEX(reference)
WHERE sp.id_store = 3  -- Needs  INDEX(id_store)

但还有更多...

你对reference的定义不一致;解决这个问题。

删除LEFT;你并不是真的在做 LEFT JOIN,因为你指定的是 sp.id_store。如果从那里开始,查询将 运行 更快。

SELECT * 正在从两个表中获取所有列;这似乎有点矫枉过正。

重新考虑您的许多 id 专栏。似乎比你需要的更多。

如果 reference 确实是 product 的唯一标识符,则将其设为 PRIMARY KEY 并删除 id

How to make good indexes

知道索引可能会导致行更新或插入 table 时出现问题。我建议使用 temp tables。到目前为止,它们是(据我所知)在不更改数据库配置中的任何内容的情况下降低时间成本的最佳方法。最后降低温度 table 始终是更好的做法。

所以对于上面的问题。它可能是一个包含以下内容的存储过程:

select reference, (the columns you need or just *) 
INTO #TempTable
from stores_product sp 
where sp.id_store = 3 

Select * 
from product AS p
left join #TempTable sp ON p.reference = sp.reference 

Drop table #TempTable 

参考MySQL JOINS,

JOINS时,确保

  1. CHARSET(latin1, utf8) 个表相同。 IE。正在连接的表应该具有相同的 CHARSET 类型。这可以通过 SHOW CREATE TABLE <table_name>
  2. 检查
  3. 连接列(用于ON条件)的数据类型(varchar, int..)也相同。
  4. 连接列的数据类型大小也相同,例如。 varchar(50) varchar(50) 将比 varchar(50)varchar(100)
  5. 加入得更快
  6. 更不用说,JOINING 列必须被索引。

注意:如果连接或任何查询都需要时间,请始终使用 EXPLAIN 检查正在使用的索引和不同的统计信息以了解执行计划。