Advertisement
  1. Code
  2. Ruby on Rails

Abfragen in Rails, Teil 3

by
Length:LongLanguages:

German (Deutsch) translation by Valentina (you can also view the original English article)

In diesem letzten Stück werden wir uns etwas eingehender mit Abfragen befassen und mit einigen fortgeschritteneren Szenarien spielen. In diesem Artikel werden wir die Beziehungen von Active Record-Modellen etwas ausführlicher behandeln, aber ich werde mich von Beispielen fernhalten, die für die Programmierung von Neulingen zu verwirrend sein könnten. Bevor Sie fortfahren, sollten Dinge wie das folgende Beispiel keine Verwirrung stiften:

Wenn Sie mit Active Record-Abfragen und SQL noch nicht vertraut sind, empfehlen wir Ihnen, sich meine beiden vorherigen Artikel anzusehen, bevor Sie fortfahren. Dieser könnte schwer zu schlucken sein, ohne das Wissen, das ich bisher aufgebaut habe. Natürlich bis zu dir. Auf der anderen Seite wird dieser Artikel nicht so lang sein wie die anderen, wenn Sie sich nur diese leicht fortgeschrittenen Anwendungsfälle ansehen möchten. Lassen Sie uns eintauchen!

Themen

  • Bereiche und Verbände
  • Slimmer Joins
  • Merge
  • has_many
  • Custom Joins

Bereiche und Verbände

Lassen Sie uns noch einmal wiederholen. Wir können Active Record-Modelle sofort abfragen, aber Assoziationen sind auch ein faires Spiel für Abfragen - und wir können all diese Dinge verketten. So weit, ist es gut. Wir können Finder auch in Ihren Modellen in ordentliche, wiederverwendbare Bereiche packen, und ich habe kurz ihre Ähnlichkeit mit Klassenmethoden erwähnt.

Rails

Sie können sie also auch in Ihre eigenen Klassenmethoden packen und damit fertig sein. Die Bereiche sind nicht zweifelhaft oder so, denke ich - obwohl die Leute sie hier und da als etwas magisch bezeichnen -, aber da Klassenmethoden dasselbe erreichen, würde ich mich dafür entscheiden.

Rails

Diese Klassenmethoden lesen sich genauso und Sie müssen niemanden mit einem Lambda erstechen. Was auch immer für Sie oder Ihr Team am besten funktioniert; Es liegt an Ihnen, welche API Sie verwenden möchten. Kombinieren Sie sie einfach nicht - bleiben Sie bei der einmaligen Auswahl! Mit beiden Versionen können Sie diese Methoden einfach in eine andere Klassenmethode verketten, zum Beispiel:

Rails

Rails

Gehen wir noch einen Schritt weiter - bleiben Sie bei mir. Wir können ein Lambda in Assoziationen selbst verwenden, um einen bestimmten Umfang zu definieren. Es sieht auf den ersten Blick etwas komisch aus, aber sie können ziemlich praktisch sein. Das macht es möglich, diese Lambdas direkt in Ihren Verbänden zu nennen.

Dies ist ziemlich cool und gut lesbar, da kürzere Methoden verkettet werden. Achten Sie jedoch darauf, diese Modelle nicht zu fest zu koppeln.

Rails

Sagen Sie mir, das ist irgendwie nicht cool! Es ist nicht für den täglichen Gebrauch, aber ich denke, es ist doof. Hier kann Mission also nur Agenten "anfordern", die die Lizenz zum Töten haben.

Ein Wort zur Syntax, da wir von Namenskonventionen abgewichen sind und etwas Ausdrucksvolleres wie double_o_agents verwendet haben. Wir müssen den Klassennamen erwähnen, um Rails nicht zu verwirren, da sonst erwartet werden könnte, dass nach einer Klasse DoubleOAgent gesucht wird. Sie können natürlich beide Agent-Zuordnungen einrichten - die übliche und Ihre benutzerdefinierte - und Rails wird sich nicht beschweren.

Rails 

Slimmer Joins

Wenn Sie die Datenbank nach Datensätzen abfragen und nicht alle Daten benötigen, können Sie angeben, was genau zurückgegeben werden soll. Warum? Da die an Active Record zurückgegebenen Daten schließlich in neue Ruby-Objekte integriert werden. Schauen wir uns eine einfache Strategie an, um ein Aufblähen des Speichers in Ihrer Rails-App zu vermeiden:

Rails

Rails

SQL

Diese Abfrage gibt also eine Liste von Agenten mit einer Mission aus der Datenbank an Active Record zurück, die dann wiederum Ruby-Objekte daraus erstellen. Die mission-Daten sind verfügbar, da die Daten aus diesen Zeilen mit den Zeilen der Agentendaten verknüpft werden. Das heißt, die verknüpften Daten sind während der Abfrage verfügbar, werden jedoch nicht an Active Record zurückgegeben. So haben Sie diese Daten zum Beispiel, um Berechnungen durchzuführen.

Es ist besonders cool, weil Sie Daten verwenden können, die nicht auch an Ihre App zurückgesendet werden. Weniger Attribute, die in Ruby-Objekte eingebaut werden müssen - die Speicherplatz beanspruchen - können ein großer Gewinn sein. Denken Sie im Allgemeinen daran, nur die absolut erforderlichen Zeilen und Spalten zurückzusenden, die Sie benötigen. Auf diese Weise können Sie ein Aufblähen vermeiden.

Rails

Nur eine kurze Bemerkung zur Syntax hier: Da wir die Agent-Tabelle nicht über where, sondern über die join :mission-Tabelle abfragen, müssen wir in unserer WHERE-Klausel angeben, dass wir nach bestimmten missions suchen.

SQL

Die Verwendung von includes hier würde auch Missionen zum eifrigen Laden an Active Record zurückgeben und den Speicheraufbau von Ruby-Objekten aufnehmen.

Merge

Eine merge ist beispielsweise nützlich, wenn Sie eine Abfrage zu Agenten und den zugehörigen Missionen kombinieren möchten, die einen bestimmten von Ihnen definierten Bereich haben. Wir können zwei ActiveRecord::Relation-Objekte nehmen und ihre Bedingungen zusammenführen. Sicher, kein Problem, aber merge ist nützlich, wenn Sie einen bestimmten Bereich verwenden möchten, während Sie einen Verband verwenden.

Mit anderen Worten, was wir mit merge tun können, ist das Filtern nach einem benannten Bereich im verbundenen Modell. In einem der vorherigen Beispiele haben wir Klassenmethoden verwendet, um solche benannten Bereiche selbst zu definieren.

Rails

Rails

SQL

Wenn wir zusammenfassen, was eine dangerous Mission im Mission-Modell ist, können wir sie auf diese Weise durch merge in einen join einbinden. Die Logik solcher Bedingungen auf das relevante Modell zu verlagern, zu dem sie gehören, ist einerseits eine gute Technik, um eine lockere Kopplung zu erreichen - wir möchten nicht, dass unsere Active Record-Modelle viele Details über einander wissen - und andererseits Hand, es gibt Ihnen eine schöne API in Ihren Joins, ohne in die Luft zu jagen. Das folgende Beispiel ohne Zusammenführung würde ohne Fehler nicht funktionieren:

Rails

SQL

Wenn wir jetzt ein ActiveRecord::Relation-Objekt für unsere Missionen auf unseren Agenten zusammenführen, weiß die Datenbank nicht, um welche Missionen es sich handelt. Wir müssen klarstellen, welcher Verband wir benötigen, und uns zuerst den Missionsdaten anschließen - sonst wird SQL verwirrt. Eine letzte Kirsche oben drauf. Wir können dies noch besser zusammenfassen, indem wir auch die Agenten einbeziehen:

Rails

Rails

SQL

Das ist eine süße Kirsche in meinem Buch. Kapselung, korrekter OOP und gute Lesbarkeit. Jackpot!

has_many

Oben haben wir den belongs_to-Verband viel in Aktion gesehen. Schauen wir uns das aus einer anderen Perspektive an und bringen wir Geheimdienstabschnitte in den Mix:

Rails

In diesem Szenario hätten Agenten also nicht nur eine mission_id, sondern auch eine section_id. So weit, ist es gut. Lassen Sie uns alle Abschnitte mit Agenten mit einer bestimmten Mission finden - also Abschnitte, in denen eine Mission ausgeführt wird.

Rails

SQL

Haben Sie etwas bemerkt? Ein kleines Detail ist anders. Die Fremdschlüssel werden umgedreht. Hier fordern wir eine Liste von Abschnitten an, verwenden jedoch Fremdschlüssel wie diesen: "agents". "section_id" = "sections." id ". Mit anderen Worten, wir suchen nach einem Fremdschlüssel aus einer Tabelle, der wir beitreten.

Rails

SQL

Bisher sahen unsere Verknüpfungen über eine belongs_to-Zuordnung folgendermaßen aus: Die Fremdschlüssel wurden gespiegelt ("missions"."id" = "agents"."mission_id") und suchten nach dem Fremdschlüssel aus der Tabelle, die wir starten.

Wenn wir zu Ihrem has_many-Szenario zurückkehren, erhalten wir jetzt eine Liste von Abschnitten, die wiederholt werden, da sie natürlich mehrere Agenten in jedem Abschnitt haben. Für jede Agentenspalte, die verbunden wird, erhalten wir eine Zeile für diesen Abschnitt oder diese section_id. Kurz gesagt, wir duplizieren grundsätzlich Zeilen. Um dies noch schwindelerregender zu machen, bringen wir auch Missionen in den Mix.

Rails

SQL

Schauen Sie sich die beiden INNER JOIN-Teile an. Immer noch bei mir? Wir "erreichen" durch Agenten ihre Missionen aus der Agentenabteilung. Ja, Sachen zum Spaß, ich weiß. Was wir bekommen, sind Missionen, die indirekt mit einem bestimmten Abschnitt verbunden sind.

Infolgedessen werden neue Spalten hinzugefügt, aber die Anzahl der Zeilen ist immer noch dieselbe, die von dieser Abfrage zurückgegeben wird. Was an Active Record zurückgesendet wird, was zum Erstellen neuer Ruby-Objekte führt, ist auch weiterhin die Liste der Abschnitte. Wenn also mehrere Missionen mit mehreren Agenten ausgeführt werden, erhalten wir erneut doppelte Zeilen für unseren Abschnitt. Lassen Sie uns dies noch etwas filtern:

Rails

SQL

Jetzt erhalten wir nur Abschnitte zurück, die an Missionen beteiligt sind, bei denen Ernst Stavro Blofeld der betroffene Feind ist. Kosmopolitisch, wie manche Superschurken vielleicht von sich denken, könnten sie in mehr als einem Abschnitt operieren - sagen wir Abschnitt A und C, die Vereinigten Staaten bzw. Kanada.

Wenn wir in einem bestimmten Abschnitt mehrere Agenten haben, die an derselben Mission arbeiten, um Blofeld oder was auch immer zu stoppen, hätten wir wieder wiederholte Zeilen in Active Record. Lassen Sie uns etwas genauer darauf eingehen:

Rails

SQL

Dies gibt uns die Anzahl der Abschnitte, in denen Blofeld operiert - die bekannt sind -, in denen Agenten in Missionen mit ihm als Feind aktiv sind. Lassen Sie uns als letzten Schritt noch einmal einige Umgestaltungen vornehmen. Wir extrahieren dies in eine nette "kleine" Klassenmethode im class Section:

Rails

Sie können dies noch weiter umgestalten und die Verantwortlichkeiten aufteilen, um eine lockerere Kopplung zu erreichen. Lassen Sie uns jedoch vorerst fortfahren.

Custom Joins

Meistens können Sie sich darauf verlassen, dass Active Record das gewünschte SQL schreibt. Das bedeutet, dass Sie in Ruby Land bleiben und sich nicht zu viele Gedanken über Datenbankdetails machen müssen. Aber manchmal müssen Sie ein Loch in das SQL-Land stecken und Ihr eigenes Ding machen. Zum Beispiel, wenn Sie einen LEFT Join verwenden und aus dem üblichen Verhalten von Active Record ausbrechen müssen, einen INNER-Join standardmäßig durchzuführen. joins ist ein kleines Fenster, in dem Sie bei Bedarf Ihr eigenes benutzerdefiniertes SQL schreiben können. Sie öffnen es, fügen Ihren benutzerdefinierten Abfragecode ein, schließen das „Fenster“ und können weiterhin Active Record-Abfragemethoden hinzufügen.

Lassen Sie uns dies anhand eines Beispiels demonstrieren, das Gadgets umfasst. Nehmen wir an, ein typischer Agent normalerweise has_many Gadgets, und wir möchten Agenten finden, die nicht mit ausgefallenen Gadgets ausgestattet sind, um ihnen vor Ort zu helfen. Ein gewöhnlicher Join würde keine guten Ergebnisse liefern, da wir tatsächlich an nil - oder null in SQL - Werten dieser Spionagespielzeuge interessiert sind.

Rails

Wenn wir eine joins-Operation ausführen, werden nur Agenten zurückgegeben, die bereits mit Gadgets ausgestattet sind, da die agent_id für diese Gadgets nicht Null ist. Dies ist das erwartete Verhalten eines Standard-Inner-Joins. Der innere Join baut auf einer Übereinstimmung auf beiden Seiten auf und gibt nur Datenzeilen zurück, die dieser Bedingung entsprechen. Ein nicht vorhandenes Gadget mit dem Wert nil für einen Agenten, der kein Gadget enthält, entspricht nicht diesem Kriterium.

Rails

SQL

Auf der anderen Seite suchen wir nach Schmuck-Agenten, die dringend etwas Liebe vom Quartiermeister brauchen. Ihre erste Vermutung könnte wie folgt ausgesehen haben:

Rails

SQL

Nicht schlecht, aber wie Sie der SQL-Ausgabe entnehmen können, spielt sie nicht mit und besteht weiterhin auf der Standardeinstellung INNER JOIN. Dies ist ein Szenario, in dem wir einen OUTER-Join benötigen, da sozusagen eine Seite unserer „Gleichung“ fehlt. Wir suchen nach Ergebnissen für nicht vorhandene Gadgets - genauer gesagt für Agenten ohne Gadgets.

Als wir bisher in einem Join ein Symbol an Active Record übergeben haben, hat es eine Zuordnung erwartet. Wenn jedoch eine Zeichenfolge übergeben wird, wird erwartet, dass es sich um ein tatsächliches Fragment von SQL-Code handelt - ein Teil Ihrer Abfrage.

Rails

SQL

Oder wenn Sie neugierig auf faule Agenten ohne Mission sind - möglicherweise auf Barbados oder wo auch immer -, würde unser benutzerdefinierter Join folgendermaßen aussehen:

Rails

SQL

Die äußere Verknüpfung ist die umfassendere Verknüpfungsversion, da sie mit allen Datensätzen aus den verknüpften Tabellen übereinstimmt, auch wenn einige dieser Beziehungen noch nicht vorhanden sind. Da dieser Ansatz nicht so exklusiv ist wie innere Verknüpfungen, erhalten Sie hier und da eine Reihe von Nullen. Dies kann natürlich in einigen Fällen informativ sein, aber innere Verknüpfungen sind normalerweise das, wonach wir suchen. In Rails 5 können wir stattdessen eine spezielle Methode namens left_outer_joins für solche Fälle verwenden. Schließlich!

Eine Kleinigkeit für die Straße: Halten Sie diese Gucklöcher im SQL-Land so klein wie möglich, wenn Sie können. Sie werden jedem - einschließlich Ihres zukünftigen Selbst - einen enormen Gefallen tun.

Abschließende Gedanken

Active Record dazu zu bringen, effizientes SQL für Sie zu schreiben, ist eine der Hauptfähigkeiten, die Sie dieser Miniserie für Anfänger abnehmen sollten. Auf diese Weise erhalten Sie auch Code, der mit der unterstützten Datenbank kompatibel ist. Dies bedeutet, dass die Abfragen datenbankübergreifend stabil sind. Sie müssen nicht nur verstehen, wie man mit Active Record spielt, sondern auch das zugrunde liegende SQL, das von gleicher Bedeutung ist.

Ja, SQL kann langweilig sein, mühsam zu lesen sein und nicht elegant aussehen. Vergessen Sie jedoch nicht, dass Rails Active Record mit SQL umschließt, und Sie sollten das Verständnis dieser wichtigen Technologie nicht vernachlässigen - nur weil Rails es sehr einfach macht, sich nicht darum zu kümmern der ganzen Zeit. Effizienz ist für Datenbankabfragen von entscheidender Bedeutung, insbesondere wenn Sie etwas für ein größeres Publikum mit starkem Datenverkehr erstellen.

Gehen Sie jetzt ins Internet und suchen Sie nach mehr Material zu SQL, um es ein für alle Mal aus Ihrem System zu entfernen!

Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.