2010-11-20 3 views
0

Ich habe eine Sammlung von Benutzern:Verwenden von Ruby, wie übergeben Sie die Sammlung an die Methode, aber eine Teilmenge davon?

users = User.all() 

Ich möchte ein Verfahren eine Teilmenge der Benutzer Sammlung zu übergeben. Jede Untergruppe sollte 1000 Elemente enthalten (oder weniger in der letzten Iteration).

some_method(users) 

So sagen Benutzer hat 9500 Artikel drin, ich some_method 10 mal anrufen möchten, 9 mal vorbei 1000 Elemente und das letzte Mal, 500.

+1

Ruby oder Schienen? Wenn dies von einer Datenbank kommt, möchten Sie vielleicht nicht sofort 'all()' in den Speicher ziehen. – Ken

+0

Wie wäre es mit Ihrer Akzeptanzbewertung? 53% sind niedrig in Anbetracht Ihres guten Rufs. –

Antwort

3

können Sie Enumerable#each_slice Methode verwenden:

User.all.each_slice(1000) do |subarray| 
    some_method subarray 
end 

aber das würde zunächst alle Datensätze aus der Datenbank ziehen.

aber ich denke, man so etwas wie dieses machen könnte:

def ar_each_slice scope, size 
    (scope.count.to_f/size).ceil.times do |i| 
    yield scope.scoped(:offset => i*size, :limit => size) 
    end 
end 

und es verwenden, wie in:

ar_each_slice(User.scoped, 1000) do |slice| 
    some_method slice.all 
end 

Es wird zunächst die Anzahl der Datensätze (mit COUNT), und dann Holen Sie 1000 durch 1000 mit LIMIT-Klausel und übergeben Sie es an Ihren Block.

0

Ich denke, Sie in Teilmenge aufteilen sollte manuell . Zum Beispiel

some_method(users[0..999]) 
+0

Was ist mit der äußeren Schleife, die die originallusers-Auflistung durchläuft und dann 1K an den Methodenaufruf übergibt? – Blankman

+0

'Benutzer [0..999] erfordert immer noch das Lesen des gesamten Satzes und dann das Abschneiden der ersten 1000. Wenn die Tabelle 2.000.000 Zeilen enthält, wäre das eine Art Ressourcenfresser. –

0

Ich habe vergessen, :batch_size zu verwenden, aber Chandra schlug vor. Das ist der richtige Weg.


.all verwendet, wird der Datenbank bitten alle Datensätze abzurufen, um sie zu Rubin vorbei dann über sie intern laufen zu halten. Das ist ein wirklich schlechter Weg, damit umzugehen, wenn Ihre Datenbank wächst. Das liegt daran, dass der Globe von Records den DBM mit zunehmendem Wachstum härter arbeiten lässt und Ruby mehr und mehr Speicherplatz zuweisen muss, um sie zu halten. Ihre Reaktionszeit wird dadurch wachsen.

Eine bessere Lösung ist die Verwendung der Optionen :limit und :offset, um dem DBM mitzuteilen, nacheinander die ersten 1000 Datensätze bei Offset 0, dann die nächsten 1000 Datensätze bei Offset 1 usw. zu finden. Schleifen solange, bis keine Datensätze mehr vorhanden sind.

Sie können bestimmen, wie oft Sie eine Schleife machen müssen, indem Sie eine .count vor der Nachfrage, die wirklich schnell ist, wenn Ihre Where-Klausel ist scheußlich, oder einfach Schleife, bis Sie keine Datensätze zurück.

3

Da Rails 2.3 kann man batch_size angeben:

User.find_in_batches(:batch_size =>1000) do |users| 
    some_method(users) 
end 

In diesem Fall Rahmen läuft Auswahlabfrage für alle 1000 Datensätze. Wenn Sie eine große Anzahl von Datensätzen verarbeiten, bleibt der Speicher niedrig.

+0

+1. Ich habe '' batch_size'' vergessen. Guter Fang! –

Verwandte Themen