如何使用许多JOIN提高查询性能

时间:2014-01-03 18:28:57

标签: mysql sql performance select query-optimization

我有一个查询(目的是创建一个视图),它使用一些连接来获取每一列。对于添加的每组连接,性能会快速降低(指数级?)。

使这个查询更快的好方法是什么?请查看查询中的评论。

如果有帮助,这是使用WordPress数据库架构。

以下是EXPLAIN的屏幕截图 enter image description here

产品表

+--+----+
|id|name|
+--+----+
|1 |test|
+--+----+

METADATA TABLE

+----------+--------+-----+
|product_id|meta_key|value|
+----------+--------+-----+
|1         |price   |9.99 |
+----------+--------+-----+
|1         |sku     |ABC  |
+----------+--------+-----+

TERM_RELATIONSHIPS表

+---------+----------------+
|object_id|term_taxonomy_id|
+---------+----------------+
|1        |1               |
+---------+----------------+
|1        |2               |
+---------+----------------+

TERM_TAXONOMY TABLE

+----------------+-------+--------+
|term_taxonomy_id|term_id|taxonomy|
+----------------+-------+--------+
|1               |1      |size    |
+----------------+-------+--------+
|2               |2      |stock   |
+----------------+-------+--------+

TERMS表

+-------+-----+
|term_id|name |
+-------+-----+
|1      |500mg|
+-------+-----+
|2      |10   |
+-------+-----+

查询

SELECT 
  products.id,
  products.name,
  price.value AS price,
  sku.value AS sku,
  size.name AS size
FROM products

/* These joins are performing quickly */

INNER JOIN `metadata` AS price ON products.id = price.product_id AND price.meta_key = 'price'
INNER JOIN `metadata` AS sku ON products.id = sku.product_id AND sku.meta_key = 'sku'

/* Here's the part that is really slowing it down - I run this chunk about 5 times with different strings to match */

INNER JOIN `term_relationships` AS tr ON products.id = tr.object_id
  INNER JOIN `term_taxonomy` AS tt
  ON tr.term_taxonomy_id = tt.term_taxonomy_id AND tt.taxonomy = 'size'
    INNER JOIN `terms` AS size
    ON tt.term_id = size.term_id

7 个答案:

答案 0 :(得分:12)

您的效果问题很可能是因为与“term_taxonomy”'表。
所有其他连接似乎都使用主键(你可能在其中有工作索引)。

所以我的建议是在 term_taxonomy_id term_id 上添加复合索引(或者如果必须:分类) 。像这样:

CREATE UNIQUE INDEX idx_term_taxonomy_id_taxonomy
ON term_taxonomy( term_taxonomy_id, taxonomy);

希望这会对你有所帮助。

答案 1 :(得分:2)

确保所有存在“ON”条件语句的列都应该被编入索引。 这将显着提高速度。

答案 2 :(得分:0)

试试这个:

SELECT p.id, p.name, MAX(CASE m.meta_key WHEN 'price' THEN m.value ELSE '' END) AS price, 
       MAX(CASE m.meta_key WHEN 'sku' THEN m.value ELSE '' END) AS sku, s.name AS size
FROM products p 
INNER JOIN `metadata` AS m ON p.id = m.product_id  
INNER JOIN `term_relationships` AS tr ON p.id = tr.object_id 
INNER JOIN `term_taxonomy` AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id AND tt.taxonomy = 'size'
INNER JOIN `terms` AS s ON tt.term_id = s.term_id
GROUP BY p.id;

如果您仍然发现查询速度很慢,请添加我的查询的EXPLAIN计划,以便找到需要INDEX的列。

答案 3 :(得分:0)

    Declare @query as NVARCHAR(MAX)
    set @query = ('SELECT 
    products.id,
    products.name,
    price.value AS price,
    sku.value AS sku,
    size.name AS size
    FROM products
    INNER JOIN metadata AS price ON products.id = price.product_id AND price.meta_key = price
    INNER JOIN metadata AS sku ON products.id = sku.product_id AND sku.meta_key = sku
    INNER JOIN term_relationships AS tr ON products.id = tr.object_id
    INNER JOIN term_taxonomy AS tt
    ON tr.term_taxonomy_id = tt.term_taxonomy_id AND tt.taxonomy = size
    INNER JOIN terms AS size
    ON tt.term_id = size.term_id
    into #t')

    exec(@query);
    select * from #t

我希望以上方式可以减少时间利用率,或者创建一个包含您选择的所有字段的临时表,并通过将临时表连接到所有其他表来更新临时表也可能是有效的,我不是确定它,但即使我在等你的结果,因为你的问题似乎很有意思

答案 4 :(得分:0)

以下脚本按照SQL Server规则进行格式化 - 您可以根据MySQL规则对其进行更改并尝试一下 -

SELECT 
  P.id,
  P.name,
  PIVOT_METADATA.price,
  PIVOT_METADATA.sku,
  size.name AS size
FROM products P (NOLOCK)

INNER JOIN term_relationships AS tr (NOLOCK)
    ON P.id = tr.object_id

INNER JOIN term_taxonomy AS tt (NOLOCK)
    ON tr.term_taxonomy_id = tt.term_taxonomy_id AND tt.taxonomy = 'size'

INNER JOIN terms AS size (NOLOCK)
    ON tt.term_id = size.term_id

INNER JOIN METADATA (NOLOCK)
    PIVOT
    (
        MAX(value)
        FOR [meta_key] IN (price,sku)
    )AS PIVOT_METADATA
    ON P.id = PIVOT_METADATA.product_id

我认为您的查询可能是瓶颈 - 您将加入元数据2次。由于表中存在1对多的关系,因此元数据2连接不会受到影响,但在此之后,当您加入更多表时 - 由于1对多关系的行数增加 - 因此性能下降

我试图实现的目标 - 我确保尽可能多地实现1对1关系。要做到这一点,我已经完成了元数据上的数据转换和价格。 sku作为列。现在我的产品ID在Metadata pivot中只有一行。 alos,我确保在最后加入这个picot。

试一试。请分享您的预期表现,记录数量和我的asnwer也能带来什么样的表现。

答案 5 :(得分:0)

METADATA_TABLE和TERM_RELATIONSHIP_TABLE没有任何合法密钥。当这些表中有大量记录时,您的查询性能将会受到影响。

检查点以提高您的表现。

  1. 所有表都应该有主键。这是因为表中的行将进行物理排序。
  2. 对于涉及少数表的小型和查询,将主键保存在表中就足够了。 如果您仍希望提高性能,请为诸如term_relationships表*的* object_Id字段之类的列创建非聚集索引。应为表中参与连接操作的列创建非聚集索引。
  3. 但是,需要注意的是,非聚集索引在那些发生多次插入和更新的表上应该非常少。 这不是一个简单的问题,只能根据运行时间来回答。还有其他因素会影响答案,尤其是在运行存储过程的环境具有大量事务性的情况下。

    您可以找到更多here

答案 6 :(得分:0)

我会建议那些:

  • 考虑从业务级别减少这些联接;
  • 如果不能从“top”(业务级别)做,并且数据不是实时的,我建议准备一个内存表(我知道解决方案并不理想)。并直接从内存表中选择数据。

根据我的经验:

  • “加入”是表现的杀手,你的数据越大,你会感受到越多的痛苦;
  • 尝试删除联接,不要尝试通过保持联接来提高查询性能,除非必须这样做。通常我会尝试将这些问题从“顶部”修复为“底部”
  • 最后一个建议是如果以上都不起作用。如果值得做的话,我会考虑“map / reduce + fulltext search”。

(原谅我,我没有提供提高查询性能的解决方案。)