Abfragen in Rails, Teil 1

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.

Themen

  • Einzelne Objekte
  • Mehrere Objekte
  • Bedingungen
  • Bestellung
  • Grenzen
  • Gruppe & Haben

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:

Schienen

Agent.where (Name: 'James Bond'). Klasse # => ActiveRecord :: Relation

Einzelne Objekte

  • 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.

Schienen

Anleihe = Agent.find (7)

SQL

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. 

Schienen

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.

SQL

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.

Schienen

bond = Agent.find_by (Nachname: 'Bond')

SQL

SELECT "agents". * FROM "agents" WO "agents". "Last_name" =? LIMIT 1 [["last_name", "Bond"]]

Mehrere Objekte

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

Schienen

mi6_agents = Agents.all

SQL

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

Schienen

NewRecruit.find_each do | recruit | recruit.start_hellweek ende

SQL

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.

Schienen

NewRecruit.find_each (start: 4000, batch_size: 3000) rekrutieren | recruit.start_hellweek ende

SQL

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.

Schienen

NewRecruit.find_in_batches (start: 2700, batch_size: 1350) rekrutiert | field_kitchen.prepare_food (rekruten) ende

Bedingungen

  • 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.

SQL

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:

Schienen

promising_candidates = Recruit.where ("family_status = 'verwaist'")

SQL

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.

Argument Sicherheit

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.

Schienen

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.

Schienen

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)

SQL

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.

Schienen

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.

Schienen

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.

SQL

SELECT "Rekruten". * FROM "Rekruten" WHERE ("Rekruten". "Zeichen"! =?) [["Zeichen", "Feigling"]]

Bestellung

  • 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!

Grenzen

  • 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.

Schienen

five_candidates = Recruit.limit (5) 

SQL

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:

Schienen

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.

Gruppe & Haben

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

Schienen

Kandidatengruppe (: iq)

Wenn wir die Anzahl der Kandidaten zählen, erhalten wir einen sehr nützlichen Hash zurück.

Schienen

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:

SQL

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.

Schienen

Recruit.having ('iq>?', 134) .group (: iq)

SQL

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:

Schienen

Recruit.having ('iq>?', 134) .group (: iq) .count # => 135 => 3, 138 => 2, 140 => 1, 141 => 1

SQL

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. 

Schienen

Recruit.having ('iq>?', 140) .group (: family_status)

SQL

SELECT "Rekruten". * FROM "Rekruten" GROUP BY "Rekruten". "Family_status" HAT Iq> '140'

Das Zählen dieser Gruppen ist jetzt zu einfach:

Schienen

Recruit.having ('iq>?', 140) .group (: family_status) .count # => "verheiratet" => 2, "single" => 1

SQL

SELECT COUNT (*) AS count_all, family_status AS family_status FROM "Rekruten" GROUP BY "Rekruten". "Family_status" HAT Iq> '140'

Abschließende Gedanken

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.