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
。但我怀疑你甚至不需要这种优化。我稍后会展示。
我也对您的JOIN
s 有很多疑问。但是由于您还没有回复 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 中重置 AUTO_INCREMENT 需要很长时间