LEFT JOIN查询

时间:2017-01-14 14:12:46

标签: mysql performance left-join execution

我有2张表加入: "产品"表,此表包含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"表,此表包含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分钟后阻止执行而没有结果。 要处理的行太多?或者我在查询中出错?

5 个答案:

答案 0 :(得分:1)

有几点需要注意:

  1. 当您在连接字段(在您的情况下为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表中存储引用主键的外键。

  4. 根据数据的分布方式,您将从以下两个索引之一中获益 - 您需要创建这些索引: stores_product(id_store,reference) stores_product(reference,id_store)

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

  6. 创建缺失的索引,并使用explain select ...查看执行计划,看看实际使用的是什么。

答案 1 :(得分:0)

好吧,既然你加入257613行和181142行,那只需要时间。 查询很好,我担心除了升级mysql-server之外你无法真正提高性能。 尽管数据量很大,35分钟似乎非常长。

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

答案 2 :(得分:0)

索引是你的表现救星;使用它们。

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

答案 3 :(得分:0)

知道索引可能会导致行更新或在表中插入时出现问题。我建议使用临时表。到目前为止(据我所知),它们是减少计时成本而不更改数据库配置中任何内容的最佳方法。在最后删除临时表始终是一种更好的做法。

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

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 

答案 4 :(得分:0)

关于MySQL JOINS,

编写JOINS时,请确保

    表的
  1. CHARSET(latin1, utf8)是相同的。即。被联接的表应具有相同的CHARSET类型。可以通过SHOW CREATE TABLE <table_name>
  2. 进行检查
  3. 联接列的数据类型(varchar, int..)(在ON条件下使用)。
  4. 连接列的数据类型的大小也相同,例如。 varchar(50) varchar(50)的加入速度将比varchar(50)varchar(100)
  5. 更不用说,必须对JOINING列建立索引。

注意:如果Joins或按要求进行的任何查询花费时间,请始终使用EXPLAIN检查正在使用的索引以及不同的统计信息以了解执行计划。