问题描述
根据另一位开发人员在 stackoverflow 上的建议,我更新了我的查询如下,但我仍然需要进一步优化它。有人可以指导我如何最好地将索引应用于查询。
见下面的查询:
SELECT a.id,a.user_unique_id,a.loan_location,a.ippis,a.tel_no,a.organisation,a.branch,a.loan_agree,a.loan_type,a.appr,a.sold,a.loan_status,a.top_up,a.current_loan,a.date_created,a.date_updated,c.loan_id,c.user_unique_id AS tu_user_unique_id,c.ippis AS tu_ippis,c.top_up_approved,c.loan_type AS tu_loan_type,c.dse,c.status,c.current_loan AS tu_current_loan,c.record_category,c.date_created AS tu_date_created,c.date_updated AS tu_date_updated
FROM loan_applications_tbl a
LEFT JOIN topup_or_reapplication_tbl c
ON a.ippis=c.ippis
WHERE ((c.status IN ('pending','corrected','Rejected','Processing','Captured','Reviewed','top up')
AND MONTH(CURRENT_DATE) IN (MONTH(c.date_created),MONTH(c.date_updated)
AND YEAR(CURRENT_DATE) IN (YEAR(c.date_created),YEAR(c.date_updated))
AND c.current_loan='1' ))
OR ( a.loan_status IN ('pending','top up')
AND MONTH(CURRENT_DATE) IN (MONTH(a.date_created),MONTH(a.date_updated)) )
AND YEAR(CURRENT_DATE) IN (YEAR(a.date_created),YEAR(a.date_updated))
AND (a.current_loan='1'
OR (a.current_loan='0'
AND a.loan_status IN('Approved','Closed')))))
执行时间:53s
记录数:11000
使用MysqL EXPLAIN给出如下截图:(如何最大化可能的keys列中的信息
我已更新以下附加信息:
我在 c 和 a 之间使用 OR 的原因如下:
-
a
是具有 66 列的父表,如果a
上的新条目具有匹配/现有的ippis
({{ {1}})a
中的某些列被新条目中的数据更新/覆盖,而条目中的剩余数据作为新行插入a
(c
不是在表ippis
中唯一)。这是为了保留所有后续贷款请求的历史记录,同时不为冗余留出空间 -
在检索记录时,我需要大
c
子句使我能够检查OR
和a
表的每个贷款记录的所有实例,其中c
列与我的 WHERE 子句中的参数匹配。 -
status,date and current_loan
中总是会有完整的记录,但a
中不会总是有记录,除非有更多的贷款请求需要相同的唯一 ID。c
包含“谁是帐户人,例如通过唯一 ID”,以及第一笔贷款的附加/补充状态详细信息,随后,在第一笔贷款之后,“c”将是附加/补充状态具有相同唯一 ID 的实际贷款申请的详细信息。 -
如果 "A" 是在 3 月 12 日创建的,并且在 3 月 16 日创建了一个新的 "c" 记录。 "A" 记录也会得到最后更新的标记为 3 月 16 日的记录,因为它有一个孩子,而新的
a
记录有它自己创建和更新的时间戳。c
记录的 Updated 字段将为空/空,直到进行更改或存在a
记录,c
记录的 Updated 字段将为空/空,直到有一些更改制作成c
条记录
我希望这是可以理解的
解决方法
我一直忘记这个术语,因为它对我来说很少出现,但无论如何,您的索引无法通过使用 MONTH() 和 YEAR() 进行优化,因为它们是基础数据上的函数。通过应用日期范围,他们可以。因此,您可以保留您的月/年,例如某些内容是在 2021 年 1 月创建并在 2021 年 3 月更新,但此外,添加 "and c.date_created >= current_date AND current_date <= c.date_updated"
,如果索引中包含创建日期(小于在这种情况下,对于更新日期很重要。
其他表也是如此。
此外,当您从“a”表向“c”表进行左连接时,然后应用 where,这几乎就像您试图强制连接但由于 OR 保持左连接。
我会将基于“c”的条件移动到左连接,然后只测试在那里找到的记录是否为 NULL。
虽然不清楚(我问时没有澄清),但我认为当创建新的“A”记录时,系统实际上可能会将创建日期放入创建日期和更新日期。如果是这种情况,那么我们只需要查询/关注当前活动的当前月份/年份的最后更新日期字段。这现在是 where 子句的主要要求——不管底层 OR 条件到“C”表。
此外,由于月() 和年() 不是sargeable(感谢Ollie),我正在做一个预查询以获取当月和下个月的开始,以便我可以构建一个>
WHERE > beginning of this month and LESS than beginning of next month
至于索引,我会开始更新到
loan_applications_tbl ( date_created,date_updated,loan_status,current_loan,ippis )
topup_or_reapplication_tbl ( ippis,status,date_created,date_updated )
要尝试的最终查询。
SELECT
a.id,a.user_unique_id,a.loan_location,a.ippis,a.tel_no,a.organisation,a.branch,a.loan_agree,a.loan_type,a.appr,a.sold,a.loan_status,a.top_up,a.current_loan,a.date_created,a.date_updated,c.loan_id,c.user_unique_id tu_user_unique_id,c.ippis tu_ippis,c.top_up_approved,c.loan_type tu_loan_type,c.dse,c.status,c.current_loan tu_current_loan,c.record_category,c.date_created tu_date_created,c.date_updated tu_date_updated
FROM
-- this creates inline mySQL variables I can use for the WHERE condition
-- by doing comma after with no explicit join,it is a single row
-- and thus no Cartesian result,just @variables available now
( select
-- first truncating any TIME portion by casting to DATE()
@myToday := date(curdate()),@howFarBack := date_sub( @myToday,interval 6 month ),-- now subtract day of month -1 to get first of THIS month
@beginOfMonth := date_sub( @myToday,interval dayOfMonth( @myToday ) -1 day ),-- and now,add 1 month for beginning of next
@beginNextMonth := date_add( @beginOfMonth,interval 1 month ) ) SqlVars,loan_applications_tbl a
LEFT JOIN topup_or_reapplication_tbl c
ON a.ippis = c.ippis
AND c.current_loan='1'
AND c.status IN ('pending','corrected','Rejected','Processing','Captured','Reviewed','top up')
AND
(
(@beginOfMonth <= c.date_created
AND c.date_created < @beginNextMonth)
OR
(@beginOfMonth <= a.date_updated
AND a.date_updated < @beginNextMonth )
)
WHERE
-- forces only activity for the single month in question
-- since the "a" table knows of any "updates" to the "C",-- its updated basis will keep overall restriction to any accounts
-- updated within this month in question only
-- testing specifically for created OR updated within the
-- current month in question
a.date_created >= @howFarBack
AND
(
(@beginOfMonth <= a.date_created
AND a.date_created < @beginNextMonth)
OR
(@beginOfMonth <= a.date_updated
AND a.date_updated < @beginNextMonth )
)
-- and NOW we can easily apply the OR without requiring
-- to run against the ENTIRE set of BOTH tables.
AND (
c.ippis IS NOT NULL
OR
( a.loan_status IN ( 'pending','top up')
AND (
a.current_loan = '1'
OR ( a.current_loan = '0'
AND a.loan_status IN ('Approved','Closed')
)
)
)
)
关闭对查询的评论
我修改了查询以及第一个表上的主索引,以包含(第一个位置)记录的创建日期。我还添加了一个额外的变量 @howFarBack 作为考虑贷款的最长回溯时间。我默认为 6 个月前。您是否需要考虑贷款超过 6 个月的给定帐户?还是“a”帐户记录的内容可以追溯到 10 年前并希望包括在内?我的印象是这是一个新的贷款申请添加日期。如果是这样,在批准、最终确定、取消之前允许回溯 6 个月,仍然会阻止过去几个月的数据。
在 WHERE 子句中,我为 CREATED_DATE >= @howFarBack 添加了显式添加。永远不可能创建子记录,更不用说在原始添加日期之前的任何时间更新了。这将仅强制当月活动或转发符合条件。
例如:在 4 月 28 日创建一笔贷款。因此,运行查询,月初是 4 月 1 日,但比 5 月 1 日少(这允许包含 4 月 30 日晚上 11:59:59)
现在,我们进入了 5 月,5 月 4 日完成了贷款变更。我们在新的一个月里,@howFarBack 仍然允许 2020 年 12 月之前的旧应用程序与整个应用程序表相比,这些应用程序可能符合我们所知道的可以追溯到 2005 年的整个表。您始终使用最新的数据,并且可以轻松地将 @howFarBack 更改为最大回溯时间。这应该有助于您的性能需求。