Abfragen in Rails, Teil 2

In diesem zweiten Artikel werden wir uns näher mit Active Record-Abfragen in Rails beschäftigen. Falls Sie noch nicht mit SQL vertraut sind, füge ich Beispiele hinzu, die so einfach sind, dass Sie die Syntax ein wenig mitnehmen und die Syntax ein wenig aufgreifen können. 

Es wäre jedoch definitiv hilfreich, wenn Sie ein kurzes SQL-Lernprogramm durchlaufen, bevor Sie zum Lesen zurückkehren. Nehmen Sie sich andernfalls Zeit, die von uns verwendeten SQL-Abfragen zu verstehen, und ich hoffe, dass es am Ende dieser Serie nicht mehr einschüchternd sein wird. 

Das meiste ist wirklich unkompliziert, aber die Syntax ist ein bisschen komisch, wenn Sie gerade mit der Codierung begonnen haben - insbesondere in Ruby. Bleib dran, es ist keine Raketenwissenschaft!

Themen

  • Enthält & Eifriges Laden
  • Tabellen verbinden
  • Eifriges Laden
  • Bereiche
  • Aggregationen
  • Dynamische Finder
  • Spezifische Felder
  • Benutzerdefinierte SQL

Enthält & Eifriges Laden

Diese Abfragen enthalten mehr als eine Datenbanktabelle, mit der Sie arbeiten können. Dies ist möglicherweise die wichtigste Information, die Sie diesem Artikel entnehmen können. Es läuft darauf hinaus: Anstatt mehrere Abfragen nach Informationen durchzuführen, die auf mehrere Tabellen verteilt sind, beinhaltet versucht, diese auf ein Minimum zu beschränken. Das Schlüsselkonzept dahinter heißt "eifriges Laden" und bedeutet, dass wir verknüpfte Objekte laden, wenn wir eine Suche durchführen.

Wenn wir das getan hätten, indem wir eine Sammlung von Objekten durchlaufen und dann versucht hätten, auf die zugehörigen Datensätze einer anderen Tabelle zuzugreifen, würden wir auf ein Problem stoßen, das als "N + 1-Abfrageproblem" bezeichnet wird. Zum Beispiel für jeden agent.handler In einer Sammlung von Agenten würden wir sowohl für Agenten als auch für ihre Handler separate Abfragen starten. Das ist es, was wir vermeiden müssen, da dies überhaupt nicht skaliert wird. Stattdessen machen wir Folgendes:

Schienen

agents = Agent.includes (: Handler)

Wenn wir jetzt über eine solche Sammlung von Agenten iterieren, die die Anzahl der zurückgegebenen Datensätze nicht begrenzt hat, erhalten wir zwei Abfragen statt möglicherweise nur einer Gazillion. 

SQL

SELECT "Agenten". * FROM "Agenten" SELECT "Handler". * FROM "Handler" WHERE "Handler". "ID" IN (1, 2)

Dieser eine Agent in der Liste verfügt über zwei Handler. Wenn wir nun das Agentenobjekt nach seinen Handlern fragen, müssen keine zusätzlichen Datenbankabfragen ausgelöst werden. Wir können dies natürlich noch einen Schritt weiter gehen und eifrig mehrere verknüpfte Tabellendatensätze laden. Wenn wir nicht nur Handler, sondern auch die damit verbundenen Aufgaben des Agenten aus irgendeinem Grund laden müssten, könnten wir dies nutzen beinhaltet so was.

Schienen

agents = Agent.includes (: Handler,: Mission)

Einfach! Seien Sie vorsichtig bei der Verwendung von Singular- und Pluralversionen für die Includes. Sie hängen von Ihren Modellassoziationen ab. EIN hat viele Assoziation verwendet Plural, während a gehört oder ein has_one braucht natürlich die singuläre Version. Wenn Sie möchten, können Sie auch eine woher Klausel für das Angeben zusätzlicher Bedingungen. Die bevorzugte Methode zum Angeben von Bedingungen für verknüpfte Tabellen, die gerne geladen werden, ist die Verwendung von schließt sich an stattdessen. 

Beim eifrigen Laden ist zu beachten, dass die hinzugefügten Daten vollständig an Active Record zurückgesendet werden, wodurch wiederum Ruby-Objekte mit diesen Attributen erstellt werden. Dies steht im Gegensatz zum „einfachen“ Zusammenfügen der Daten, bei dem Sie ein virtuelles Ergebnis erhalten, das Sie beispielsweise für Berechnungen verwenden können, und weniger Speicherplatz beanspruchen wird als Einschlüsse.

Tabellen verbinden

Das Verbinden von Tabellen ist ein weiteres Tool, mit dem Sie vermeiden können, dass zu viele unnötige Abfragen in der Pipeline gesendet werden. Ein häufiges Szenario verbindet zwei Tabellen mit einer einzigen Abfrage, die eine Art kombinierter Datensatz zurückgibt. schließt sich an ist nur eine andere Finder-Methode von Active Record, mit der Sie SQL-Begriffe eingeben können-BEITRETEN Tische. Diese Abfragen können Datensätze aus mehreren Tabellen zurückgeben, und Sie erhalten eine virtuelle Tabelle, die Datensätze aus diesen Tabellen kombiniert. Dies ist ziemlich radikal, wenn Sie das vergleichen, anstatt alle Arten von Abfragen für jede Tabelle auszulösen. Bei diesem Ansatz können verschiedene Arten von Datenüberlappungen auftreten. 

Der innere Join ist der Standardmodus für schließt sich an. Dies stimmt mit allen Ergebnissen überein, die mit einer bestimmten ID und deren Darstellung als Fremdschlüssel eines anderen Objekts oder einer anderen Tabelle übereinstimmen. Im folgenden Beispiel einfach ausgedrückt: Gib mir alle Missionen, bei denen die Mission ist Ich würde zeigt sich als mission_id in einem Tisch eines Agenten. "agents". "mission_id" = "Missionen". "id". Inner Joins schließen Beziehungen aus, die nicht existieren.

Schienen

Mission.joins (: Agenten)

SQL

SELECT "Missionen". * FROM "Missionen" INNER JOIN "Agenten" ON "Agenten." Mission_id "=" mission "." Id "

Wir passen also Missionen und die dazugehörigen Agenten an - in einer einzigen Abfrage! Sicher, wir könnten die Missionen zuerst bekommen, eine nach der anderen durchlaufen und nach ihren Agenten fragen. Aber dann kehren wir zu unserem schrecklichen "N + 1-Abfrageproblem" zurück. Nein danke! 

Das Schöne an diesem Ansatz ist, dass wir keine Fälle mit inneren Verknüpfungen erhalten. Es werden nur Datensätze zurückgegeben, die ihre IDs mit Fremdschlüsseln in zugeordneten Tabellen übereinstimmen. Wenn wir zum Beispiel Missionen finden müssen, denen es an Agenten fehlt, brauchen wir stattdessen eine äußere Verknüpfung. Da dies derzeit das Schreiben von eigenen beinhaltet ÄUSSERE JOIN SQL, wir werden das im letzten Artikel untersuchen. Zurück zu den Standard-Joins können Sie natürlich auch mehrere verknüpfte Tabellen verbinden.

Schienen

Mission.joins (: Agenten,: Kosten,: Handler)

Und Sie können einige hinzufügen woher Klauseln, um Ihre Finder noch genauer anzugeben. Nachfolgend suchen wir nur nach Missionen, die von James Bond ausgeführt werden, und nur nach Agenten, die zur Mission 'Moonraker' im zweiten Beispiel gehören.

Mission.joins (: agents) .where (agents: Name: 'James Bond')

SQL

SELECT "Missionen". * FROM "Missionen" INNER JOIN "Agenten" ON "Agenten." Mission_id "=" Missionen "." ID "WO" Agenten "." Name "=? [["" Name "," James Bond "]]

Schienen

Agent.joins (: mission) .where (Missionen: mission_name: 'Moonraker')

SQL

SELECT "agents". * FROM "agents" INNER JOIN "-Missionen" ON "-Missionen". "Id" = "Agenten". "Mission_id" WO "-Missionen". "Mission_name" =? [["mission_name", "Moonraker"]]

Mit schließt sich an, Sie müssen auch darauf achten, dass Ihre Modellassoziationen im Singular und Plural verwendet werden. Weil mein Mission Klasse has_many: Agenten, Wir können den Plural verwenden. Auf der anderen Seite für die Agent Klasse gehört_zu: Mission, nur die singuläre version funktioniert ohne zu sprengen. Wichtiges kleines Detail: der woher Teil ist einfacher. Da Sie in der Tabelle nach mehreren Zeilen suchen, die eine bestimmte Bedingung erfüllen, ist die Pluralform immer sinnvoll.

Bereiche

Bereiche sind eine praktische Methode, um gängige Abfragen in benannten Methoden zu extrahieren. Auf diese Weise sind sie ein bisschen einfacher zu umgehen und möglicherweise auch leichter zu verstehen, wenn andere mit Ihrem Code arbeiten müssen oder wenn Sie in Zukunft bestimmte Abfragen erneut aufrufen müssen. Sie können sie für einzelne Modelle definieren, aber auch für ihre Assoziationen verwenden. 

Der Himmel ist wirklich die Grenze-schließt sich an, beinhaltet, und woher sind alle faires Spiel! Da kommen auch die Bereiche zurück ActiveRecord :: Relations Sie können Objekte ketten und andere Bereiche ohne Bedenken aufrufen. Solche Bereiche zu extrahieren und für komplexere Abfragen zu verketten, ist sehr praktisch und macht längere umso lesbarer. Bereiche werden über die Syntax "Stabby Lambda" definiert:

Schienen

Klasse Mission < ActiveRecord::Base has_many: agents scope :successful, -> where (mission_complete: true) Ende Mission.successful
Klasse Agent < ActiveRecord::Base belongs_to :mission scope :licenced_to_kill, -> where (licence_to_kill: true) scope: womanizer, -> where (womanizer: true) scope: gambler, -> where (gambler: true) Ende # Agent.gambler # Agent.womanizer # Agent.licenced_to_kill # Agent.womanizer.gambler Agent.licenced_to_kill.womanizer.gambler

SQL

SELECT "agents". * FROM "agents" WO "agents". "Licence_to_kill" =? UND "Agenten". "Womanizer" =? UND "Agenten". "Spieler" =? [["" licence_to_kill "," t "], [" womanizer "," t "], [" gambler "," t "]]

Wie Sie im obigen Beispiel sehen können, ist es viel schöner, James Bond zu finden, wenn Sie nur Zielfernrohre miteinander verketten können. Auf diese Weise können Sie verschiedene Abfragen kombinieren und gleichzeitig TROCKEN bleiben. Wenn Sie Bereiche über Assoziationen benötigen, stehen Ihnen diese ebenfalls zur Verfügung:

Mission.last.agents.licenced_to_kill.womanizer.gambler
SELECT "Missionen". * FROM "Missionen" ORDER BY "Missionen". "ID" DESC LIMIT 1 SELECT "Agenten". * FROM "Agenten" WO "Agenten". "Mission_id" =? UND "Agenten". "Licence_to_kill" =? UND "Agenten". "Womanizer" =? UND "Agenten". "Spieler" =? [["mission_id", 33], ["licence_to_kill", "t"], ["womanizer", "t"], ["gambler", "t"]]

Sie können das auch neu definieren default_scope für, wenn Sie so etwas suchen Mission.all.

Klasse Mission < ActiveRecord::Base default_scope  where status: "In progress"  end Mission.all

SQL

 SELECT "Missionen". * FROM "Missionen" WO "Missionen". "Status" =? [["Status in Bearbeitung"]]

Aggregationen

Dieser Abschnitt ist in Bezug auf das Verständnis nicht so weit fortgeschritten, aber Sie werden sie in Szenarien, die als etwas fortgeschrittener betrachtet werden können als der Durchschnitt eines Finders, mehr als nötig .alles, .zuerst, .find_by_id oder Wasauchimmer. Beispielsweise ist das Filtern basierend auf grundlegenden Berechnungen höchstwahrscheinlich etwas, mit dem sich Neulinge nicht sofort in Verbindung setzen. Was betrachten wir genau hier??

  • Summe
  • Anzahl
  • Minimum
  • maximal
  • durchschnittlich

Leichter Erbsen, richtig? Die coole Sache ist, dass wir Active Record nicht für eine ganze Reihe von Objekten durchlaufen müssen, um diese Berechnungen durchzuführen, sondern all diese Arbeit für uns erledigen und diese Ergebnisse vorzugsweise zusammen mit den Abfragen zurückgeben. Schön, huh?

  • Anzahl

Schienen

Mission.count # => 24

SQL

WÄHLEN SIE COUNT (*) VON "Missionen"
  • durchschnittlich

Schienen

Agent.average (: number_of_gadgets) .to_f # => 3.5

SQL

SELECT AVG ("agents". "Number_of_gadgets") FROM "agents"

Da wissen wir jetzt, wie wir davon Gebrauch machen können schließt sich an, Wir können diesen Schritt noch weiter gehen und nur nach dem Durchschnitt der Gadgets fragen, die die Agenten beispielsweise für eine bestimmte Mission haben.

Schienen

Agent.joins (: mission) .where (Missionen: Name: 'Moonraker'). Durchschnitt (: number_of_gadgets) .to_f # => 3.4

SQL

SELECT AVG ("agents". "Number_of_gadgets") FROM "Agenten" INNER JOIN "-Missionen" ON "-Missionen". "Id" = "Agenten". "Mission_id" WO "-Missionen". "Name" =? [["" name "," Moonraker "]]

Das Gruppieren dieser durchschnittlichen Anzahl von Gadgets nach den Namen der Missionen wird an diesem Punkt trivial. Weitere Informationen zur Gruppierung finden Sie unten:

Schienen

Agent.joins (: mission) .group ('missions.name'). Average (: number_of_gadgets)

SQL

SELECT AVG ("agents". "Number_of_gadgets") AS average_number_ofadgets, missions.name AS missions_name FROM "Agenten" INNER JOIN "-Missionen" ON "-Missionen". "Id" = "Agenten". "Mission_id" GROUP BY missions.name
  • Summe

Schienen

Agent.sum (: number_of_gadgets) Agent.where (licence_to_kill: true) .sum (: number_of_gadgets) Agent.where.not (licence_to_kill: true) .sum (: number_of_gadgets)

SQL

SELECT SUM ("agents". "Number_of_gadgets") FROM "Agenten" SELECT SUM ("agents". "Number_of_gadgets") FROM "Agents" WHERE "Agents". "Licence_to_kill" =? [["licence_to_kill", "t"]] SELECT SUM ("agents". "number_of_gadgets") FROM "agents" WHERE ("agents". "licence_to_kill"! =?) [["" licence_to_kill "," t "]]
  • maximal

Schienen

Agent.maximum (: number_of_gadgets) Agent.where (licence_to_kill: true) .maximum (: number_of_gadgets) 

SQL

SELECT MAX ("agents". "Number_of_gadgets") FROM "Agents" SELECT MAX ("agents". "Number_of_gadgets") FROM "Agents" WHERE Agents "." Licence_to_kill "=? [["" licence_to_kill "," t "]]
  • Minimum

Schienen

Agent.minimum (: iq) Agent.where (licence_to_kill: true) .minimum (: iq) 

SQL

SELECT MIN ("Agenten". "Iq") FROM "Agenten" SELECT MIN ("Agents". "Iq") FROM "Agenten" WO "Agenten". "Licence_to_kill" =? [["" licence_to_kill "," t "]]

Beachtung!

Alle diese Aggregationsmethoden lassen Sie nicht an andere Dinge ketten - sie sind endgültig. Die Reihenfolge ist wichtig, um Berechnungen durchzuführen. Wir bekommen keine ActiveRecord :: Relation Objekt zurück von diesen Operationen, wodurch die Musik an diesem Punkt stoppt. Stattdessen erhalten wir einen Hash oder Zahlen. Die folgenden Beispiele funktionieren nicht:

Schienen

Agent.maximum (: number_of_gadgets) .where (licence_to_kill: true) Agent.sum (: number_of_gadgets) .where.not (licence_to_kill: true) Agent.joins (: mission) .average (: number_of_gadgets) .group ('missions.name.) ')

Gruppiert

Wenn Sie möchten, dass die Berechnungen in logische Gruppen unterteilt und sortiert werden, sollten Sie a verwenden GRUPPE Klausel und nicht in Ruby. Ich meine damit, dass Sie es vermeiden sollten, über eine Gruppe zu iterieren, die potenziell Tonnen von Anfragen produziert.

Schienen

Agent.joins (: mission) .group ('missions.name'). Average (: number_of_gadgets) # => "Moonraker" => 4.4, "Octopussy" => 4.9

SQL

SELECT AVG ("agents". "Number_of_gadgets") AS average_number_ofadgets, missions.name AS missions_name FROM "Agenten" INNER JOIN "-Missionen" ON "-Missionen". "Id" = "Agenten". "Mission_id" GROUP BY missions.name

In diesem Beispiel werden alle Agenten gefunden, die einer bestimmten Mission gruppiert sind, und sie geben einen Hashwert mit der berechneten durchschnittlichen Anzahl an Gadgets als Werte in einer einzigen Abfrage zurück. Jep! Dasselbe gilt natürlich auch für die anderen Berechnungen. In diesem Fall ist es wirklich sinnvoller, SQL die Arbeit erledigen zu lassen. Die Anzahl der Anfragen, die wir für diese Aggregationen auslösen, ist einfach zu wichtig.

Dynamische Finder

Sagen Sie für jedes Attribut an Ihren Modellen Name, E-Mail-Addressefavorite_gadget usw. Mit Active Record können Sie sehr lesbare Suchmethoden verwenden, die dynamisch für Sie erstellt werden. Hört sich kryptisch an, ich weiß, aber es bedeutet nichts anderes als find_by_id oder find_by_favorite_gadget. Das find_by Teil ist Standard, und Active Record berücksichtigt nur den Namen des Attributs für Sie. Sie können sogar eine hinzufügen ! Wenn Sie möchten, dass der Finder einen Fehler ausgibt, wenn nichts gefunden werden kann. Der kritische Teil ist, Sie können diese dynamischen Suchmethoden sogar miteinander verketten. Genau wie dieser:

Schienen

Agent.find_by_name ('James Bond') Agent.find_by_name_and_licence_to_kill ('James Bond', true)

SQL

SELECT "Agenten". * FROM "Agenten" WO "Agenten". "Name" =? LIMIT 1 [["Name", "James Bond"]] SELECT "Agenten". * FROM "Agenten" WO "Agenten". "Name" =? UND "Agenten". "Licence_to_kill" =? LIMIT 1 [["name", "James Bond"], ["licence_to_kill", "t"]]

Natürlich können Sie damit verrückt werden, aber ich denke, es verliert seinen Charme und seine Nützlichkeit, wenn Sie über zwei Eigenschaften hinausgehen:

Schienen

Agent.find_by_name_and_licence_to_kill_and_womanizer_and_gambler_and_number_of_gadgets ('James Bond', true, true, true, 3) 

SQL

SELECT "Agenten". * FROM "Agenten" WO "Agenten". "Name" =? UND "Agenten". "Licence_to_kill" =? UND "Agenten". "Womanizer" =? UND "Agenten". "Spieler" =? UND "Agenten". "Number_of_gadgets" =? LIMIT 1 [["name", "James Bond"], ["licence_to_kill", "t"], ["womanizer", "t"], ["gambler", "t"], ["number_of_gadgets", 3 ]]

In diesem Beispiel ist es trotzdem schön zu sehen, wie es unter der Haube funktioniert. Jeder neue _und_ fügt eine SQL hinzu UND Operator, um die Attribute logisch miteinander zu verknüpfen. Insgesamt ist der Hauptvorteil von dynamischen Suchern, dass Lesbarkeit bei zu vielen dynamischen Attributen diesen Vorteil schnell verliert. Ich benutze das selten, vielleicht meistens, wenn ich in der Konsole spiele, aber es ist auf jeden Fall gut zu wissen, dass Rails diesen kleinen Trick bietet.

Spezifische Felder

Active Record bietet Ihnen die Möglichkeit, Objekte zurückzugeben, die sich etwas mehr auf die Attribute konzentrieren, die sie tragen. Wenn nicht anders angegeben, fragt die Abfrage normalerweise alle Felder in einer Zeile über * (SELECT "Agenten". *) und Active Record erstellt dann Ruby-Objekte mit allen Attributen. Sie können jedoch wählen Nur bestimmte Felder, die von der Abfrage zurückgegeben werden sollen und die Anzahl der Attribute begrenzen, die Ihre Ruby-Objekte zum Mitnehmen benötigen.

Schienen

Agent.select ("name") => #, #,…]>

SQL

SELECT "Agenten". "Name" FROM "Agenten"

Schienen

Agent.select ("number, favorite_gadget") => #, #,…]>

SQL

SELECT "agents". "Number", "agents". "Favorite_gadget" FROM "agents"

Wie Sie sehen, haben die zurückgegebenen Objekte nur die ausgewählten Attribute und natürlich ihre IDs - das ist bei jedem Objekt gegeben. Es macht keinen Unterschied, ob Sie wie oben Strings oder Symbole verwenden - die Abfrage ist die gleiche.

Schienen

Agent.select (: number_of_kills) Agent.select (: name,: licence_to_kill)

Ein Wort der Vorsicht: Wenn Sie versuchen, auf Attribute des Objekts zuzugreifen, das Sie in Ihren Abfragen nicht ausgewählt haben, erhalten Sie eine MissingAttributeError. Seit der Ich würde wird automatisch für Sie bereitgestellt, Sie können jedoch nach der ID fragen, ohne diese auszuwählen.

Benutzerdefinierte SQL

Nicht zuletzt können Sie Ihre eigene benutzerdefinierte SQL über schreiben find_by_sql. Wenn Sie sich auf Ihr eigenes SQL-Fu vertrauen und einige benutzerdefinierte Aufrufe der Datenbank benötigen, kann diese Methode manchmal sehr nützlich sein. Aber das ist eine andere Geschichte. Vergessen Sie nicht, zuerst nach Active Record-Wrapper-Methoden zu suchen und vermeiden Sie, das Rad neu zu erfinden, wo Rails Sie mehr als auf halber Strecke treffen möchte.

Schienen

Agent.find_by_sql ("SELECT * FROM-Agenten") Agent.find_by_sql ("SELECT-Name, Lizenzen-bis-Kill-FROM-Agenten") 

Wenig überraschend führt dies zu:

SQL

SELECT * FROM-Agenten SELECT-Name, licence_to_kill FROM-Agenten

Da Bereiche und Ihre eigenen Klassenmethoden für Ihre benutzerdefinierten Sucheranforderungen austauschbar verwendet werden können, können wir bei komplexeren SQL-Abfragen einen Schritt weiter gehen. 

Schienen

Klasse Agent < ActiveRecord::Base… def self.find_agent_names query = <<-SQL SELECT name FROM agents SQL self.find_by_sql(query) end end

Wir können Klassenmethoden schreiben, die die SQL in einem Here-Dokument einkapseln. Dadurch können wir mehrzeilige Zeichenfolgen sehr lesbar schreiben und dann diese SQL-Zeichenfolge in einer Variablen speichern, die wir wiederverwenden und an die wir übergeben können find_by_sql. Auf diese Weise gießen wir nicht Tonnen von Abfragecode innerhalb des Methodenaufrufs. Wenn Sie mehr als eine Stelle für die Abfrage haben, ist es auch TROCKEN.

Da dies angeblich Neulinge ist und kein SQL-Tutorial an sich ist, habe ich das Beispiel aus einem bestimmten Grund sehr minimalistisch gehalten. Die Technik für weitaus komplexere Abfragen ist jedoch die gleiche. Es ist leicht vorstellbar, dass sich dort eine benutzerdefinierte SQL-Abfrage befindet, die über zehn Codezeilen hinausgeht. 

Gehen Sie so verrückt, wie Sie möchten, vernünftig! Es kann ein Lebensretter sein. Ein Wort zur Syntax hier. Das SQL Teil ist hier nur ein Bezeichner, um den Anfang und das Ende der Zeichenfolge zu kennzeichnen. Ich wette, du brauchst diese Methode nicht so sehr - lass uns hoffen! Es hat definitiv seinen Platz, und Rails Land wäre nicht dasselbe ohne es - in den seltenen Fällen, in denen Sie unbedingt Ihre eigene SQL damit abstimmen wollen.

Abschließende Gedanken

Ich hoffe, Sie haben ein bisschen mehr Komfort beim Schreiben von Abfragen und beim Lesen der gefürchteten, alten SQL. Die meisten der in diesem Artikel behandelten Themen sind für das Schreiben von Abfragen, die sich mit komplexer Geschäftslogik befassen, unerlässlich. Nehmen Sie sich Zeit, dies zu verstehen und spielen Sie ein wenig mit Abfragen in der Konsole. 

Ich bin mir ziemlich sicher, dass, wenn Sie das Lernland hinterlassen, früher oder später Ihr Rails-Guthaben erheblich ansteigt, wenn Sie an Ihren ersten realen Projekten arbeiten und Ihre eigenen benutzerdefinierten Abfragen erstellen müssen. Wenn Sie immer noch ein wenig schüchtern sind, würde ich sagen, dass Sie einfach Spaß daran haben - es ist wirklich keine Raketenwissenschaft!