Neu: udemy Kurs Ruby für Anfänger von Stefan Wintermeyerdiv class="para">In dem ein oder anderen Fall braucht man immer mal den ersten oder den
letzten oder auch alle Datensätze. Deshalb gibt es für alle drei Fälle eine
fertige Methode. Fangen wir mit den einfachsten an:
first
und last
.MacBook:europe xyz$ rails console
Loading development environment (Rails 3.2.3)
1.9.3p194 :001 > Country.first
Country Load (0.2ms) SELECT "countries".* FROM "countries" LIMIT 1
=> #<Country id: 1, name: "Deutschland", population: 81831000, created_at: "2012-04-26 10:26:38", updated_at: "2012-04-26 10:26:38">
1.9.3p194 :002 > Country.last
Country Load (0.3ms) SELECT "countries".* FROM "countries" ORDER BY "countries"."id" DESC LIMIT 1
=> #<Country id: 4, name: "Niederlande", population: 16680000, created_at: "2012-04-26 10:47:23", updated_at: "2012-04-26 10:47:23">
1.9.3p194 :003 > exit
MacBook:europe xyz$
Und jetzt mal alle auf einmal mit
all
:
MacBook:europe xyz$ rails console
Loading development environment (Rails 3.2.3)
1.9.3p194 :001 > Country.all
Country Load (0.2ms) SELECT "countries".* FROM "countries"
=> [#<Country id: 1, name: "Deutschland", population: 81831000, created_at: "2012-04-26 10:26:38", updated_at: "2012-04-26 10:26:38">, #<Country id: 2, name: "Frankreich", population: 65447374, created_at: "2012-04-26 10:41:54", updated_at: "2012-04-26 10:41:54">, #<Country id: 3, name: "Belgien", population: 10839905, created_at: "2012-04-26 10:44:49", updated_at: "2012-04-26 10:44:49">, #<Country id: 4, name: "Niederlande", population: 16680000, created_at: "2012-04-26 10:47:23", updated_at: "2012-04-26 10:47:23">]
1.9.3p194 :002 > exit
MacBook:europe xyz$
Die von
first
,
last
und
all
erzeugten Objekte sind aber
unterschiedlich. Bei
first
und
last
wird ein Objekt der Klasse
Country
ausgegeben und bei
all
natürlich ein
Array
solcher
Objekte:
MacBook:europe xyz$ rails console
Loading development environment (Rails 3.2.3)
1.9.3p194 :001 > Country.first
Country Load (0.1ms) SELECT "countries".* FROM "countries" LIMIT 1
=> #<Country id: 1, name: "Deutschland", population: 81831000, created_at: "2012-04-26 10:26:38", updated_at: "2012-04-26 10:26:38">
1.9.3p194 :002 > Country.first.class
Country Load (0.3ms) SELECT "countries".* FROM "countries" LIMIT 1
=> Country(id: integer, name: string, population: integer, created_at: datetime, updated_at: datetime)
1.9.3p194 :003 > Country.all
Country Load (0.3ms) SELECT "countries".* FROM "countries"
=> [#<Country id: 1, name: "Deutschland", population: 81831000, created_at: "2012-04-26 10:26:38", updated_at: "2012-04-26 10:26:38">, #<Country id: 2, name: "Frankreich", population: 65447374, created_at: "2012-04-26 10:41:54", updated_at: "2012-04-26 10:41:54">, #<Country id: 3, name: "Belgien", population: 10839905, created_at: "2012-04-26 10:44:49", updated_at: "2012-04-26 10:44:49">, #<Country id: 4, name: "Niederlande", population: 16680000, created_at: "2012-04-26 10:47:23", updated_at: "2012-04-26 10:47:23">]
1.9.3p194 :004 > Country.all.class
Country Load (0.3ms) SELECT "countries".* FROM "countries"
=> Array
1.9.3p194 :005 > exit
MacBook:europe xyz$
Wenn
County.all
ein Array zurückgibt, dann
müsste man doch auch Iteratoren (siehe
„Iteratoren (Iterators)“ und
„Iterator each“) benutzen können, oder? Ja, natürlich! Das ist
ja das Schöne daran. Kleiner Versuch mit
each
:
MacBook:europe xyz$ rails console
Loading development environment (Rails 3.2.3)
1.9.3p194 :001 > Country.all.each do |country|
1.9.3p194 :002 > puts country.name
1.9.3p194 :003?> end
Country Load (0.1ms) SELECT "countries".* FROM "countries"
Deutschland
Frankreich
Belgien
Niederlande
=> [#<Country id: 1, name: "Deutschland", population: 81831000, created_at: "2012-04-26 10:26:38", updated_at: "2012-04-26 10:26:38">, #<Country id: 2, name: "Frankreich", population: 65447374, created_at: "2012-04-26 10:41:54", updated_at: "2012-04-26 10:41:54">, #<Country id: 3, name: "Belgien", population: 10839905, created_at: "2012-04-26 10:44:49", updated_at: "2012-04-26 10:44:49">, #<Country id: 4, name: "Niederlande", population: 16680000, created_at: "2012-04-26 10:47:23", updated_at: "2012-04-26 10:47:23">]
1.9.3p194 :004 > exit
MacBook:europe xyz$
Kann man dann auch
.all.first
als Alternative zu
.first
benutzen? Ja, aber es macht wenig Sinn. Sehen Sie
selbst:
MacBook:europe xyz$ rails console
Loading development environment (Rails 3.2.3)
1.9.3p194 :001 > Country.first
Country Load (0.2ms) SELECT "countries".* FROM "countries" LIMIT 1
=> #<Country id: 1, name: "Deutschland", population: 81831000, created_at: "2012-04-26 10:26:38", updated_at: "2012-04-26 10:26:38">
1.9.3p194 :002 > Country.all.first
Country Load (0.3ms) SELECT "countries".* FROM "countries"
=> #<Country id: 1, name: "Deutschland", population: 81831000, created_at: "2012-04-26 10:26:38", updated_at: "2012-04-26 10:26:38">
1.9.3p194 :003 > exit
MacBook:europe xyz$
Selbst bei unserer Mini-Datenbank sehen wir schon einen
Geschwindigkeitsunterschied von 0.1 Millisekunden. Bei
Country.first
wird per SQL in der Datenbank mit einem
LIMIT 1
der erste Datensatz herausgesucht und von ActiveRecord
als einzelnes Objekt der Country-Klasse ausgegeben. Bei
Country.all.first
wird erst die ganze Tabelle mit SELECT
"countries".* FROM "countries"
als Array eingelesen und das erste
Element dieses Arrays rausgezogen. Bei dieser kleinen Applikation kann man
sich das noch leisten, aber stellen Sie sich mal vor, es würde sich um eine
Datenbank mit vielen Millionen Einträgen handeln.
Warnung
SQL-Datenbanken haben normalerweise keine automatische Sortierung
der Ergebnisse eines
SELECT * FROM xyz
. Die Datenbank kann
die Reihenfolge der Datensätze selber bestimmen. Entsprechend können wir
bei einem
LIMIT 1
aus einer solchen Menge auch nicht 100%ig
sicher sein, dass der für uns Menschen logisch erste Datensatz rauskommt.
Faktisch heißt das, dass wir mit einem
Country.first
nicht
zwingend den ersten Datensatz bekommen müssen. Der SQL-Datenbank steht es
frei, z. B. aus Performance-Gründen eine andere Sortierung vorzunehmen.
Wenn wir absolut sichergehen wollen, dass wir den für uns logisch ersten
Datensatz bekommen, so müssen wir mit der
order
-Methode (siehe
„order und reverse_order“) arbeiten:
MacBook:europe xyz$ rails console
Loading development environment (Rails 3.2.3)
1.9.3p194 :001 > Country.order(:id).first
Country Load (0.6ms) SELECT "countries".* FROM "countries" ORDER BY id LIMIT 1
=> #<Country id: 1, name: "Deutschland", population: 81831000, created_at: "2012-04-26 10:26:38", updated_at: "2012-04-26 10:26:38">
1.9.3p194 :002 > exit
MacBook:europe xyz$
Sie sehen im SQL, dass mit SELECT "countries".* FROM
"countries" ORDER BY id LIMIT 1
erst die Tabelle nach der
id
sortiert und dann die erste Zeile ausgegeben
wird.
Meistens arbeitet die SQL-Datenbank genau so, wie man es erwartet
und man bekommt mit Country.first
den ersten Datensatz
geliefert. Wenn man aber ganz sicher sein will, dann sollte man immer mit
einem order
arbeiten.
Am einfachsten geht dies mit einem default_scope.