10.6 Suchen
kap_activerecord_suchenfind
Für die Suche wird die Klassen-Methode find auf ein ActiveRecord-Model angewendet. ActiveRecord kennt folgende Arten der Suche:
- Suche nach einem oder mehreren Datensätzen über ihre ID
Mit dem Aufruf Client.find(1) wird ein Objekt mit dem ersten Datensatz zurückgeliefert.
- Suche nach dem ersten Datensatz, der bestimmte Kriterien erfüllt
Client.find(:first) liefert ein Objekt mit dem ersten Datensatz zurück. Optional kann ein Suchkriterium und/oder die Sortierreihenfolge angegeben werden:Client.find(:first, :conditions => "firstname = 'William'")
- Suche nach allen Datensätze, die bestimmte Kriterien erfüllen
Client.find(:all) liefert eine Liste von Objekten aller Datensätze zurück. Bei dieser Suchvariante kann auch ein Suchkriterium und/oder eine Sortierreihenfolge angegeben werden:Client.find(:all, :order => "created_at DESC")
- Suche mit dynamischen find-Methoden
ActiveRecord bietet eine Kurzschreibweise für die Suche nach einem bestimmten Feld an (find_by_feldname). Um z. B. nach dem Vornamen eines Kunden zu suchen, kann man die Schreibweise Client. find_by_firstname("Hussein") verwenden.
- Suche mit selbstdefinierten find-Methoden
Sie können auch Ihre eigenen find-Methoden definieren, wie z. B. client.find_new_last_week, um alle neuen User, die seit der letzten Woche angelegt wurden, abzufragen.
- Suche über SQL
Sie können mit find_by_sql direkt SQL-Befehle an die Datenbank schicken. Wird in der Regel relativ selten verwendet.
Suche nach IDs
Primärschlüssel ID
Die einfachste Suche ist die Suche nach einer oder mehreren IDs. In ActiveRecord sollte jede Tabelle einen Primärschlüssel vom Typ Integer mit dem Feldnamen id besitzen. Wird eine Datenbanktabelle über eine Migration-Datei erzeugt, wird dieses Feld automatisch angelegt. Die ID wird von Rails automatisch verwaltet. Beim Einfügen eines neuen Datensatzes wird jeweils die nächsthöhere ID verwendet (auto increment). Jeder Datensatz besitzt also eine eindeutige ID.
In der Praxis verwenden Sie oft noch weitere Felder, um einen Datensatz eindeutig zu kennzeichnen. Dazu gehören z. B. Kundennummern, ISBN, Bestellnummern usw. Diese Felder können zusätzlich zu der ID in der Tabelle angelegt werden. Die Suche nach der ID, die im Folgenden beschrieben wird, sucht jedoch nur nach dem Feld id . Verwenden Sie für die Suche nach mehreren Feldern die Suche über alle Datensätze.
Zusammengesetzte Primärschlüssel |
Zusammengesetzte Primärschlüssel werden in Rails nicht direkt unterstützt. |
Die Suche nach einer ID liefert genau ein Objekt mit dem entsprechenden Datensatz zurück, wenn der Datensatz mit dieser ID existiert:
client = Client.find(1) client.id => 1
Mehrere IDs
Um mehrere IDs zu suchen, werden einfach die entsprechenden IDs übergeben. Es ist auch erlaubt, die Werte in einem Array zu übergeben. Das Ergebnis ist das gleiche:
clients = Client.find(1,3) clients.size => 2 clients.map(&:id) => [1,3]
clients = Client.find([1,3]) clients.size => 2 clients.map(&:id) => [1,3]
Der Ausdruck clients.map(&:id) ist eine Abkürzung für clients.map{|c| c.id\ }.
Laufzeitfehler, falls IDs nicht vorhanden |
Wenn der gesuchte Datensatz bei der Suche nach einer ID nicht existiert,
wird ein RecordNotFound-Laufzeitfehler generiert. Bei allen anderen
Sucharten wird ein leeres Ergebnis (nil) zurückgeliefert.
client = Client.find(13) => ActiveRecord::RecordNotFound: Couldn't find Client with ID=13 begin client = Client.find(13) rescue ActiveRecord::RecordNotFound # Der Code in diesem Bereich wird ausgeführt, # wenn es zu einem Fehler gekommen ist. end |
Suche nach dem ersten Datensatz oder Ausgabe aller Datensätze
Sortierreihenfolge
Mit dem Aufruf find(:first) wird der erste Datensatz zurückgeliefert. Wenn keine Sortierreihenfolge oder kein Suchkriterium angegeben wurde, ist nicht klar, welcher Datensatz dann zurückgeliefert wird (in MySQL wird der Datensatz mit der kleinsten ID zurückgegeben). Deshalb ist es empfehlenswert, eine Sortierreihenfolge anzugeben. Im folgenden Fall wird der Datensatz mit der kleinsten ID ausgegeben:
client = Client.find(:first, :order=>"id") client.id => 1
Der Aufruf find(:all) liefert ein Array aller Datensätze:
clients = Client.find(:all) clients.size => 3 clients.map(&:firstname) => ["William", "Kara", "Lee"]
Richtig interessant wird es erst, wenn die Suche mittels Suchkriterien eingeschränkt wird.
Wie das letzte Element ermitteln? |
Das letzte Element kann einfach ermittelt werden, indem man die Sortierreihenfolge umkehrt. Um z. B. den zuletzt eingefügten Datensatz zu ermitteln (der Datensatz mit der höchsten ID), geben Sie die Sortierreihenfolge absteigend :order => " id DESC" statt aufsteiegend :order =>" id" an.
client = Client.find(:first, :order=>"id DESC") |
Suchoptionen im Überblick
Es gibt eine Menge von optionalen Parametern, die für find(:first) und find(:all) verwendet werden können:
- :conditions
Gibt ein Suchkriterium an.
- :order
Gibt an, nach welchen Feldern sortiert werden soll.
- :group
Hiermit können Gruppierungen durchgeführt werden.
- :limit
Beschränkt die Anzahl der Suchergebnisse.
- :offset
Gibt an, ab welchem Datensatz die Ergebnisse zurückgeliefert werden sollen.
- :joins
Entspricht dem Join-SQL-Befehl. Kommt eher selten zum Einsatz, da Joins automatisch von ActiveRecord durchgeführt werden.
- :include
Hiermit können assoziierte Tabellen angegeben werden, die beim Generieren der Ergebnisobjekte gleich mitgeladen werden.
- :select
Hiermit kann man angeben, welche Felder in den Suchergebnissen enthalten sein sollen. Der Standardwert ist * , was allen Feldern entspricht. Um nur die Felder firstname und lastname in den Suchergebnissen zu berücksichtigen, muss :select auf 'firstname, lastname' gesetzt werden.
- :from
Gibt an, in welcher Tabelle gesucht werden soll. Wird von ActiveRecord automatisch gesetzt.
- :readonly
Wenn diese Option auf true gesetzt wird, können die Suchergebnisse nicht verändert werden.
- :lock
Dient zum Sperren einer Tabelle.
Es können mehrere Optionen, wie im folgenden Beispiel gezeigt, gleichzeitig verwendet werden:
Client.find( :all, :conditions => :lastname=>"Adama"}, :limit => 10, :order =>"firstname" )
Suchbedingung (:conditions)
Meistverwendete Suchoption
Die meistverwendete Suchoption ist :conditions, mit der die Suchbedingung angegeben wird. Die Suchbedingung kann in drei Formaten angegeben werden:
- SQL-Zeichenkette
z. B. Client.find(:all, :conditions => "firstname = 'Lee'")
- SQL-Array
z. B. Client.find(:all, :conditions => ["firstname = ?", 'Lee'])
- Hash
z. B. Client.find(:all, :conditions => [:firstname => 'Lee' ])
Im Folgenden werden die verschiedenen Formate in den Suchbedingungen näher beschrieben:
:conditions mit SQL-Zeichenkette
Client.find(:all, :conditions => "lastname = 'Adama' AND birthday > '1950-01-01'")
Die Bedingung wird von Rails unverändert in die WHERE -Klausel des SQL-Befehls, der intern ausgeführt wird, übernommen:
SELECT * FROM clients WHERE lastname = 'Adama' AND birthday > '1950-01-01'
Sicherheit
Deshalb ist Vorsicht geboten, insbesondere dann, wenn Sie Variablen aus Benutzereingaben einsetzen(siehe Kapitel 17).
lastname = "Adama" firsname = Client.find( :all, :conditions => "lastname = '#lastname' AND birthday > '#birthday'")
Außerdem werden die Variablen gegebenenfalls nicht in den richtigen Typ umgewandelt.
Sicherheitshinweis zu SQL-Injection |
Vermeiden Sie die Verwendung der SQL-Zeichenkette in :conditions , wenn Sie Variablen einsetzen. Es könnten per SQL-Injection unerlaubte SQL-Befehle ausgeführt werden. Verwenden Sie deshalb in diesen Fällen das SQL-Array oder einen Hash in :conditions . Mehr zum Thema Sicherheit erfahren Sie in Kapitel 17 |
:conditions mit SQL-Array
lastname = "Adama" birthday = Date.new(1950,1,1) Client.find( :first, :conditions => ["lastname= ? AND birthday > ?", lastname, birthday])
Alternativ:
Client.find( :first, :conditions => ["lastname= :lname AND birthday > :bday ", :lname => "Adama" , :bday => Date.new(1950,1,1)])
ActiveRecord führt Folgendes automatisch durch:
- Automatische Umwandlung der Datentypen
Zum Beispiel wird der Ruby-Typ Date automatisch in den Datumstyp umgewandelt, der in SQL erwartet wird.
- Zeichenketten werden automatisch in Hochkommas gesetzt
Bei der Verwendung einer SQL-Zeichenkette müssen Zeichenketten manuell mit Hochkommas umschlossen werden:Wenn Sie ein SQL-Array oder einen Hash übergeben, übernimmt ActiveRecord das::conditions=>"lastname = 'Adama'"
:conditions=>[:lastname => var]
- Unerlaubte Zeichen werden geschützt ausgegeben
Unerlaubte Zeichen wie Hochkammas in den Variablen werden automatisch geschützt, um Sicherheitsprobleme z. B. durch eine SQL-Injection zu vermeiden.
:conditions mit Hash
Auf Gleichheit prüfen
Diese Variante ist sehr praktisch, wenn auf Gleichheit geprüft wird. In einem Hash werden die Werte den Feldnamen zugeordnet. Die einzelnen Zuweisungen werden im daraus generierten SQL-Code jeweils mit AND verbunden.
Client.find(:first, :conditions => {:firstname =>'Lee', :lastname => 'Adama'}
Diese Variante ist genauso sicher wie die Übergabe eines SQL-Arrays.
Sortierreihenfolge (:order)
Suchergebnisse werden standardmäßig aufsteigend sortiert. Das heißt, es ist nicht erforderlich, die Sortierreihenfolge anzugeben, wenn Sie Ihr Ergebnis aufsteigend sortieren möchten:
clients = Client.find(:all, :order=>"firstname") clients.map(&:firstname) => ["Kara", "Lee", "William"]
DESC
Um das Suchergebnis absteigend zu sortieren, müssen Sie die Sortierreihenfolge angeben:
clients = Client.find(:all, :order=>"firstname DESC") clients.map(&:firstname) => ["Kara", "Lee", "William"]
ASC
Für eine aufsteigende Sortierung können Sie auch die Reihenfolge ASC angeben.
Es sind auch zusammengesetzte Sortierreihenfolgen möglich. Im folgenden Beispiel soll nach dem Nachnamen sortiert werden. Falls gleiche Nachnamen vorkommen, wird der jüngere Datensatz zuerst gelistet.
clients = Client.find(:all, :order=>"lastname ASC, birthday DESC") clients.map(&:firstname) => ["Lee","William","Kara"]
Wie die Sortierung funktioniert |
Die Zeichenkette, die Sie in der Option :order angeben, wird als SQL-Fragment direkt bei ORDER BY angefügt. Deshalb können Sie im Prinzip alles angeben, was Ihr Datenbanksystem an der Stelle ORDER BY erlaubt. |
Limitieren der Suchergebnisse
:limit
Mit :limit kann die Anzahl der Suchergebnisse eingeschränkt werden.
Um z. B. die ersten 10 Datensätze zu ermitteln, können Sie :limit wie folgt einsetzen:
clients = Client.find(:all, :limit => 10) clients.size => 10
Wenn es weniger Datensätze gibt, als mit der Option :limit angegeben wurde, werden alle verfügbaren Datensätze zurückgeliefert. Es kommt also zu keinem Fehler.
:offset
Mit :offset kann die Position, ab der die Suchergebnisse angezeigt werden sollen, angegeben werden. Die Zählung beginnt dabei bei 0, das heißt, um z. B. beim dritten Datensatz zu beginnen, muss :offset auf 2 gesetzt werden.
clients = Client.find(:all, :offset => 2) clients.map(&:id) => [3,4,5,...]
Kombination
Besonders nützlich ist die Kombination von :limit und :offset.
Sie wird oft für die Anzeige von Suchergebnissen verwendet, wenn zum Beispiel nur eine bestimmte Anzahl von Ergebnissen pro Seite angezeigt werden soll.
Um zum Beispiel die dritte Seite von Suchergebnissen zu zeigen, wenn 10 Ergebnisse pro Seite angezeigt werden, kann man wie folgt vorgehen:
clients = Client.find(:all, :offset => 30, :limit => 10 ) clients.map(&:id) => [31,32,33,...40]
:offset nur in Kombination mit :limit |
Wenn :offset ohne :limit verwendet wird, wird das von ActiveRecord nicht beachtet. Die Option :limit kann hingegen allein verwendet werden. |
Suche mit dynamischen Find-Methoden
In vielen Fällen kann auf die Angabe einer Suchbedingung mit Hilfe der Option :conditions verzichtet werden.
find_by_feldname
Für jedes Feld einer Tabelle steht die Methode find_by_feldname(wert) zur Verfügung, um nach dem entsprechenden Feld zu suchen. Der Befehl
Product.find_by_name("iPod")
Um alle Datensätze zurückzuliefern, die mit dem gesuchten Wert übereinstimmen, kann die Methode find_all_by_feldname(wert) verwendet werden:
Product.find_all_by_name(" iPod")
Es sind auch Verknüpfungen mit and möglich:
Product.find_by_name("iPod") # entspricht Product.find(:first, :conditions => "iPod") Product.find_all_by_name("iPod") # entspricht Product.find(:all, :conditions => "iPod") Product.find_all_by_name_and_price("iPod",149) # entspricht Product.find(:all, :conditions => # ["name = ? AND price = ?", 'iPod',149])
Dynamisch
Diese Find-Methoden werden als dynamisch bezeichnet, weil sie erst beim ihrem Aufruf zur Laufzeit erzeugt werden.
Es ist auch möglich, mit dieser Technik neue Objekte zu erstellen. Mit der Methode find_or_create_by_feldname(wert) wird zunächst eine Suche nach dem entsprechenden Feld durchgeführt. Ist die Suche erfolgreich, wird das entsprechende Objekt zurückgeliefert. Wird kein Datensatz gefunden, wird ein neues Objekt mit dem Wert erstellt.
product = Product.find_or_create_by_name("iPod") # entspricht product = Product.find("iPod") # oder product = Product.create(:name => "iPod") # je nachdem, ob das Objekt gefunden wurde oder nicht
Wenn das Objekt jedoch noch nicht gespeichert werden soll, kann die Methode find_or_initialize_by_feldname(wert) verwendet werden:
product = Product.find_or_initialize_by_name("iPod") # entspricht product = Product.find("iPod") # oder product = Product.new(:name => "iPod") # je nachdem, ob das Objekt gefunden wurde oder nicht
Suche über SQL
find_by_sql
ActiveRecord erlaubt es auch, den SQL-Code direkt für die Suche anzugeben. Dies sollte in der Praxis jedoch nur in Ausnahmefällen erfolgen. Aus Performance-Gründen kann es sinnvoll sein, spezielle SQL-Befehle einzusetzen, die das Datenbanksystem bietet.
Im folgenden Beispiel wird der SQL-Befehl SOUNDS LIKE verwendet, der auch Suchergebnisse findet, die zwar von der Schreibweise verschieden sind, aber ungefähr gleich ausgesprochen werden. Dieser SQL-Befehl kann nur bei Verwendung einer MySQL-Datenbank benutzt werden.
Listing Beispiel für find_by_sql
Product.find_by_sql( "SELECT trim(name) AS name, price FROM products WHERE name SOUNDS LIKE 'ball'") Product.find_by_sql( "SELECT trim(name) AS name, price FROM products WHERE name SOUNDS LIKE ?", 'ball')
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.