按优化排序复杂选择查询

Posted

技术标签:

【中文标题】按优化排序复杂选择查询【英文标题】:Order By Optimization Complex Select Query 【发布时间】:2018-04-16 12:27:57 【问题描述】:

以下是我的查询参考 URL,执行时间为 74.086 秒,

my complex sql query

和解释语句参考网址

explain statement

但是一旦我删除了这个订单条款

 ORDER BY Field( countries.id, 231 ) DESC  

它在 6.981 秒内执行。

我已在此查询中使用的所有列上使用了索引。

我想在此查询中需要优化的地方获得指导/建议

【问题讨论】:

哇,这是一个很好的查询。 尝试将依赖的子查询移动到外部选择,并将顺序和限制放在内部选择中 @Vatev 你能解释一下吗,谢谢 total_fan_count 在什么表??? (这有很大的不同。) 【参考方案1】:

让我们只检查查询的一个方面:

SELECT total_fan_count  
  FROM users                                     social_users
  JOIN entities_users                            seu 
    ON seu.user_id = social_users.id
  JOIN entities                                  social_influencers 
    ON social_influencers.id = seu.entity_id
  LEFT 
  JOIN influencers_socialstats                   sss 
    ON sss.influencer_user_id = social_users.id
  LEFT 
  JOIN setup_socialmedia_channels                ssc 
    ON ssc.id = sss.setup_socialmedia_channel_id 
 WHERE social_influencers.id = influencers.id 
   AND ssc.code_name = 'facebook' 
 GROUP 
    BY influencers.id 

一些观察:

    这是一个相关子查询。将其重写为不相关的子查询可能有助于提高性能。

    您有一个 GROUP BY 子句,但没有聚合函数。在这种情况下,这可能无关紧要,因为您只选择一列 - 但非常奇怪的是 GROUP BY 中的列集与 SELECT 中的未聚合列集不同。

    您在 LEFT JOINed 表 (scc) 上有一个 WHERE 子句。这是矛盾的。将条件移至 ON 子句,或切换至 INNER JOIN。

我只检查了这一个很小的部分,但如果您的查询中存在大量此类错误(2 和 3),那么我不希望它实际返回有效结果 - 快速或其他方式。我建议您重新开始,逐步构建查询,并观察性能是否受到影响。

【讨论】:

【参考方案2】:

几个建议:

    有一些多余的 LEFT JOIN 可以删除(例如,setup_influencer_types)。删除它们可以显着提高性能(更少的连接 = 更少的数据库工作)。 在限制之后(而不是之前)在 SELECT 子句中执行查询会很有帮助,因为这些查询在每个选择行执行一次。下面的优化查询包含该修改。 某些连接可以是 INNER JOINed 而不是 LEFT JOINed(因为对它们进行了过滤,所以没有必要左连接它们)。 INNER JOINing 的优点是您为优化器提供了更多选项来选择执行顺序。在大多数情况下,您最终会得到更好的执行计划。 阅读here,了解如何更好地优化分页的 LIMIT 和 OFFSET 子句。

所以总结一下,首先添加这些索引来优化这个查询:

ALTER TABLE `campaigns_offered_influencers` ADD INDEX `campaigns_offered_in_idx_status_id` (`campaign_offered_status`,`user_id`);
ALTER TABLE `entities` ADD INDEX `entities_idx_type_statu_id_id_id_id` (`entity_type`,`row_status`,`id`,`rating_id`,`country_id`,`states_id`);
ALTER TABLE `entities` ADD INDEX `entities_idx_id` (`id`);
ALTER TABLE `entities_users` ADD INDEX `entities_users_idx_id_id` (`user_id`,`entity_id`);
ALTER TABLE `entities_users` ADD INDEX `entities_users_idx_id` (`id`);
ALTER TABLE `entities_users_roles` ADD INDEX `entities_users_roles_idx_id_id` (`user_id`,`role_id`);
ALTER TABLE `favourite_influencers` ADD INDEX `favourite_influencer_idx_id_status` (`entity_id`,`row_status`);
ALTER TABLE `file_attachments` ADD INDEX `file_attachments_idx_id` (`id`);
ALTER TABLE `influencer_interests` ADD INDEX `influencer_interests_idx_id_id` (`entity_id`,`interest_groups_id`);
ALTER TABLE `influencers_audience_location` ADD INDEX `influencers_audience_idx_id_id_id_id` (`user_id`,`country_id`,`state_id`,`city_id`);
ALTER TABLE `influencers_socialstats` ADD INDEX `influencers_socialst_idx_id_id_id` (`influencer_user_id`,`setup_socialmedia_channel_id`,`setup_social_engagement_rate_id`);
ALTER TABLE `setup_cities` ADD INDEX `setup_cities_idx_id_name` (`id`,`city_name`);
ALTER TABLE `setup_countries` ADD INDEX `setup_countries_idx_id_name_2_3` (`id`,`country_name`,`iso_code_2`,`iso_code_3`);
ALTER TABLE `setup_interest_groups` ADD INDEX `setup_interest_group_idx_id_id_name` (`id`,`parent_id`,`interest_name`);
ALTER TABLE `setup_rating_list` ADD INDEX `setup_rating_list_idx_id_value` (`id`,`display_value`);
ALTER TABLE `setup_roles` ADD INDEX `setup_roles_idx_id_name_name` (`id`,`code_name`,`role_name`);
ALTER TABLE `setup_social_engagement_levels` ADD INDEX `setup_social_engagem_idx_id` (`id`);
ALTER TABLE `setup_socialmedia_channels` ADD INDEX `setup_socialmedia_ch_idx_id` (`id`);
ALTER TABLE `setup_states` ADD INDEX `setup_states_idx_id_name` (`id`,`state_name`);
ALTER TABLE `users` ADD INDEX `users_idx_status_verified_id` (`row_status`,`account_verified`,`id`);
ALTER TABLE `users` ADD INDEX `users_idx_id` (`id`);

然后,尝试运行 this optimized query(我使用了 pastebin,因为查询对于 *** 的限制来说太长了)。

P.S,我使用EverSQL 来优化此查询(针对索引和查询建议)。免责声明:我是 EverSQL 的联合创始人。

【讨论】:

【参考方案3】:

mysql 正在为每一行执行相关的子查询,但您只需要前 25 个。如果订单不依赖于那些(就是这种情况),您可以通过将订单/限制移动到子查询来避免这种情况:

SELECT 
    <dependent subquery1> as campaigns_completed,
    <dependent subquery2> as total_fan_count,
    ...
    t.*
FROM (
    <the rest of your query>
    ORDER BY
        Field( countries.id, 231 ) DESC 
    LIMIT 25 OFFSET 0
) t

【讨论】:

【参考方案4】:

单次通过即可获得粉丝数

所有(?)这些相关的子查询都可以滚动到

CREATE TEMPORARY TABLE tfc
    ( PRIMARY KEY code_name )
    SELECT  ssc.code_name, total_fan_count
        FROM  users AS social_users
        INNER JOIN  entities_users AS seu
               ON seu.user_id = social_users.id
        GROUP BY  seu.entity_id

通过一次传递,这可能会比所有那些相关的子查询快得多。

然后使用更简单的查询来获取每列的 total_fan_count。

将旋转视为一个单独的步骤

查看此问题的另一种方法是分两步执行该过程;第二个是“枢轴”。

先做LIMIT

另一个技巧是从找到您需要的 25 个 ID 开始。也就是说,编写最小 量的SQL 以获得LIMIT 实现。然后JOIN 其余的东西。这可能只允许 25 次查找等。这可能会绕过您提到的 74 秒与 6 秒时序。它也可能因为其他原因而缩短时间。

不要过度标准化

将“位置”分布在多个表(城市 + 州 + 国家/地区)而不是单个位置表会导致 SELECT 中的大量额外工作,在空间或其他方面几乎没有或没有好处。 (城市名称多久更改一次?Truth or Consequences,NM,尽管如此。)

小心充气放气

JOIN 增加了正在处理的行数,只是为了让您执行GROUP BY 将其压缩回您需要的行数。我之前的一些 cmets 解决了这种综合症,但可能还涉及其他方面。

【讨论】:

以上是关于按优化排序复杂选择查询的主要内容,如果未能解决你的问题,请参考以下文章

排序算法系列之选择排序

JavaScript实现选择排序及其优化

排序优化

我终于弄懂选择排序(堆排序)

您如何优化这个复杂的 sql 查询,然后选择正确的表索引

常见排序之平方排序(时间复杂度)