简体   繁体   English

查询优化(多连接)

[英]Query optimization (multiple joins)

I would like to find a way to improve a query but it seems i've done it all.我想找到一种方法来改进查询,但似乎我已经完成了这一切。 Let me give you some details.让我给你一些细节。

Below is my query :以下是我的查询:

SELECT 
    `u`.`id` AS `id`,
    `p`.`lastname` AS `lastname`,
    `p`.`firstname` AS `firstname`,
    COALESCE(`r`.`value`, 0) AS `rvalue`,
    SUM(`rat`.`category` = 'A') AS `count_a`,
    SUM(`rat`.`category` = 'B') AS `count_b`,
    SUM(`rat`.`category` = 'C') AS `count_c`
FROM
    `user` `u`
    JOIN `user_customer` `uc` ON (`u`.`id` = `uc`.`user_id`)
    JOIN `profile` `p` ON (`p`.`id` = `u`.`profile_id`)
    JOIN `ad` FORCE INDEX (fk_ad_customer_idx) ON (`uc`.`customer_id` = `ad`.`customer_id`)
    JOIN `ac` ON (`ac`.`id` = `ad`.`ac_id`)
    JOIN `a` ON (`a`.`id` = `ac`.`a_id`)
    JOIN `rat` ON (`rat`.`code` = `a`.`rat_code`)
    LEFT JOIN `r` ON (`r`.`id` = `u`.`r_id`)
GROUP BY `u`.`id`
;

Note : Some table and column names are voluntarily hidden.注意:某些表名和列名是自愿隐藏的。

Now let me give you some volumetric data :现在让我给你一些体积数据:

user => 6534 rows
user_customer => 12 923 rows
profile => 6511 rows
ad => 320 868 rows
ac => 4505 rows
a => 536 rows
rat => 6 rows
r => 3400 rows

And finally, my execution plan :最后,我的执行计划:

在此处输入图片说明

My query does currently run in around 1.3 to 1.7 seconds which is slow enough to annoy users of my application of course ... Also fyi result set is composed of 165 rows.我的查询目前运行在 1.3 到 1.7 秒左右,这当然足以惹恼我的应用程序的用户......此外,fyi 结果集由 165 行组成。

Is there a way I can improve this ?有什么办法可以改善这一点吗?

Thanks.谢谢。

EDIT 1 (answer to Rick James below) : What are the speed and EXPLAIN when you don't use FORCE INDEX?编辑 1(下面对 Rick James 的回答):不使用 FORCE INDEX 时的速度和 EXPLAIN 是多少?

Surprisingly it gets faster when i don't use FORCE INDEX.令人惊讶的是,当我不使用 FORCE INDEX 时它会变得更快。 To be honest, i don't really remember why i've done that change.老实说,我真的不记得我为什么要做出这样的改变。 I've probably found better results in terms of performance with it during one of my various tries and didn't remove it since.在我的各种尝试中,我可能在性能方面发现了更好的结果,并且此后没有将其删除。

When i don't use FORCE INDEX, it uses an other index ad_customer_ac_id_blocked_idx(customer_id, ac_id, blocked) and times are around 1.1 sec.当我不使用 FORCE INDEX 时,它使用另一个索引 ad_customer_ac_id_blocked_idx(customer_id, ac_id,blocked) 并且时间约为 1.1 秒。 I don't really get it because fk_ad_customer_idx(customer_id) is the same when we talk about index on customer_id.我真的不明白,因为 fk_ad_customer_idx(customer_id) 在我们谈论 customer_id 上的索引时是一样的。

Get rid of FORCE INDEX .摆脱FORCE INDEX Even if it helped yesterday;即使昨天有帮助; it may hurt tomorrow.明天可能会痛。

Some of these indexes may be beneficial.其中一些索引可能是有益的。 (It is hard to predict; so simply add them all.) (很难预测;所以只需将它们全部添加。)

a:  (rat_code, id)
rat:  (code, category)
ac:  (a_id, id)
ad:  (ac_id, customer_id)
ad:  (customer_id, ac_id)
uc:  (customer_id, user_id)
uc:  (user_id, customer_id)
u:  (profile_id, r_id, id)

(This assumes that id is the PRIMARY KEY of each table. Note that none have id first.) Most of the above are "covering". (这假设id是每个表的PRIMARY KEY 。请注意,没有一个首先有id 。)以上大部分都是“覆盖”。

Another approach that sometimes helps: Gather the SUMs before joining to any unnecessary table.另一种有时会有所帮助的方法:在加入任何不必要的表之前收集SUMs But is seems that p is the only table not involved in getting from u (the target of GROUP BY ) to r and rat (used in aggregates).但似乎p是唯一不涉及从uGROUP BY的目标)到rrat (用于聚合)的表。 It would look something like:它看起来像:

SELECT ..., firstname, lastname
    FROM ( everything as above except for `p` ) AS most
    JOIN `profile` `p`  ON (`p`.`id` = most.`profile_id`)
    GROUP BY most.id

This avoids hauling around firstname and lastname while doing most of the joins and the GROUP BY .这避免了在执行大部分连接和GROUP BY拖拽 firstname 和 lastname 。

When doing JOINs and GROUP BY , be sure to sanity check the aggregates.在执行JOINsGROUP BY ,一定要对聚合进行完整性检查。 Your COUNTs and SUMs may be larger than they should be.您的COUNTsSUMs可能比应有的大。

First, you don't need to tick .首先,您不需要tick everyTableAndColumn in your queries, nor result columns, aliases, etc. The tick marks are used primarily when you are in conflict with a reserved work so the parser knows you are referring to a specific column... like having a table with a COLUMN named "JOIN", but JOIN is part of SQL command... see the confusion it would cause.查询中的everyTableAndColumn ,也不是结果列、别名等。 tick线主要在您与保留工作发生冲突时使用,因此解析器知道您指的是特定列......就像有一个名为 COLUMN 的表“JOIN”,但 JOIN 是 SQL 命令的一部分......看看它会引起的混乱。 Helps clean readability too.也有助于清洁可读性。

Next, and this is just personal preference and can help you and others following behind you on data and their relationships.其次,这只是个人偏好,可以帮助您和其他人关注数据及其关系。 I show the join as indented from where it is coming from.我将连接显示为从它来自的地方缩进。 As you can see below, I see the chain on how do I get from the User (u alias) to the rat alias table... You get there only by going 5 levels deep, and I put the first table on the left-side of the join (coming from table) then = the table joining TO right-side of join.正如你在下面看到的,我看到了如何从用户(你的别名)到老鼠别名表的链......你只需要深入 5 层就可以到达那里,我把第一个表放在左边 -连接的一侧(来自表)然后 = 连接到连接右侧的表。

Now, that I can see the relationships, I would suggest the following.现在,我可以看到这些关系,我建议如下。 Make COVERING indexes on your tables that have the criteria, and id/value where appropriate.在具有条件的表上创建 COVERING 索引,并在适当的情况下使用 id/value。 This way the query gets as best it needs, the data from the index page vs having to go to the raw data.通过这种方式,查询得到它需要的最佳状态,来自索引页的数据与必须转到原始数据的数据。 So here are suggestions for indexes.所以这里是索引的建议。

table             index
user_customer     ( user_id, customer_id )   -- dont know what your fk_ad_customer_idx parts are)
ad                ( customer_id, ac_id )
ac                ( id, a_id )
a                 (id, rat_code )
rat               ( code, category )

Reformatted query for readability and seeing relationships between the tables重新格式化查询以提高可读性并查看表之间的关系

SELECT 
        u.id,
        p.lastname,
        p.firstname,
        COALESCE(r.value, 0) AS rvalue,
        SUM(rat.category = 'A') AS count_a,
        SUM(rat.category = 'B') AS count_b,
        SUM(rat.category = 'C') AS count_c
    FROM
        user u
            JOIN user_customer uc
                ON u.id = uc.user_id
                JOIN ad FORCE INDEX (fk_ad_customer_idx) 
                    ON uc.customer_id = ad.customer_id
                    JOIN ac 
                        ON ad.ac_id = ac.id
                        JOIN a 
                            ON ac.a_id = a.id
                            JOIN rat 
                                ON a.rat_code = rat.code
            JOIN profile p
                ON u.profile_id = p.id
            LEFT JOIN r
                ON u.r_id = r.id
    GROUP BY 
        u.id

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM