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
scope
und einer Klassenmethode:scope
-definiertescope
geben 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"