Dans cet article, vous allez apprendre les bases des requêtes Active Record et quelques notions de base sur SQL tout au long du processus. Il est destiné aux débutants qui souhaitent en savoir plus sur les requêtes de base de données dans Ruby on Rails.
Enregistrement actif est utilisé pour interroger la base de données. Il peut être utilisé avec SQL, PostgresSQL et SQLite. Pour récupérer des enregistrements de votre base de données, vous disposez de plusieurs méthodes de recherche. La bonne chose à leur sujet est que vous pouvez vous épargner la peine d’écrire du SQL brut.
Qu'est-ce qu'une méthode de recherche fait vraiment? Fondamentalement, trois choses: vos options fournies sont converties en une requête SQL. Ensuite, la requête SQL est exécutée et extrait les données de la base de données. De plus, pour chaque ligne de cette liste de résultats, nous obtenons les objets Ruby nouvellement instanciés du modèle qui correspond à la requête..
Si vous n'avez jamais joué avec SQL, je ferai de mon mieux pour que les choses restent simples et que je vous présente les bases. Suivez les exemples SQL et essayez de donner un sens à ces requêtes simples. SQL n'est pas vraiment sorcier, la syntaxe demande un peu de temps pour s'y habituer. J'espère que cela vous donnera envie de dénicher des didacticiels utiles pour combler les lacunes..
Voyons quelques méthodes à votre disposition:
trouver
premier
dernier
trouver_par
tout
find_each
find_in_batches
où
ordre
limite
décalage
groupe
ayant
Tous ces éléments renverront une instance de ActiveRecord :: Relation
. Un quoi? C'est une classe qui est namespaced au sein de la module ActiveRecord
, et cela nous permet d'appeler plusieurs méthodes de requête et de les chaîner. Cet objet est le cœur de la syntaxe de requête utilisée dans Rails. Vérifions la classe d'un tel objet et voyons nous-mêmes:
Agent.where (nom: 'James Bond'). Class # => ActiveRecord :: Relation
trouver
Cette méthode vous permet de fournir l'identifiant principal d'un objet et de récupérer ce seul objet pour vous. Si vous fournissez un tableau d'identifiants, vous pouvez également récupérer plusieurs objets..
bond = Agent.find (7)
SELECTIONNEZ "agents". * FROM "agents" O "agents". "Id" =? LIMITE 1 [["" id ", 7]]
Cette ligne de code SQL indique que vous souhaitez tout sélectionner (*
) attributs de la agents
table et "filtre" uniquement l'enregistrement ayant l'identifiant 7. Une limite lui permet de ne renvoyer qu'un seul enregistrement à partir de la base de données..
premier
, dernier
Sans surprise, cela vous donnera les premier et dernier enregistrements qui peuvent être identifiés par leur clé primaire. La partie intéressante, cependant, est que vous pouvez fournir un nombre optionnel qui vous renvoie le premier ou le dernier de ce nombre d'enregistrements..
ennemis_agents = SpectreAgent.first (10) ennemis_agents = SpectreAgent.last (10)
Sous le capot, vous fournissez une nouvelle limite pour le nombre que vous fournissez et vous le commandez par ordre croissant ou décroissant..
SELECT "spectreagents". * FROM "spectreagents" ORDER BY "spectreagents". "Id" ASC LIMIT 10 SELECT "spectreagents". * FROM "spectreagents" ORDER BY "spectreagents". "Id" DESC LIMIT 10
trouver_par
Ce chercheur renvoie le premier objet correspondant à la condition fournie..
bond = Agent.find_by (last_name: 'Bond')
SELECT "agents". * FROM "agents" O "agents". "Dernier_nom" =? LIMIT 1 [["" nom "," Bond "]]
De toute évidence, nous avons souvent besoin de parcourir une collection d'objets avec un ordre du jour. Récupérer un ou plusieurs objets à la main est bien, mais le plus souvent, nous voulons qu'Active Record récupère les objets par lots..
Présenter aux utilisateurs toutes sortes de listes est le pain quotidien de la plupart des applications Rails. Ce dont nous avons besoin, c'est d'un outil puissant avec une API pratique pour collecter ces objets pour nous, d'une manière qui nous permet d'éviter d'écrire le code SQL impliqué nous-mêmes la plupart du temps..
tout
mi6_agents = Agents.all
SELECT "agents". * FROM "agents"
Cette méthode est pratique pour les collections d'objets relativement petites. Essayez d'imaginer faire cela sur une collection de tous les utilisateurs de Twitter. Non, pas une bonne idée. Ce que nous voulons plutôt, c'est une approche plus précise pour les tables de plus grande taille..
Aller chercher toute la table ne va pas à l'échelle! Pourquoi? Parce que nous ne demanderions pas seulement un tas d’objets, nous aurions également besoin de construire un objet par ligne dans cette table et de les placer dans un tableau en mémoire. J'espère que cela ne semble pas être une bonne idée! Alors, quelle est la solution pour cela? Des lots! Nous divisons ces collections en lots qui permettent de traiter plus facilement la mémoire. Woohoo!
Regardons find_each
et find_in_batches
. Les deux sont similaires mais se comportent différemment dans la manière dont ils produisent des objets en blocs. Ils acceptent une option pour réguler la taille du lot. La valeur par défaut est 1 000.
find_each
NewRecruit.find_each do | recruit | recruit.start_hellweek end
SÉLECTIONNEZ "newrecruits". * FROM "newrecruits" ORDER BY "newrecruits". "Id" ASC LIMIT 1000
Dans ce cas, nous récupérons un lot par défaut de 1 000 nouvelles recrues, nous les cédons au bloc et nous les envoyons à la semaine de l'enfer, une par une. Comme les lots découpent des collections, nous pouvons également leur dire par où commencer via début
. Disons que nous voulons traiter 3 000 recrues possibles en une fois et que nous voulons partir à 4 000.
NewRecruit.find_each (début: 4000, batch_size: 3000) faire | recruter | recruit.start_hellweek end
SÉLECTIONNEZ "newrecruits". * FROM "newrecruits" WHERE ("newrecruits". "Id"> = 4000) ORDER BY "newrecruits". "Id" ASC LIMIT 3000
Pour rappel, nous récupérons d’abord un lot de 3 000 objets Ruby, puis nous les envoyons dans le bloc.. début
nous permet de spécifier l'id des enregistrements où nous voulons commencer à aller chercher ce lot.
find_in_batches
Celui-ci renvoie son lot sous forme de tableau au bloc - il le transmet à un autre objet qui préfère gérer les collections. Le SQL est le même ici.
NewRecruit.find_in_batches (début: 2700, batch_size: 1350) ne | recrute | field_kitchen.prepare_food (recrues) end
où
Nous devons passer en revue où
avant de continuer plus loin. Cela nous permet de spécifier des conditions qui limitent le nombre d'enregistrements renvoyés par nos requêtes: un filtre pour «où» récupérer les enregistrements de la base de données. Si vous avez joué avec SQL OÙ
clauses alors vous pourriez vous sentir juste à la maison-même chose avec cette enveloppe Ruby.
En SQL, cela nous permet de spécifier la ligne de la table que nous voulons affecter, en gros, là où elle remplit certains critères. C'est une clause facultative, en passant. Dans le SQL brut ci-dessous, nous sélectionnons uniquement les recrues orphelines via OÙ
.
Sélectionnez une ligne spécifique dans une table.
SELECT * FROM recrues WHERE FamilyStatus = 'Orphan';
Via où
, vous pouvez spécifier des conditions avec des chaînes, des hachages ou des tableaux. En mettant tout cela ensemble, Active Record vous permet de filtrer pour de telles conditions:
promise_candidates = Recruit.where ("family_status = 'orphan'")
SELECT "recrute". * FROM "recrute" WHERE (family_status = 'orphan')
Plutôt chouette, non? Je tiens à mentionner qu'il s'agit toujours d'une opération de recherche. Nous spécifions simplement la manière dont nous souhaitons filtrer cette liste immédiatement. Dans la liste de toutes les recrues, une liste filtrée de candidats orphelins sera renvoyée. Cet exemple est une condition de chaîne. Éloignez-vous des conditions de chaîne pures car elles ne sont pas considérées comme sûres en raison de leur vulnérabilité aux injections SQL.
Dans l'exemple ci-dessus, nous mettons le orphelin
variable dans la chaîne avec les conditions. Ceci est considéré comme une mauvaise pratique parce que c'est dangereux. Nous devons échapper à la variable pour éviter cette vulnérabilité de sécurité. Vous devriez vous renseigner sur l'injection SQL s'il s'agit d'une nouvelle totale pour vous, votre base de données pourrait en dépendre.
promise_candidates = Recruter.where ("family_status =?", 'orphelin' ")
le ?
sera remplacé comme valeur de condition par la valeur suivante dans la liste d'arguments. Donc, le point d'interrogation est un espace réservé essentiellement. Vous pouvez également spécifier plusieurs conditions avec plusieurs ?
et les enchaîner. Dans un scénario réel, nous utiliserions un hachage params comme ceci:
promise_candidates = Recruter.where ("family_status =?", params [: recruits])
Si vous avez un grand nombre de conditions variables, vous devez utiliser des conditions d'espace réservé clé / valeur..
promise_candidates = Recruit.where ("family_status =: statut_pronféré ET iq> =: requis_iq ET charmant =: lady_killer", statut_précié: 'orphelin', requis_iq: 140, lady_killer: vrai)
SELECT "recrute". * FROM "recrute" WHERE (family_status = 'orphan' ET iq> = 140 AND lady_killer = true)
L'exemple ci-dessus est stupide, bien sûr, mais il montre clairement les avantages de la notation d'espace réservé. La notation de hachage, en général, est certainement la plus lisible.
promise_candidates = Recruter.where (statut de famille: 'orphelin') promise_candidates = Recruit.where ('charmant': vrai)
Comme vous pouvez le constater, vous pouvez utiliser des symboles ou des chaînes de caractères. Fermons cette section avec des plages et des conditions négatives via NOT.
promise_candidates = Recruit.where (anniversaire: ('1994-01-01'… '2000-01-01'))
Deux points et vous pouvez établir la plage dont vous avez besoin.
promise_candidates = Recruter.where.not (caractère: 'lâche')
Vous pouvez rentrer le ne pas
sur la où
pour filtrer tous les lâches et obtenir uniquement des résultats qui n'ont pas cet attribut spécifique non désiré. Sous le capot, un !=
annule le «filtre» WHERE.
SELECT "recrute". * FROM "recrute" WHERE ("recrute". "Character"! =?) [["Character", "lâche"]]
ordre
Pour ne pas vous ennuyer à mort, faisons-en un rapide.
candidats = Recruit.order (: date_of_birth)
candidats = Recruit.order (: date_au_naissance,: desc)
Appliquer : asc
ou : desc
pour le trier en conséquence. C'est fondamentalement ça, alors passons à autre chose!
limite
Vous pouvez réduire le nombre d'enregistrements renvoyés à un nombre spécifique. Comme mentionné précédemment, la plupart du temps, vous n'aurez pas besoin de récupérer tous les enregistrements. L'exemple ci-dessous vous donnera les cinq premières recrues de la base de données - les cinq premiers identifiants.
five_candidates = Recruit.limit (5)
SELECT "recrute". * FROM "recrute" LIMIT 5
décalage
Si vous vous êtes déjà demandé comment la pagination fonctionne sous le capot, limite
et décalage
-en même temps, faire le dur travail. limite
peut être autonome, mais décalage
dépend de l'ancien.
La définition d'un décalage est surtout utile pour la pagination et vous permet d'ignorer le nombre de lignes souhaité dans la base de données. La page deux d'une liste de candidats pourrait être consultée comme ceci:
Recruter.limite (20) .offset (20)
Le SQL ressemblerait à ceci:
SELECT "recrute". * FROM "recrute" LIMITE 20 OFFSET 20
Encore une fois, nous sélectionnons toutes les colonnes de la Recruter
modèle de base de données, en limitant les enregistrements renvoyés à 20 objets Ruby de Class Recruit et en sautant par-dessus les 20 premiers.
Disons que nous voulons une liste de recrues regroupées par QI. En SQL, cela pourrait ressembler à quelque chose comme ça.
SELECT "recrute". * FROM "recrute" GROUP BY "recrute". "Iq"
Cela vous donnerait une liste où vous verrez quelles recrues possibles ont un QI de 120, puis un autre groupe de 140 et ainsi de suite, quel que soit leur QI, et combien pourraient figurer sous un chiffre spécifique. Ainsi, lorsque deux recrues ont le même QI de 130, elles sont regroupées.
Une autre liste pourrait être regroupée par candidats potentiels souffrant de claustrophobie, de peur des hauteurs ou médicalement impropres à la plongée. La requête Active Record ressemblerait simplement à ceci:
groupe
Candidate.group (: iq)
Lorsque nous comptons le nombre de candidats, nous obtenons un hachage très utile.
Candidate.group (: iq) .count # => 130 => 7, 134 => 4, 135 => 3, 138 => 2, 140 => 1, 141 => 1
Voilà, nous avons sept recrues possibles avec un QI de 130 et une seule avec 141. Le résultat SQL ressemblerait à ceci:
SELECT COUNT (*) AS count_all, iq AS iq FROM "candidats" GROUP BY "candidats". "Iq"
La pièce importante est la PAR GROUPE
partie. Comme vous pouvez le constater, nous utilisons la table des candidats pour obtenir leurs identifiants. Vous pouvez également observer, à partir de cet exemple simple, à quel point les versions Active Record en lecture et en écriture sont plus pratiques. Imaginez que vous le fassiez à la main sur des exemples plus extravagants. Bien sûr, parfois, vous devez le faire, mais tout le temps est clairement une douleur que nous pouvons éviter avec plaisir.
ayant
Nous pouvons spécifier encore plus ce groupe en utilisant AYANT
-une sorte de filtre pour le groupe
. Dans ce sens, ayant
est une sorte de OÙ
clause de GROUPE
. En d'autres termes, ayant
dépend de l'utilisation groupe
.
Recruit.having ('iq>?', 134) .group (: iq)
SELECT "recrute". * FROM "recrute" GROUP BY "recrute". "Iq" AYANT iq> '134'
Nous avons maintenant regroupé nos candidats dans des listes de personnes ayant un QI minimum de 135. Comptons-les pour obtenir des statistiques:
Recruit.having ('iq>?', 134) .group (: iq) .count # => 135 => 3, 138 => 2, 140 => 1, 141 => 1
SELECT COUNT (*) AS count_all, iq AS iq FROM "recrute" GROUP BY "recrute". "Iq" AYANT iq> '134'
Nous pourrions également les combiner et voir, par exemple, quels candidats ayant un QI supérieur à 140 sont liés ou non..
Recruit.having ('iq>?', 140) .group (: état_famille)
SELECT "recrute". * FROM "recrute" GROUP BY "recrute". "Family_status" AYANT iq> '140'
Compter ces groupes est maintenant trop facile:
Recruit.having ('iq>?', 140) .group (: statut_famille) .compte # => "marié" => 2, "célibataire" => 1
SELECT COUNT (*) AS count_all, family_status AS family_status FROM "recrute" GROUP BY "recrute". "Family_status" AYANT iq> '140'
J'espère que ce fut un premier aperçu utile de ce qu'Active Record a à offrir pour rendre vos efforts d'interrogation aussi lisibles et pratiques que possible. Globalement, je dirais que c'est un excellent wrapper qui vous empêche la plupart du temps d'écrire SQL à la main.
Dans le prochain article, nous examinerons quelques chercheurs plus impliqués et développerons ce que nous avons appris jusqu'à présent..