Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Otimize a consulta (Indexação, EXPLAIN) Mysql


Eu continuo esquecendo o termo, pois ele aparece muito raramente para mim, mas de qualquer forma, seus índices não podem ser otimizados usando MONTH() e YEAR(), pois são funções nos dados subjacentes. Ao aplicar um RANGE de datas, eles podem. Assim, você pode manter seu mês/ano, como se algo tivesse sido criado em janeiro de 2021 e atualizado em março de 2021, mas, além disso, adicionando um "and c.date_created>=current_date AND current_date <=c.date_updated" , você PODE utilizar o índice se ele tiver a data de criação nele (menos importante neste caso para a data atualizada. Da mesma forma para sua outra tabela.

Além disso, quando você tem sua junção esquerda da tabela "a" para a tabela "c", aplicando where, é quase como se você estivesse tentando forçar a junção, mas permanecendo junção esquerda devido ao OR.

Eu moveria a condição baseada em "c" para a junção à esquerda e, em seguida, apenas testaria o registro encontrado lá como NULL ou não.

Embora não esteja claro (não foi esclarecido quando perguntei), ACHO que quando um novo registro "A" é criado, o sistema pode realmente colocar a data de criação tanto na data de criação quanto na data de atualização. SE FOR O CASO, basta consultar/referir o campo de data da última atualização com o mês/ano atual de atividade. Esse é agora o requisito PRINCIPAL para a cláusula where -- INDEPENDENTEMENTE da condição OR subjacente à tabela "C".

Além disso, como month() e year() não são sargeable (Obrigado Ollie), estou fazendo uma pré-consulta para obter o início do mês atual e o próximo mês para que eu possa construir um
WHERE > beginning of this month and LESS than beginning of next month

Quanto aos índices, eu começaria a atualizar para
loan_applications_tbl ( date_created, date_updated, loan_status, current_loan, ippis )
topup_or_reapplication_tbl ( ippis, status, current_loan, date_created, date_updated )

Consulta final para tentar.
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')
                            )
                        )
                    )
            )

COMENTÁRIOS DE ENCERRAMENTO PARA CONSULTA

Modifiquei a consulta e também o índice primário na primeira tabela para INCLUIR (primeira posição) a data de criação do registro. Também adicionei uma variável adicional @howFarBack para ser o tempo máximo de retorno a ser considerado para um empréstimo. Eu desativei 6 meses atrás. Você precisaria considerar uma determinada conta com mais de 6 meses para um empréstimo? Ou a conta "a" registra algo que poderia voltar 10 anos e deseja incluir? Minha impressão é que é uma nova data de adição de APLICAÇÃO DE EMPRÉSTIMO. Nesse caso, permitir voltar 6 meses antes de ser aprovado, finalizado e cancelado ainda impediria o histórico de tantos meses de dados.

Na cláusula WHERE, adicionei um add explícito para CREATED_DATE>=@howFarBack. Nunca seria possível criar um registro filho, muito menos atualizar a qualquer momento antes da data de adição original. Isso forçará apenas a atividade do mês atual OU FORWARD a se qualificar.

Ex:Crie um empréstimo no dia 28 de abril. Então, executando a consulta, o início do mês é 1º de abril, mas MENOS que 1º de maio (isso permite a inclusão de 30 de abril às 23:59:59)

Agora, entramos em maio e uma mudança no empréstimo é feita no dia 4 de maio. Estamos em um novo mês e o @howFarBack ainda permite que aplicativos mais antigos até dezembro de 2020 POSSIVELMENTE se qualifiquem contra toda a tabela de aplicativos que podem voltar até 2005, pelo que sabemos. Você está sempre com os dados mais atuais e pode alterar o @howFarBack com bastante facilidade como o tempo máximo de retorno. Isso deve ajudar suas necessidades de desempenho.