2015-05-28 13 views
9

Ho una domanda che carica migliaia di oggetti e voglio domarlo utilizzando find_in_batches:ActiveRecord: alternativa a find_in_batches?

Car.includes(:member).where(:engine => "123").find_in_batches(batch_size: 500) ...

Secondo la documentazione, non posso avere un ordine di ordinamento personalizzato: http://www.rubydoc.info/docs/rails/4.0.0/ActiveRecord/Batches:find_in_batches

Tuttavia, ho bisogno di un ordinamento personalizzato di created_at DESC. Esiste un altro metodo per eseguire questa query in blocchi come fa in find_in_batches in modo che non tutti gli oggetti vivano contemporaneamente nell'heap?

+0

Ottima domanda. Hai guardato [questa gemma] (https://github.com/dburry/each_batched)? Solo circa 5.000 download quindi potrebbe richiedere un po 'di lavoro. Se non ottieni una risposta migliore e finisci per provare questo, sarebbe interessante sapere come ha funzionato. –

+0

Si sta tentando di eseguire un ordinamento personalizzato sulla query originale (in modo da estrarre 500 alla volta in un ordine specifico) o di ordinare i risultati restituiti (ordinare solo i 500)? – creativereason

+0

Ordinamento della query originale, non solo del lotto – Edmund

risposta

1

Hm ho pensato a una soluzione per questo (io sono la persona che ha posto la domanda). È logico che find_in_batches non ti consenta di disporre di un ordine personalizzato perché consente di ordinare l'ordinamento per created_at DESC e specificare un batch_size di 500. Il primo ciclo va da 1 a 500, il secondo ciclo va da 501-1000, ecc. se prima si verifica il secondo ciclo, qualcuno inserisce un nuovo record nella tabella? Questo sarebbe messo in cima ai risultati della query e i risultati sarebbero spostati 1 a sinistra e il tuo secondo ciclo avrebbe una ripetizione.

Si potrebbe obiettare che il created_at ASC sarebbe sicuro, ma non è garantito se l'app specifica un valore created_at.

UPDATE:

ho scritto un gioiello per questo problema: https://github.com/EdmundMai/batched_query

Dato che l'utilizzo di esso, la memoria media della mia applicazione si è dimezzato. Suggerisco caldamente a chiunque abbia problemi simili di verificarlo! E contribuisci se vuoi!

0

Potete immaginare come funziona find_in_batches con l'ordinamento su 1 milione di righe o più? Ordinerà tutte le righe ogni lotto.

Quindi, penso che sarà meglio diminuire il numero di chiamate di ordinamento. Ad esempio per le dimensioni del batch pari a 500 è possibile caricare solo ID (include l'ordinamento) per N * 500 righe e dopo il caricamento di batch di oggetti con questi ID. Quindi, tale modo dovrebbe diminuire avere query con l'ordinamento su DB in N volte.

+0

Per default l'ordinamento su 'id ASC'. com'è diverso? – Edmund

+0

È possibile definire un indice su una colonna del database, in modo che il database non lo separi per ogni richiesta. – Meier

0

Il modo più lento manuale per fare questo, è quello di fare qualcosa di simile:

count = Cars.includes(:member).where(:engine => "123").count 
count = count/500 
count += 1 if count%500 > 0 
last_id = 0 
while count > 0 
    ids = Car.includes(:member).where("engine = "123" and id > ?", last_id).order(created_at: :desc).limit(500).ids #which plucks just the ids` 
    cars = Cars.find(ids) 
    #cars.each or #cars.update_all 
    #do your updating 
    last_id = ids.last 
    count -= 1 
end 
+0

Come menzionato in un commento sopra, se stai cercando l'impaginazione, c'è una gemma per questo.Se stai cercando un modo per scorrere il set di dati di grandi dimensioni per l'aggiornamento o l'estrazione di query, qualcosa come questo funziona fintanto che tieni traccia dell'ultimo id visualizzato. Deve solo fare più chiamate per farlo, ma la seconda chiamata sta tirando in base almeno all'indice ID. – creativereason