优化查询(索引、解释)Mysql

Optimise query (Indexing, EXPLAIN) Mysql

根据另一位开发人员在 Whosebug 上的建议,我已经更新了我的查询如下,但我仍然需要进一步优化它。有人可以指导我如何最好地将索引应用于查询。

参见下面的查询:

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', 'corrected', 'Rejected', 'Processing', 'Captured', 'Reviewed', '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 给出了以下屏幕截图:(如何最大化 possible_keys 列中的信息

我更新了以下附加信息:

出于以下原因,我在 c 和 a 之间使用 OR:

  1. a 是具有 66 列的父级 table,如果 a 上的新条目具有 matching/existing ippisa 上的唯一字段)a 中的某些列 updated/overwriten 包含来自新条目的数据,而条目中的其余数据作为新行插入 cippis 在 table c 中不是唯一的。这是为了保留所有后续贷款请求的历史记录,同时不留出冗余空间

  2. 在检索记录时,我需要大的 OR 子句来让我检查每个记录的所有实例的 ac table status, date and current_loan 列与我的 WHERE 子句中的参数匹配的贷款记录。

  3. a 中始终会有完整的记录,但 c 中不会始终有记录,除非有更多贷款请求使用相同的唯一 ID。 a 包含 "who is the account person such as by unique ID", and the additional / supplemental status detail FOR THE FIRST LOAN, 随后,在第一笔贷款“c”之后将是附加/具有相同 Unique ID.

    的实际贷款申请的补充状态详细信息
  4. 如果“A”是在 3 月 12 日创建的,并且在 3 月 16 日创建了一个新的“c”记录。“A”记录也获得最后更新的标记为 Mar 16 因为它有一个对它有一些影响的子附件,而新的 c 记录有它自己创建和更新的时间戳。 a 记录的更新字段将为 blank/null,直到进行更改或存在 c 记录,更新字段将为 c 记录的 blank/null直到对 c 记录

    进行了一些更改

我希望这是可以理解的

我一直忘记这个术语,因为它很少出现在我身上,但是无论如何,您的索引不能通过使用 MONTH() 和 YEAR() 来优化,因为它们是基础数据的函数。通过应用日期范围,他们可以。因此,您可以保留 month/year,例如某些内容是在 2021 年 1 月创建并在 2021 年 3 月更新的,但此外,添加一个 "and c.date_created >= current_date AND current_date <= c.date_updated",如果其中包含创建日期,您可以使用该索引(在这种情况下,更新日期不太重要。 对于您的其他 table.

也是如此

此外,当您从“a”到“c”进行左连接时 table,然后应用 where,这几乎就像您试图强制连接但仍然是左连接由于 OR.

我会将基于“c”的条件移动到左连接,然后只测试在那里找到的记录是否为 NULL。

虽然不清楚(我问的时候没有弄清楚),但我认为当创建新的“A”记录时,系统实际上可能会将创建日期同时放入创建日期和更新日期。如果是这种情况,那么我们只需要 query/concern 最后更新的日期字段与 activity 的当前 month/year。现在这是 where 子句的主要要求——不管“C”的基础 OR 条件如何 table.

此外,由于 month() 和 year() 不是 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, current_loan, 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', 'corrected', 'Rejected', 'Processing', 
                            'Captured', 'Reviewed', 'top up')
                    AND (   
                            a.current_loan = '1' 
                        OR  (   a.current_loan = '0' 
                            AND a.loan_status IN ('Approved', 'Closed')
                            )
                        )
                    )
            )

关闭查询评论

我修改了查询以及第一个 table 上的主索引以包含(第一个位置)记录的创建日期。我还添加了一个额外的变量@howFarBack 作为考虑贷款的最长回溯时间。我默认为 6 个月前。您是否需要考虑将超过 6 个月的给定帐户用于贷款?还是“a”帐户记录了可以追溯到 10 年前并想要包含的内容?我的印象是这是一个新的 LOAN APPLICATION 添加日期。如果是这样,允许在批准、最终确定、取消之前回溯 6 个月,仍然会阻止历史上处理尽可能多的月数数据。

在 WHERE 子句中,我为 CREATED_DATE >= @howFarBack 添加了显式添加。永远不可能创建子记录,更不用说在原始添加日期之前的任何时间进行更新了。这将强制只有当月 activity 或 FORWARD 符合资格。

例如:在 4 月 28 日创建贷款。所以 运行 查询,月初是 4 月 1 日,但小于 5 月 1 日(这允许在 11:59:59pm 包含 4 月 30 日)

现在,我们进入了 5 月,贷款的变更将于 5 月 4 日完成。我们进入了新的一个月,@howFarBack 仍然允许截至 2020 年 12 月的旧应用程序有可能符合我们所知道的可以追溯到 2005 年的整个 table 应用程序。您始终使用最新数据,并且可以很容易地将@howFarBack 更改为最大回溯时间。这应该有助于满足您的性能需求。