In diesem Artikel lernen Sie die Grundlagen von Active Record-Abfragen und einige Grundlagen zu SQL auf dem Weg. Es richtet sich an Anfänger, die mehr über Datenbankabfragen in Ruby on Rails lernen möchten.
Active Record wird zum Abfragen der Datenbank verwendet. Es kann mit SQL, PostgresSQL und SQLite verwendet werden. Für das Abrufen von Datensätzen aus Ihrer Datenbank stehen Ihnen mehrere Finder-Methoden zur Verfügung. Das Coole an ihnen ist, dass Sie sich die Mühe sparen können, rohe SQL zu schreiben.
Was macht eine Finder-Methode wirklich? Grundsätzlich drei Dinge: Ihre zur Verfügung gestellten Optionen werden in eine SQL-Abfrage umgewandelt. Dann wird die SQL-Abfrage ausgeführt und ruft Daten aus der Datenbank ab. Außerdem erhalten wir für jede Zeile in dieser Ergebnisliste neu instanziierte Ruby-Objekte des Modells, die der Abfrage entsprechen.
Wenn Sie noch nicht mit SQL gespielt haben, versuche ich mein Bestes, um die Dinge einfach zu halten und Sie mit den Grundlagen vertraut zu machen. Folgen Sie den SQL-Beispielen und versuchen Sie, diese einfachen Abfragen zu verstehen. SQL ist keine Hexerei. Die Syntax ist gewöhnungsbedürftig. Dies wird hoffentlich Ihren Appetit anregen, einige nützliche Tutorials zu suchen, die die Lücken füllen.
Schauen wir uns einige Methoden an, die Ihnen zur Verfügung stehen:
finden
zuerst
zuletzt
find_by
alles
find_each
find_in_batches
woher
Auftrag
Grenze
Versatz
Gruppe
mit
Alle werden eine Instanz von zurückgeben ActiveRecord :: Relation
. Ein Was? Es ist eine Klasse, die innerhalb des Namensraums benannt wird Modul ActiveRecord
, und es ermöglicht uns, mehrere Abfragemethoden aufzurufen und sie zu verketten. Dieses Objekt ist das Herzstück der in Rails verwendeten Abfragesyntax. Schauen wir uns die Klasse eines solchen Objekts an und überzeugen Sie sich selbst:
Agent.where (Name: 'James Bond'). Klasse # => ActiveRecord :: Relation
finden
Mit dieser Methode können Sie die primäre ID eines Objekts angeben und dieses Objekt für Sie abrufen. Wenn Sie ein Array von IDs angeben, können Sie auch mehrere Objekte abrufen.
Anleihe = Agent.find (7)
SELECT "agents". * FROM "agents" WO "agents". "Id" =? LIMIT 1 [["id", 7]]
Diese SQL-Zeile gibt an, dass Sie alle auswählen möchten (*
) Attribute aus der Agenten
Tabelle und "filtern" nur den Datensatz mit der ID 7. Bei einer Begrenzung wird nur ein einzelner Datensatz aus der Datenbank zurückgegeben.
zuerst
, zuletzt
Es ist nicht überraschend, dass Sie die ersten und letzten Datensätze erhalten, die anhand ihres Primärschlüssels identifiziert werden können. Der interessante Teil ist jedoch, dass Sie eine optionale Nummer angeben können, die Ihnen den ersten oder letzten Datensatz dieser Anzahl von Datensätzen zurückgibt.
enemy_agents = SpectreAgent.first (10) enemy_agents = SpectreAgent.last (10)
Unter der Haube geben Sie ein neues Limit für die angegebene Anzahl an und ordnen sie aufsteigend oder absteigend an.
SELECT "spectreagents". * FROM "spectreagents" ORDER BY "spectreagents". "Id" ASC LIMIT 10 SELECT "spectreagents". * FROM "spectreagents" ORDER BY "spectreagents". "Id" DESC LIMIT 10
find_by
Dieser Finder gibt das erste Objekt zurück, das der von Ihnen angegebenen Bedingung entspricht.
bond = Agent.find_by (Nachname: 'Bond')
SELECT "agents". * FROM "agents" WO "agents". "Last_name" =? LIMIT 1 [["last_name", "Bond"]]
Offensichtlich müssen wir oft eine Sammlung von Objekten mit einem gewissen Zeitplan durchlaufen. Das Abrufen eines einzelnen Objekts oder einiger ausgewählter Objekte von Hand ist schön, aber in den meisten Fällen möchten wir, dass Active Record Objekte in Batches abruft.
Die Anzeige aller Arten von Benutzern ist für die meisten Rails-Apps das A und O. Was wir brauchen, ist ein leistungsfähiges Werkzeug mit einer bequemen API, um diese Objekte für uns hoffentlich so zu sammeln, dass wir die betroffene SQL meist nicht selbst schreiben.
alles
mi6_agents = Agents.all
SELECT "Agenten". * FROM "Agenten"
Diese Methode ist praktisch für relativ kleine Objektkollektionen. Stellen Sie sich vor, dies auf einer Sammlung aller Twitter-Benutzer durchzuführen. Nein, keine gute Idee. Was wir stattdessen wollen, ist ein besser abgestimmter Ansatz für größere Tischgrößen.
Das Abrufen der gesamten Tabelle wird nicht skaliert! Warum? Da wir nicht nur nach einer Reihe von Objekten fragen würden, müssten wir auch ein Objekt pro Zeile in dieser Tabelle erstellen und in einem Array im Speicher ablegen. Ich hoffe das klingt nicht nach einer guten Idee! Also, was ist die Lösung dafür? Chargen! Wir unterteilen diese Sammlungen in Stapel, deren Speicher für die Verarbeitung einfacher ist. Woohoo!
Lass uns einen Blick darauf werfen find_each
und find_in_batches
. Beide sind sich ähnlich, verhalten sich jedoch unterschiedlich, wenn sie Objekte in Blöcke bringen. Sie akzeptieren eine Option zur Regulierung der Chargengröße. Der Standardwert ist 1.000.
find_each
NewRecruit.find_each do | recruit | recruit.start_hellweek ende
SELECT "newrecruits". * FROM "newrecruits" ORDER BY "newrecruits". "Id" ASC LIMIT 1000
In diesem Fall holen wir eine Standardcharge von 1.000 neuen Rekruten ab, übergeben sie an den Block und schicken sie Woche für Woche in die Hölle. Da Batches Sammlungen aufteilen, können wir ihnen auch mitteilen, wo sie über starten sollen Start
. Nehmen wir einmal an, wir wollen 3.000 mögliche Rekruten auf einmal bearbeiten und bei 4.000 beginnen.
NewRecruit.find_each (start: 4000, batch_size: 3000) rekrutieren | recruit.start_hellweek ende
SELECT "newrecruits". * FROM "newrecruits" WHERE ("newrecruits". "Id"> = 4000) ORDER BY "newrecruits". "Id" ASC LIMIT 3000
Um dies zu wiederholen, rufen wir zuerst einen Stapel von 3.000 Ruby-Objekten ab und senden sie dann in den Block. Start
können wir die ID der Datensätze angeben, bei denen der Stapel abgerufen werden soll.
find_in_batches
Dieser gibt seinen Stapel als Array an den Block aus. Er gibt ihn an ein anderes Objekt weiter, das sich lieber mit Sammlungen befasst. Die SQL ist hier gleich.
NewRecruit.find_in_batches (start: 2700, batch_size: 1350) rekrutiert | field_kitchen.prepare_food (rekruten) ende
woher
Wir müssen rübergehen woher
bevor wir weiter fahren. Auf diese Weise können Sie Bedingungen angeben, die die Anzahl der von unseren Abfragen zurückgegebenen Datensätze begrenzen. Ein Filter, mit dem "wo" Datensätze aus der Datenbank abgerufen werden. Wenn Sie mit SQL gespielt haben WOHER
Klauseln, dann fühlen Sie sich bei diesem Ruby-Wrapper vielleicht gleich wie zu Hause.
In SQL können wir so festlegen, auf welche Tabellenzeile wir Einfluss nehmen möchten, im Wesentlichen dort, wo sie bestimmte Kriterien erfüllt. Dies ist übrigens eine optionale Klausel. In der Raw-SQL unten wählen wir nur Rekruten aus, die über Waisenkinder sind WOHER
.
Wählen Sie eine bestimmte Zeile aus einer Tabelle aus.
SELECT * FROM Rekruten WHERE FamilyStatus = 'Orphan';
Über woher
, Sie können Bedingungen mit Strings, Hashes oder Arrays angeben. Wenn Sie all dies zusammenfassen, können Sie mit Active Record nach folgenden Bedingungen filtern:
promising_candidates = Recruit.where ("family_status = 'verwaist'")
SELECT "Rekruten". * FROM "Rekruten" WO (family_status = 'Waise')
Ziemlich ordentlich, richtig? Ich möchte erwähnen, dass dies immer noch eine Suchoperation ist. Wir geben nur an, wie wir diese Liste sofort filtern möchten. Aus der Liste aller Rekruten wird eine gefilterte Liste verwaister Kandidaten zurückgegeben. Dieses Beispiel ist eine Zeichenfolgenbedingung. Vermeiden Sie reine String-Bedingungen, da sie aufgrund ihrer Anfälligkeit für SQL-Injektionen nicht als sicher gelten.
Im obigen Beispiel setzen wir die Waise
Variable in den String mit den Bedingungen. Dies wird als schlechte Praxis betrachtet, weil sie unsicher ist. Wir müssen der Variablen entgehen, um diese Sicherheitsanfälligkeit zu vermeiden. Sie sollten sich über die SQL-Injection informieren, wenn dies eine vollständige Neuigkeit für Sie ist - Ihre Datenbank könnte davon abhängen.
promising_candidates = Recruit.where ("family_status =?", "verwaist" ")
Das ?
wird als Bedingungswert durch den nächsten Wert in der Argumentliste ersetzt. Das Fragezeichen ist also im Grunde ein Platzhalter. Sie können auch mehrere Bedingungen mit mehreren angeben ?
und ketten sie zusammen. In einem realen Szenario würden wir einen Params-Hash wie folgt verwenden:
promising_candidates = Recruit.where ("family_status =?", params [: Rekruten])
Wenn Sie viele variable Bedingungen haben, sollten Sie Platzhalterbedingungen für Schlüssel / Werte verwenden.
promising_candidates = Recruit.where ("family_status =: preferred_status AND iq> =: required_iq AND charming =: lady_killer", Preferred_status: 'verwaist', required_iq: 140, lady_killer: true)
SELECT "Rekruten". * FROM "Rekruten" WO (family_status = 'Waisenkind' UND iq> = 140 AND lady_killer = true)
Das obige Beispiel ist natürlich dumm, aber es zeigt deutlich die Vorteile der Platzhalter-Notation. Die Hash-Notation ist im Allgemeinen definitiv die lesbarere.
promising_candidates = Recruit.where (familienstatus: 'verwaist') promising_candidates = Recruit.where ('charmant': wahr)
Wie Sie sehen, können Sie mit Symbolen oder Zeichenfolgen zu Ihnen gehen. Schließen Sie diesen Abschnitt mit Bereichen und negativen Bedingungen über NOT.
promising_candidates = Recruit.where (Geburtstag: ('1994-01-01'… '2000-01-01'))
Zwei Punkte und Sie können jeden Bereich festlegen, den Sie benötigen.
promising_candidates = Recruit.where.not (Zeichen: 'Feigling')
Sie können das verstauen nicht
auf die woher
um alle Feiglinge herauszufiltern und nur Ergebnisse zu erhalten, die nicht über dieses spezifische, unerwünschte Attribut verfügen. Unter der Haube a !=
negiert den WHERE-Filter.
SELECT "Rekruten". * FROM "Rekruten" WHERE ("Rekruten". "Zeichen"! =?) [["Zeichen", "Feigling"]]
Auftrag
Um dich nicht zu Tode zu langweilen, lass uns das schnell machen.
Kandidaten = Recruit.order (: date_of_birth)
Kandidaten = Recruit.order (: date_of_birth,: desc)
Sich bewerben : asc
oder : absteigend
entsprechend sortieren. Das ist es im Grunde, also lasst uns weitergehen!
Grenze
Sie können die Anzahl der zurückgegebenen Datensätze auf eine bestimmte Anzahl reduzieren. Wie bereits erwähnt, benötigen Sie meist nicht alle zurückgegebenen Datensätze. Das folgende Beispiel zeigt Ihnen die ersten fünf Rekruten in der Datenbank - die ersten fünf IDs.
five_candidates = Recruit.limit (5)
SELECT "Rekruten". * FROM "Rekruten" LIMIT 5
Versatz
Wenn Sie sich jemals gefragt haben, wie die Paginierung unter der Haube funktioniert?, Grenze
und Versatz
-in zusammenarbeit die harte arbeit. Grenze
kann alleine stehen, aber Versatz
hängt von der ersteren ab.
Das Festlegen eines Versatzes ist vor allem für die Paginierung hilfreich. Sie können die gewünschte Anzahl von Zeilen in der Datenbank überspringen. Die zweite Seite einer Liste von Kandidaten könnte folgendermaßen aussehen:
Recruit.limit (20). Versatz (20)
Die SQL würde so aussehen:
WÄHLEN Sie "Rekruten" aus. * FROM "Rekruten" LIMIT 20 OFFSET 20
Wieder wählen wir alle Spalten aus Rekrutieren
Datenbankmodell, wobei die zurückgegebenen Datensätze auf 20 Ruby-Objekte von Class Recruit begrenzt werden und die ersten 20 übersprungen werden.
Nehmen wir an, wir möchten eine Liste von Rekruten, die nach ihren IQs gruppiert sind. In SQL könnte dies ungefähr so aussehen.
SELECT "Rekruten". * FROM "Rekruten" GROUP BY "Rekruten". "Iq"
Dies würde Ihnen eine Liste geben, in der Sie sehen, welche möglichen Rekruten einen IQ von 120 haben, und dann eine weitere Gruppe von 140, usw. - was auch immer ihre IQs sind und wie viele unter eine bestimmte Anzahl fallen würden. Wenn zwei Rekruten den gleichen IQ von 130 haben, würden sie zusammen gruppiert.
Eine andere Liste könnte nach möglichen Kandidaten gruppiert werden, die an Platzangst oder Höhenangst leiden oder medizinisch nicht tauchfähig sind. Die Active Record-Abfrage würde einfach so aussehen:
Gruppe
Kandidatengruppe (: iq)
Wenn wir die Anzahl der Kandidaten zählen, erhalten wir einen sehr nützlichen Hash zurück.
Kandidatengruppe (: iq) .Zahl # => 130 => 7, 134 => 4, 135 => 3, 138 => 2, 140 => 1, 141 => 1
Wir haben sieben mögliche Rekruten mit einem IQ von 130 und nur einen mit 141. Die resultierende SQL würde folgendermaßen aussehen:
SELECT COUNT (*) AS count_all, iq AS iq FROM "Kandidaten" GROUP BY "Kandidaten". "Iq"
Das wichtige Stück ist das GRUPPIERE NACH
Teil. Wie Sie sehen, verwenden wir die Kandidatentabelle, um ihre IDs zu erhalten. Dieses einfache Beispiel zeigt auch, wie bequem die Active Record-Versionen lesen und schreiben. Stellen Sie sich vor, dies anhand von extravaganten Beispielen von Hand zu tun. Sicher, manchmal muss man, aber die ganze Zeit ist eindeutig ein Schmerz, den wir gerne vermeiden können.
mit
Wir können diese Gruppe noch spezifizieren, indem Sie verwenden HABEN
-eine Art Filter für die Gruppe
. In diesem Sinne, mit
ist eine Art von WOHER
Klausel für GRUPPE
. Mit anderen Worten, mit
ist abhängig von der Verwendung Gruppe
.
Recruit.having ('iq>?', 134) .group (: iq)
SELECT "Rekruten". * FROM "Rekruten" GROUP BY "Rekruten". "Iq" mit iq> '134'
Wir haben unsere Kandidaten nun in Listen mit Personen zusammengefasst, die einen Mindest-IQ von 135 haben. Lassen Sie uns sie zählen, um einige Statistiken zu erhalten:
Recruit.having ('iq>?', 134) .group (: iq) .count # => 135 => 3, 138 => 2, 140 => 1, 141 => 1
SELECT COUNT (*) AS count_all, iq AS iq FROM "rekrutiert" GROUP BY "rekrutiert". "Iq" mit iq> '134'
Wir könnten diese auch kombinieren und beispielsweise feststellen, welche Kandidaten, deren IQs über 140 liegen, in Beziehungen gebunden sind oder nicht.
Recruit.having ('iq>?', 140) .group (: family_status)
SELECT "Rekruten". * FROM "Rekruten" GROUP BY "Rekruten". "Family_status" HAT Iq> '140'
Das Zählen dieser Gruppen ist jetzt zu einfach:
Recruit.having ('iq>?', 140) .group (: family_status) .count # => "verheiratet" => 2, "single" => 1
SELECT COUNT (*) AS count_all, family_status AS family_status FROM "Rekruten" GROUP BY "Rekruten". "Family_status" HAT Iq> '140'
Ich hoffe, dass dies ein nützlicher erster Blick darauf war, was Active Record zu bieten hat, um Ihre Abfrageanstrengungen so lesbar und bequem wie möglich zu gestalten. Alles in allem würde ich sagen, dass es ein hervorragender Wrapper ist, der Sie meistens davon abhält, SQL von Hand zu schreiben.
Im nächsten Artikel werden wir uns ein paar ausführlichere Finder ansehen und das bisher Erlernte erweitern.