phpMyAdmin
 sql >> Database >  >> Database Tools >> phpMyAdmin

Seleziona un record solo se quello precedente ha un valore inferiore impiega troppo tempo e fallisce

Ecco una soluzione per la tua domanda 1 che verrà eseguita molto più velocemente, poiché hai molte scansioni complete di tabelle e sottoquery dipendenti. Qui avrai al massimo una sola scansione della tabella (e forse una tabella temporanea, a seconda di quanto sono grandi i tuoi dati e quanta memoria hai). Penso che tu possa facilmente adattarlo alla tua domanda qui. Probabilmente anche la domanda 2 (non l'ho letto davvero) ha una risposta poiché ora è facile aggiungere semplicemente where date_column = whatever

select * from (
    select
    t.*,
    if(@prev_toner < Remain_Toner_Black and @prev_sn = SerialNumber, 1, 0) as select_it,
    @prev_sn := SerialNumber,
    @prev_toner := Remain_Toner_Black
    from
    Table1 t
    , (select @prev_toner:=0, @prev_sn:=SerialNumber from Table1 order by SerialNumber limit 1) var_init
    order by SerialNumber, id
) sq  
where select_it = 1
  • guardalo in funzione dal vivo in un sqlfiddle

MODIFICA:

Spiegazione:

Con questa linea

    , (select @prev_toner:=0, @prev_sn:=SerialNumber from Table1 order by SerialNumber 

inizializziamo semplicemente le variabili @prev_toner e @prev_sn al volo. È come non avere questa riga nella query ma scrivere davanti alla query

SET @prev_toner = 0;
SET @prev_sn = (select serialnumber from your_table order by serialnumber limit 1);
SELECT ...

Quindi, perché la query per assegnare un valore a @prev_sn e perché ordinare per numero di serie? L'ordine di è molto importante. Senza un ordine per non esiste un ordine garantito in cui vengono restituite le righe. Inoltre, accederemo al valore delle righe precedenti con le variabili, quindi è importante che gli stessi numeri di serie siano "raggruppati".

Le colonne nella clausola select vengono valutate una dopo l'altra, quindi è importante selezionare prima questa riga

if(@prev_toner < Remain_Toner_Black and @prev_sn = SerialNumber, 1, 0) as select_it,

prima di selezionare queste due righe

@prev_sn := SerialNumber,
@prev_toner := Remain_Toner_Black

Perché? Le ultime due righe assegnano alle variabili solo i valori delle righe correnti. Pertanto in questa riga

if(@prev_toner < Remain_Toner_Black and @prev_sn = SerialNumber, 1, 0) as select_it,

le variabili mantengono ancora i valori delle righe precedenti. E quello che facciamo qui non è altro che dire "se il valore delle righe precedenti nella colonna Remain_Toner_Black è più piccolo di quello nella riga corrente e il numero di serie delle righe precedenti è uguale al numero di serie delle righe effettive, restituisce 1, altrimenti restituisce 0."

Quindi possiamo semplicemente dire nella query esterna "seleziona ogni riga, dove sopra ha restituito 1".

Data la tua query, non hai bisogno di tutte queste sottoquery. Sono molto costosi e inutili. In realtà è abbastanza folle. In questa parte della query

    SELECT  a.ID, 
            a.Time, 
            a.SerialNumber, 
            a.Remain_Toner_Black,
            a.Remain_Toner_Cyan,
            a.Remain_Toner_Magenta,
            a.Remain_Toner_Yellow,
            (
                SELECT  COUNT(*)
                FROM    Reports c
                WHERE   c.SerialNumber = a.SerialNumber AND
                        c.ID <= a.ID) AS RowNumber
    FROM    Reports a

selezioni l'intera tabella e per ogni riga conta le righe all'interno di quel gruppo. Questa è una sottoquery dipendente. Tutto solo per avere una sorta di numero di riga. Quindi lo fai una seconda volta, solo così puoi unire queste due tabelle temporanee per ottenere la riga precedente. Davvero, non c'è da stupirsi che la performance sia orribile.

Quindi, come adattare la mia soluzione alla tua richiesta? Invece dell'unica variabile che ho usato per ottenere la riga precedente per Remain_Toner_Black, usane quattro per i colori nero, ciano, magenta e giallo. E unisciti al tavolo Stampanti e Clienti come hai già fatto. Non dimenticare l'ordine entro e il gioco è fatto.