Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Abfrage optimieren (Indizierung, EXPLAIN) Mysql

Ich vergesse den Begriff immer wieder, da er mir sehr selten einfällt, aber Ihre Indizes können sowieso nicht mit MONTH() und YEAR() optimiert werden, da es sich um Funktionen für die zugrunde liegenden Daten handelt. Durch Anwenden eines Datumsbereichs ist dies möglich. Sie können also Ihren Monat/Ihr Jahr beibehalten, z. B. wenn etwas im Januar 2021 erstellt und im März 2021 aktualisiert wurde, aber zusätzlich einen " und c.date_created>=aktuelles_datum UND aktuelles_datum <=c.date_updated" hinzufügen. Code> , KÖNNEN Sie den Index verwenden, wenn er das Erstellungsdatum enthält (weniger wichtig in diesem Fall für das Aktualisierungsdatum. Ähnlich für Ihre andere Tabelle.

Wenn Sie Ihren Left-Join von der "a"- zur "c"-Tabelle haben und dann anwenden, wo, ist es fast so, als ob Sie versuchen würden, den Join zu erzwingen, aber aufgrund des OR Links-Join bleiben.

Ich würde die auf "c" basierende Bedingung in den Left-Join verschieben und dann einfach auf den dort gefundenen Datensatz als NULL oder nicht testen.

Obwohl nicht klar (wurde nicht geklärt, als ich fragte), denke ich, dass das System das Erstellungsdatum tatsächlich sowohl in das Erstellungsdatum als auch in das Aktualisierungsdatum einfügen kann, wenn ein neuer "A" -Datensatz erstellt wird. WENN DIES DER FALL IST, müssen wir nur das zuletzt aktualisierte Datumsfeld mit dem aktuellen Monat/Jahr der Aktivität abfragen/betreffen. Das ist jetzt die PRIMÄRE Anforderung für die where-Klausel – UNABHÄNGIG von der zugrunde liegenden OR-Bedingung für die „C“-Tabelle.

Da außerdem month() und year() nicht sargeable sind (Danke Ollie), ich mache eine Vorabfrage, um den Beginn des aktuellen Monats und des nächsten Monats zu erhalten, damit ich ein

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

Was Indizes betrifft, würde ich mit dem Update auf

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

Letzte Abfrage zum Ausprobieren.

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

SCHLUSSKOMMENTARE FÜR DIE ABFRAGE

Ich habe die Abfrage und auch den Primärindex in der ersten Tabelle so geändert, dass sie das Erstellungsdatum des Datensatzes (erste Position) enthalten. Ich habe auch eine zusätzliche Variable @howFarBack hinzugefügt, um die maximale Rücklaufzeit anzugeben, die für einen Kredit in Betracht gezogen werden kann. Ich bin auf 6 Monate zurückgefallen. Würden Sie jemals ein bestimmtes Konto, das älter als 6 Monate ist, für einen Kredit in Betracht ziehen müssen? Oder handelt es sich bei den „a“-Kontoaufzeichnungen um etwas, das 10 Jahre zurückgehen könnte und das Sie einbeziehen möchten? Mein Eindruck ist, dass es sich um ein neues KREDITANTRAG-Hinzufügungsdatum handelt. Wenn dies der Fall ist, würde das Zurückgehen von 6 Monaten vor der Genehmigung, Fertigstellung oder Stornierung immer noch verhindern, dass historisch so viele Monate an Daten verarbeitet werden.

In der WHERE-Klausel habe ich das explizite Hinzufügen für das CREATED_DATE>=@howFarBack hinzugefügt. Es wäre niemals möglich, einen untergeordneten Datensatz vor dem ursprünglichen Hinzufügungsdatum zu erstellen, geschweige denn zu aktualisieren. Dadurch werden nur Aktivitäten des aktuellen Monats ODER FORWARD zur Qualifizierung gezwungen.

Bsp.:Erstellen Sie am 28. April einen Kredit. Wenn Sie also die Abfrage ausführen, ist der Beginn des Monats der 1. April, aber WENIGER als der 1. Mai (dies ermöglicht die Einbeziehung des 30. April um 23:59:59 Uhr)

Jetzt kommen wir in den Mai und eine Änderung des Darlehens wird am 4. Mai vorgenommen. Wir befinden uns in einem neuen Monat und das @howFarBack ermöglicht immer noch, dass sich ältere Bewerbungen bis Dezember 2020 MÖGLICHERWEISE qualifizieren, verglichen mit der gesamten Bewerbungstabelle, die nach allem, was wir wissen, bis 2005 zurückreichen könnte. Sie bleiben immer bei den aktuellsten Daten und können @howFarBack ganz einfach als maximale Rücklaufzeit ändern. Dies sollte Ihren Leistungsanforderungen entsprechen.