Mysql Join Query需要很长时间才能执行

Posted

技术标签:

【中文标题】Mysql Join Query需要很长时间才能执行【英文标题】:Mysql Join Query taking a long time to execute 【发布时间】:2018-08-06 12:08:56 【问题描述】:

我有一个查询需要很长时间才能执行。 表说明。这些表非常大,因此将在描述中给出相关列。所有列都是 varchar。

表 1 - 常规 PK - CLAIM_ID 记录数 - 2.63 Mill,

表 2 - 注册 记录数 - 250 万 列 - CLAIM_ID(PK),POLICY_ID,MEMBER_ID

表 3 - 成员 没有记录 - 2800 万 列 - MEMBER_ID(PK),POLICY_GROUP_ID

表 4 - 政策 没有记录 - 200 万 Cols- POLICY_ID,policy_sub_general_type_id

表 5 - 余额 记录数量 - 1200 万。 列

查询是

SELECT  cg.CLAIM_ID,mem.Policy_group_ID ,
CAST(CASE when pol.policy_sub_general_type_id = 'PFL'
               then (bal2.sum_insured - bal2.utilised_sum_insured)
          when pol.policy_sub_general_type_id = 'PNF'
               then (bal1.sum_insured - bal1.utilised_sum_insured)
     end AS DECIMAL(10, 2) ) Balance_SI
    FROM  General cg
    LEFT JOIN  Enrol ce  ON cg.CLAIM_ID = ce.CLAIM_ID
    LEFT JOIN  Member mem  ON ce.MEMBER_ID = mem.MEMBER_ID
    LEFT JOIN  Policy pol  ON pol.POLICY_ID = ce.POLICY_ID
    LEFT join  Balance bal1  ON bal1.MEMBER_ID = ce.MEMBER_ID
                           and  bal1.MEMBER_ID is not null
    LEFT join  Balance bal2  ON bal2.Policy_group_ID = mem.Policy_group_ID
                           and  bal2.Policy_group_ID is not null
    GROUP BY  cg.CLAIM_ID

解释声明显示

Select Type|table|Type|key|rows|Extra
_____________________________________
SIMPLE|cg |index|PRIMARY|2662233|Using Index
SIMPLE|ce |ref|index1|1|NULL
SIMPLE|mem|eq_ref|PRIMARY|1|using where
SIMPLE|pol|eq_ref|PRIMARY|1| Using Where
SIMPLE|bal1|ref|index2|3|Using Where
SIMPLE|bal2|ref|index1|1|using where

服务器参数

InnoDB_Buffer_pool - 10GB InnoDB_Log_File_Size - 3GB 4核处理器

所有表和列都有相同的排序规则和字符集,所以这不是排序规则问题。连接列也是 varchar。解释语句显示(我假设)表的索引很好。 查询需要大约 15 分钟才能返回前 50000 行,这在此时是不可接受的。对于整个表,它仍然运行了最后 3 个小时而没有任何结果。 不知道为什么会这样。请帮忙。

【问题讨论】:

我在索引和加入 varchar 数据类型方面的经验非常糟糕。我使用整数 id(s) 加入,如果需要,从 WHERE 部分中的两个表中匹配 varchar id(s) 如果没有针对所有相关表的 SHOW CREATE TABLE 语句,实在是帮不上忙。 你真的需要 2662233 行的结果吗? 您正在按一列聚合,但选择了许多其他列,没有聚合函数。这没有意义。 这是一个简单的请求:-(。见meta.***.com/questions/333952/… 【参考方案1】:

对于初学者,您可以完全删除您的“cg”别名 General 表,除非您将其用于未在此处显示的其他列。原因是,您直接从您的注册表中获得了索赔 ID。只是删除额外的水平。

接下来,您的 Group by 仅在声明中,但策略组 ID 是您选择的一部分。您是否也打算按照政策对其进行汇总?一项索赔可以被多个保单组覆盖吗?如果没有,而您只是想继续前进,您可以通过 MAX(mem.Policy_Group_ID) 作为 Policy_Group_ID

正如 Strawberry 所指出的,按照您可能得到笛卡尔结果的位置进行聚合/分组可能会给您错误的答案。

我还建议编辑您的帖子并确认一些其他详细信息,例如余额表。您有一个基于“PFL”的“PNF”总数,我们知道它们背后有特定的含义,但对我们没有任何意义。您的案例/何时从“Bal1”与“Bal2”别名中提取值的原因。这是特定策略组未输入余额表并且属于某个“通用存储桶”或特定于单个策略的存储桶的情况吗?比如常规覆盖“X”,但你对“Y”类有限制?

以下是删除通用表后更清晰的 SQL 可读性。

SELECT  
      ce.CLAIM_ID,
      mem.Policy_group_ID,
      CAST(CASE when pol.policy_sub_general_type_id = 'PFL' 
                   then (bal2.sum_insured - bal2.utilised_sum_insured)
                when pol.policy_sub_general_type_id = 'PNF' 
                   then (bal1.sum_insured - bal1.utilised_sum_insured) end AS DECIMAL(10,2)) Balance_SI
   FROM 
      Enrol ce
         LEFT JOIN Member mem
            on ce.MEMBER_ID = mem.MEMBER_ID
         LEFT join Balance bal2
            on mem.Policy_group_ID = bal2.Policy_group_ID
            and bal2.Policy_group_ID  <> ''
         LEFT JOIN Policy pol
            on ce.POLICY_ID = pol.POLICY_ID
         LEFT join Balance bal1
            on ce.MEMBER_ID = bal1.MEMBER_ID
            and bal1.MEMBER_ID <> ''
   GROUP BY 
      ce.CLAIM_ID

最后,查看您的案例/何时加入 Bal2 别名,您没有参考成员 ID,因此让我们向您展示您可能遇到的笛卡尔杀手。例如,联邦雇员属于一个政策组,拥有 20,000 名雇员。现在您有一个注册记录左连接到余额表?是每个策略组一个记录还是每个成员/策略组一个记录。如果每个成员/政策,您每次尝试从 Bal2 获取价值时都要翻阅 20k 余额记录。而余额表“Bal1”别名对于每个成员 ID 都是明确的。所以我知道这两个字段都在表中,这可能会害死你。

再次,请编辑您现有的帖子以澄清细节和关系,尤其是 1:1 与 1:n

【讨论】:

添加了更多关于表格和查询目的的解释。请看看现在是否更有帮助【参考方案2】:

这还不是答案

我不清楚您的数据库架构。

我有很多问题和很多想法如何加快这个查询。

让我们看一下您的第一部分查询:

SELECT  cg.CLAIM_ID,
    mem.Policy_group_ID,
    CAST(
      CASE 
        when 
pol.policy_sub_general_type_id = 'PFL' then
(bal2.sum_insured - bal2.utilised_sum_insured)
            when pol.policy_sub_general_type_id = 'PNF' then
(bal1.sum_insured - bal1.utilised_sum_insured)
          END
        AS DECIMAL(10,2)
        ) Balance_SI

您有“内联”函数调用,这会影响性能:CAST, CASE, bal1.sum_insured - bal1.utilised_sum_insured, bal2.sum_insured - bal2.utilised_sum_insured

如果您的应用程序或您所做的任何事情都可以接受查询返回的非“格式化”结果,我建议删除CAST - 它会加快查询速度,而不会影响返回的实际值。您可以稍后在应用程序级别对这些值进行四舍五入。

接下来是CASE,如果你有你的应用级别(我希望)你可以返回原始数据而不是转换结果。我的意思是您可以返回 3 列:pol.policy_sub_general_type_id, bal1.sum_insured - bal1.utilised_sum_insured, bal2.sum_insured - bal2.utilised_sum_insured 而不是 CASE。但我怀疑你甚至不需要这种优化。我稍后会展示。

我也对您的JOINs 有很多疑问。但是由于您还没有回复 DRapp 的答案,所以我会暂时保留我的问题。

让我们直接进入查询,我怀疑它会返回您需要的几乎相同的数据,如果您有任何特定问题,稍后再讨论详细信息。

SELECT
    cg.CLAIM_ID,
    mem.Policy_group_ID ,
    SUM(bal.sum_insured - bal.utilised_sum_insured) Balance_SI
FROM `General` cg
LEFT JOIN Enrol ce
ON cg.CLAIM_ID = ce.CLAIM_ID
LEFT JOIN Member mem
ON ce.MEMBER_ID = mem.MEMBER_ID
LEFT JOIN Policy pol
ON pol.POLICY_ID = ce.POLICY_ID
  AND (pol.policy_sub_general_type_id = 'PNF' 
       OR pol.policy_sub_general_type_id = 'PFL')
LEFT JOIN Balance bal
ON (bal.MEMBER_ID = ce.MEMBER_ID
   AND bal.MEMBER_ID <> '')
   OR (bal.Policy_group_ID = mem.Policy_group_ID 
   AND bal.Policy_group_ID  <> '')
GROUP BY cg.CLAIM_ID, mem.Policy_group_ID

【讨论】:

不错的报价,但是如果您查看原始查询,他对 Balance 表的连接是基于两个独立的,因为您应用了“OR”。问题是他们真正试图汇总什么并相信他正在陷入笛卡尔结果。 @DRapp 让我们等待 OP cmets,我觉得他不需要 2 个加入。 @Alex 添加了更多关于表格和查询目的的解释。请看看现在是否更有帮助 @AngryLeo 你检查我的查询了吗?它返回的结果是否与您的不同?有什么不同?为什么它是错误的(如果是的话)? OR 可能会抹杀任何使用JOINing 索引的机会。

以上是关于Mysql Join Query需要很长时间才能执行的主要内容,如果未能解决你的问题,请参考以下文章

简单的选择需要很长时间才能执行

如果使用 ORDER BY String Column,MySQL 查询需要很长时间才能执行

在 MYSQL 表上选择查询需要很长时间并超时

在 MySQL 中重置 AUTO_INCREMENT 需要很长时间

在 MySQL 中重置 AUTO_INCREMENT 需要很长时间

在 MySQL 中重置 AUTO_INCREMENT 需要很长时间