Mysql
 sql >> Database >  >> RDS >> Mysql

Ottimizza query (indicizzazione, SPIEGAZIONE) Mysql

Continuo a dimenticare il termine poiché mi viene in mente molto raramente, ma comunque i tuoi indici non possono essere ottimizzati usando MONTH() e YEAR() in quanto sono funzioni sui dati sottostanti. Applicando un RANGE di date, possono. Quindi puoi mantenere il tuo mese/anno come se qualcosa fosse stato creato a gennaio 2021 e aggiornato a marzo 2021, ma in aggiunta, aggiungendo un "and c.date_created >= current_date AND current_date <= c.date_updated" , PUOI utilizzare l'indice se contiene la data di creazione (meno importante in questo caso per la data aggiornata. Analogamente per l'altra tabella.

Inoltre, quando hai il tuo join sinistro dalla tabella "a" alla "c", quindi applicando dove, è quasi come se stessi tentando di forzare il join ma rimane un join sinistro a causa dell'OR.

Vorrei spostare la condizione basata su "c" sul join sinistro, quindi testare il record trovato come NULL o meno.

Sebbene non sia chiaro (non è stato chiarito quando ho chiesto), PENSO che quando viene creato un nuovo record "A", il sistema potrebbe effettivamente inserire la data di creazione sia nella data di creazione che in quella aggiornata. SE QUESTO È IL CASO, allora dobbiamo solo interrogare/riguardare il campo della data dell'ultimo aggiornamento con il mese/anno di attività corrente. Questo è ora il requisito PRIMARIO per la clausola where -- INDIPENDENTE dalla condizione OR sottostante alla tabella "C".

Inoltre, poiché il mese() e l'anno() non sono sargeable (Grazie Ollie), sto eseguendo una prequery per ottenere l'inizio del mese in corso e il mese prossimo in modo da poter creare un

WHERE > beginning of this month and LESS than beginning of next month

Per quanto riguarda gli indici, inizierei l'aggiornamento a

loan_applications_tbl ( date_created, date_updated, loan_status, current_loan, ippis )
topup_or_reapplication_tbl ( ippis, status, current_loan, date_created, date_updated )

Query finale con cui provare.

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')
                            )
                        )
                    )
            )

COMMENTI DI CHIUSURA PER LA QUERY

Ho modificato la query e anche l'indice primario sulla prima tabella per INCLUDE (prima posizione) la data di creazione del record. Ho anche aggiunto una variabile aggiuntiva @howFarBack per essere il tempo massimo di ritorno da considerare per un prestito. Sono tornato indietro di 6 mesi. Avresti mai bisogno di prendere in considerazione un determinato account più vecchio di 6 mesi per un prestito? O l'account "a" registra qualcosa che potrebbe tornare indietro di 10 anni e voler includere? La mia impressione è che si tratti di una nuova data di aggiunta della RICHIESTA DI PRESTITO. In tal caso, consentire di tornare indietro di 6 mesi prima che venga approvato, finalizzato, annullato impedirebbe comunque di passare storicamente tanti mesi di dati.

Nella clausola WHERE, ho aggiunto l'aggiunta esplicita per CREATED_DATE>=@howFarBack. Non sarebbe mai possibile creare un record figlio, per non parlare di aggiornarlo in qualsiasi momento prima della data di aggiunta originale. Ciò forzerà solo l'attività del mese corrente O AVANTI a qualificarsi.

Es:creare un prestito il 28 aprile. Quindi, eseguendo la query, l'inizio del mese è il 1 aprile ma MENO del 1 maggio (questo consente l'inclusione del 30 aprile alle 23:59:59)

Ora entriamo a maggio e il 4 maggio viene effettuato un cambio sul prestito. Siamo in un nuovo mese e @howFarBack consente ancora alle applicazioni più vecchie fino a dicembre 2020 di qualificarsi POSSIBILE rispetto all'intera tabella delle domande che potrebbero risalire fino al 2005 per quanto ne sappiamo. Rimani sempre con i dati più aggiornati e puoi modificare abbastanza facilmente @howFarBack come tempo massimo per tornare indietro. Questo dovrebbe aiutare le tue esigenze di prestazioni.