Ruby on Rails
ActiveRecord-Abfrage-Schnittstelle
Suche…
Einführung
ActiveRecord ist das M in MVC, dh die Schicht des Systems, die für die Darstellung von Geschäftsdaten und -logik zuständig ist. Die Technik , die die reichen Objekte einer Anwendung Tabellen in einer relationalen Datenbank - Management - System verbindet , ist O bject R elational M apper (ORM).
ActiveRecord führt für Sie Abfragen in der Datenbank durch und ist mit den meisten Datenbanksystemen kompatibel. Unabhängig davon, welches Datenbanksystem Sie verwenden, das Format der ActiveRecord-Methode ist immer dasselbe.
.woher
Die where Methode ist für jedes ActiveRecord Modell verfügbar und ermöglicht das Abfragen der Datenbank nach einem Datensatz, der den angegebenen Kriterien entspricht.
Die where Methode akzeptiert einen Hash, bei dem die Schlüssel den Spaltennamen in der Tabelle entsprechen, die das Modell darstellt.
Als einfaches Beispiel verwenden wir folgendes Modell:
class Person < ActiveRecord::Base
#attribute :first_name, :string
#attribute :last_name, :string
end
Um alle Personen mit dem Vornamen von Sven :
people = Person.where(first_name: 'Sven')
people.to_sql # "SELECT * FROM people WHERE first_name='Sven'"
Um alle Personen mit dem Vornamen von Sven und dem Nachnamen von Schrodinger :
people = Person.where(first_name: 'Sven', last_name: 'Schrodinger')
people.to_sql # "SELECT * FROM people WHERE first_name='Sven' AND last_name='Schrodinger'"
Im obigen Beispiel zeigt die SQL-Ausgabe, dass Datensätze nur zurückgegeben werden, wenn sowohl der first_name als auch der last_name übereinstimmen.
Abfrage mit ODER-Bedingung
So suchen Sie Datensätze mit first_name == 'Bruce' ODER last_name == 'Wayne'
User.where('first_name = ? or last_name = ?', 'Bruce', 'Wayne')
# SELECT "users".* FROM "users" WHERE (first_name = 'Bruce' or last_name = 'Wayne')
.wo mit einem Array
Die where Methode für ein beliebiges ActiveRecord-Modell kann verwendet werden, um SQL der Form WHERE column_name IN (a, b, c, ...) zu generieren. Dies wird erreicht, indem ein Array als Argument übergeben wird.
Als einfaches Beispiel verwenden wir folgendes Modell:
class Person < ActiveRecord::Base
#attribute :first_name, :string
#attribute :last_name, :string
end
people = Person.where(first_name: ['Mark', 'Mary'])
people.to_sql # "SELECT * FROM people WHERE first_name IN ('Mark', 'Mary')"
Wenn das Array eine nil enthält, wird das SQL geändert, um zu überprüfen, ob die Spalte null :
people = Person.where(first_name: ['Mark', 'Mary', nil])
people.to_sql # "SELECT * FROM people WHERE first_name IN ('Mark', 'Mary') OR first_name IS NULL"
Bereiche
Bereiche dienen als vordefinierte Filter für ActiveRecord Modelle.
Ein Gültigkeitsbereich wird mithilfe der scope definiert.
Als einfaches Beispiel verwenden wir folgendes Modell:
class Person < ActiveRecord::Base
#attribute :first_name, :string
#attribute :last_name, :string
#attribute :age, :integer
# define a scope to get all people under 17
scope :minors, -> { where(age: 0..17) }
# define a scope to search a person by last name
scope :with_last_name, ->(name) { where(last_name: name) }
end
Bereiche können direkt aus der Modellklasse abgerufen werden:
minors = Person.minors
Bereiche können verkettet werden:
peters_children = Person.minors.with_last_name('Peters')
Die where Methode und andere Abfragetypmethoden können auch verkettet werden:
mary_smith = Person.with_last_name('Smith').where(first_name: 'Mary')
Hinter den Kulissen sind Geltungsbereiche einfach syntaktischer Zucker für eine Standardklassenmethode. Zum Beispiel sind diese Methoden funktional identisch:
scope :with_last_name, ->(name) { where(name: name) }
# This ^ is the same as this:
def self.with_last_name(name)
where(name: name)
end
Standardumfang
in Ihrem Modell, um einen Standardbereich für alle Vorgänge am Modell festzulegen.
Es gibt einen bemerkenswerten Unterschied zwischen der
scopeund einer Klassenmethode:scope-definiertescopegeben immer ActiveRecordActiveRecord::Relation, selbst wenn die Logik in nil zurückgibt. Klassenmethoden haben jedoch kein solches Sicherheitsnetz und können die Kettenfähigkeit brechen, wenn sie etwas anderes zurückgeben.
wo nicht
where Klauseln können mit der where.not Syntax negiert werden:
class Person < ApplicationRecord
#attribute :first_name, :string
end
people = Person.where.not(first_name: ['Mark', 'Mary'])
# => SELECT "people".* FROM "people" WHERE "people"."first_name" NOT IN ('Mark', 'Mary')
Unterstützt von ActiveRecord 4.0 und höher.
Bestellung
Sie können mit Activeabfrageergebnisse bestellen .order :
User.order(:created_at)
#=> => [#<User id: 2, created_at: "2015-08-12 21:36:23">, #<User id: 11, created_at: "2015-08-15 10:21:48">]
Wenn nicht angegeben, erfolgt die Bestellung in aufsteigender Reihenfolge. Sie können es angeben, indem Sie Folgendes tun:
User.order(created_at: :asc)
#=> => [#<User id: 2, created_at: "2015-08-12 21:36:23">, #<User id: 11, created_at: "2015-08-15 10:21:48">]
User.order(created_at: :desc)
#=> [#<User id: 7585, created_at: "2016-07-13 17:15:27">, #<User id: 7583, created_at: "2016-07-13 16:51:18">]
.order akzeptiert auch eine Zeichenfolge, also können Sie dies auch tun
User.order("created_at DESC")
#=> [#<User id: 7585, created_at: "2016-07-13 17:15:27">, #<User id: 7583, created_at: "2016-07-13 16:51:18">]
Da es sich bei der Zeichenfolge um Raw-SQL handelt, können Sie auch eine Tabelle und nicht nur ein Attribut angeben. Angenommen , Sie bestellen möchten users entsprechend ihrer role Namen, können Sie dies tun:
Class User < ActiveRecord::Base
belongs_to :role
end
Class Role < ActiveRecord::Base
has_many :users
end
User.includes(:role).order("roles.name ASC")
Der order kann auch einen Arel-Knoten akzeptieren:
User.includes(:role).order(User.arel_table[:name].asc)
ActiveRecord Bang (!) -Methoden
Wenn Sie eine ActiveRecord- Methode benötigen, um im Fehlerfall eine Ausnahme anstelle eines false Werts auszulösen, können Sie hinzufügen ! zu ihnen. Dies ist sehr wichtig. Da einige Ausnahmen / Ausfälle schwer zu fassen sind, wenn Sie sie nicht verwenden! auf sie. Ich habe empfohlen, dies in Ihrem Entwicklungszyklus zu tun, um Ihren gesamten ActiveRecord-Code auf diese Weise zu schreiben, um Zeit und Ärger zu sparen.
Class User < ActiveRecord::Base
validates :last_name, presence: true
end
User.create!(first_name: "John")
#=> ActiveRecord::RecordInvalid: Validation failed: Last name can't be blank
Die ActiveRecord- Methoden, die einen Knall ( ! ) Akzeptieren, sind:
-
.create! -
.take! -
.first! -
.last! -
.find_by! -
.find_or_create_by! -
#save! -
#update! - alle AR dynamischen Sucher
.find_by
Mit find_by können Sie Datensätze nach beliebigen Feldern in Ihrer Tabelle find_by .
Wenn Sie ein User mit einem first_name Attribut haben, können Sie first_name tun:
User.find_by(first_name: "John")
#=> #<User id: 2005, first_name: "John", last_name: "Smith">
find_by dass find_by standardmäßig keine Ausnahme find_by . Wenn das Ergebnis ein leerer Satz ist, wird nil anstelle von find .
Wenn die Ausnahme benötigt wird, kann find_by! das verursacht einen ActiveRecord::RecordNotFound Fehler wie find .
.alles löschen
Wenn Sie eine Menge von Datensätzen schnell löschen müssen, gibt Active .delete_all Methode. um direkt in einem Modell aufgerufen zu werden, um alle Datensätze in dieser Tabelle oder eine Auflistung zu löschen. .delete_all Sie jedoch, da .delete_all kein Objekt instanziiert und daher keinen Rückruf before_* ( before_* und after_destroy nicht ausgelöst).
User.delete_all
#=> 39 <-- .delete_all return the number of rows deleted
User.where(name: "John").delete_all
Bei ActiveRecord wird die Groß- / Kleinschreibung nicht berücksichtigt
Wenn Sie ein ActiveRecord-Modell nach ähnlichen Werten suchen müssen, werden Sie möglicherweise versucht, LIKE oder ILIKE , dies ist jedoch zwischen Datenbank-Engines nicht übertragbar. In ähnlicher Weise kann der Rückgriff auf immer Downcasing oder Upcasing zu Performance-Problemen führen.
Sie können die zugrunde liegende Arel- matches Methode von ActiveRecord verwenden, um dies auf eine sichere Weise durchzuführen:
addresses = Address.arel_table
Address.where(addresses[:address].matches("%street%"))
Arel wendet das entsprechende LIKE- oder ILIKE-Konstrukt für das konfigurierte Datenbankmodul an.
Holen Sie sich den ersten und letzten Datensatz
Rails haben eine sehr einfache Möglichkeit, den first und last Datensatz aus der Datenbank abzurufen.
Um den first Datensatz aus der users , müssen Sie den folgenden Befehl eingeben:
User.first
Es wird folgende sql Abfrage generiert:
SELECT `users`.* FROM `users` ORDER BY `users`.`id` ASC LIMIT 1
Und wird folgende Aufzeichnung zurückgeben:
#<User:0x007f8a6db09920 id: 1, first_name: foo, created_at: Thu, 16 Jun 2016 21:43:03 UTC +00:00, updated_at: Thu, 16 Jun 2016 21:43:03 UTC +00:00 >
Um den last Datensatz aus der users , müssen Sie den folgenden Befehl eingeben:
User.last
Es wird folgende sql Abfrage generiert:
SELECT `users`.* FROM `users` ORDER BY `users`.`id` DESC LIMIT 1
Und wird folgende Aufzeichnung zurückgeben:
#<User:0x007f8a6db09920 id: 10, first_name: bar, created_at: Thu, 16 Jun 2016 21:43:03 UTC +00:00, updated_at: Thu, 16 Jun 2016 21:43:03 UTC +00:00 >
Durch Übergeben einer Ganzzahl an die erste und letzte Methode wird eine LIMIT- Abfrage erstellt und ein Array von Objekten zurückgegeben.
User.first(5)
Es wird folgende sql Abfrage generiert.
SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 5
Und
User.last(5)
Es wird folgende sql Abfrage generiert.
SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT 5
.group und .count
Wir haben ein Product und möchten sie nach ihrer category gruppieren.
Product.select(:category).group(:category)
Dadurch wird die Datenbank wie folgt abgefragt:
SELECT "product"."category" FROM "product" GROUP BY "product"."category"
Stellen Sie sicher, dass das gruppierte Feld ebenfalls ausgewählt ist. Die Gruppierung eignet sich besonders zum Zählen des Vorkommens - in diesem Fall - von categories .
Product.select(:category).group(:category).count
Wie die Abfrage zeigt, wird die Datenbank zum Zählen verwendet, was wesentlich effizienter ist, als zuerst alle Datensätze abzurufen und im Code zu zählen:
SELECT COUNT("products"."category") AS count_categories, "products"."category" AS products_category FROM "products" GROUP BY "products"."category"
.distinct (oder .uniq)
Wenn Sie Duplikate aus einem Ergebnis entfernen möchten, können Sie .distinct() :
Customers.select(:country).distinct
Dadurch wird die Datenbank wie folgt abgefragt:
SELECT DISTINCT "customers"."country" FROM "customers"
.uniq() hat den gleichen Effekt. Mit Rails 5.0 wurde es veraltet und mit Version 5.1 von Rails entfernt. Der Grund ist, dass das Wort " unique nicht die gleiche Bedeutung wie "different" hat und es kann irreführend sein. Weiterhin distinct ist näher an der SQL - Syntax.
Schließt sich an
joins() können Sie Tabellen mit Ihrem aktuellen Modell verknüpfen. Für ex.
User.joins(:posts)
erzeugt die folgende SQL-Abfrage:
"SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id""
Nachdem Sie den Tisch verbunden haben, können Sie darauf zugreifen:
User.joins(:posts).where(posts: { title: "Hello world" })
Achten Sie auf die Pluralform. Wenn Ihre Beziehung :has_many , sollte das Argument joins() pluralisiert werden. Ansonsten verwenden Sie Singular.
Verschachtelte joins :
User.joins(posts: :images).where(images: { caption: 'First post' })
was wird produzieren:
"SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id" INNER JOIN "images" ON "images"."post_id" = "images"."id""
Enthält
ActiveRecord with includes stellt sicher, dass alle angegebenen Zuordnungen mit der minimal möglichen Anzahl von Abfragen geladen werden. Wenn Sie also eine Tabelle nach Daten mit einer zugeordneten Tabelle abfragen, werden beide Tabellen in den Speicher geladen.
@authors = Author.includes(:books).where(books: { bestseller: true } )
# this will print results without additional db hitting
@authors.each do |author|
author.books.each do |book|
puts book.title
end
end
Author.joins(:books).where(books: { bestseller: true } ) lädt nur Autoren mit Bedingungen in den Speicher, ohne Bücher zu laden . Verwenden Sie joins wenn keine zusätzlichen Informationen zu verschachtelten Zuordnungen erforderlich sind.
@authors = Author.joins(:books).where(books: { bestseller: true } )
# this will print results without additional queries
@authors.each { |author| puts author.name }
# this will print results with additional db queries
@authors.each do |author|
author.books.each do |book|
puts book.title
end
end
Limit und Offset
Sie können mit limit die Anzahl der abzurufenden Datensätze angeben und mit offset die Anzahl der zu überspringenden Datensätze angeben, bevor Sie die Datensätze zurückgeben.
Zum Beispiel
User.limit(3) #returns first three records
Es wird folgende SQL-Abfrage generiert.
"SELECT `users`.* FROM `users` LIMIT 3"
Da der Offset in der obigen Abfrage nicht erwähnt wird, werden die ersten drei Datensätze zurückgegeben.
User.limit(5).offset(30) #returns 5 records starting from 31th i.e from 31 to 35
Es wird folgende SQL-Abfrage generiert.
"SELECT `users`.* FROM `users` LIMIT 5 OFFSET 30"