Consultas en Carriles, Parte 1

En este artículo, aprenderá los conceptos básicos de las consultas de Registro Activo y aprenderá algunos aspectos básicos sobre SQL en el camino. Está dirigido a principiantes que desean comenzar a aprender más sobre las consultas de bases de datos en Ruby on Rails.

Los temas

  • Objetos individuales
  • Objetos multiples
  • Condiciones
  • Ordenando
  • Límites
  • Grupo y tener

Registro activo se utiliza para consultar la base de datos. Se puede utilizar con SQL, PostgresSQL y SQLite. Para recuperar registros de su base de datos, tiene varios métodos de búsqueda a su disposición. Lo bueno de ellos es que puede ahorrarse la molestia de escribir SQL en bruto. 

¿Qué hace un método de buscador realmente? Básicamente tres cosas: sus opciones proporcionadas se convierten en una consulta SQL. Luego, la consulta SQL se ejecuta y recupera los datos de la base de datos. Además, para cada fila en esa lista de resultados, obtenemos objetos Ruby recién instanciados del modelo que corresponde con la consulta. 

Si no has jugado con SQL antes, haré todo lo posible para mantener las cosas simples y presentarte lo más básico. Siga los ejemplos de SQL y trate de dar sentido a estas consultas simples. SQL no es realmente una ciencia espacial, la sintaxis solo requiere un poco de tiempo para acostumbrarse. Con suerte, esto abrirá su apetito para buscar algunos tutoriales útiles que llenen los espacios en blanco.. 

Echemos un vistazo a algunos métodos que están a su disposición:

  • encontrar
  • primero
  • último
  • encontrar por
  • todos
  • encontrar
  • find_in_batches
  • dónde
  • orden
  • límite
  • compensar
  • grupo
  • teniendo

Todos estos devolverán una instancia de ActiveRecord :: Relación. ¿Un qué? Es una clase que tiene espacio de nombre dentro del módulo ActiveRecord, y nos permite llamar a múltiples métodos de consulta y encadenarlos. Este objeto es el corazón de la sintaxis de consulta utilizada en Rails. Revisemos la clase de tal objeto y veamos por nosotros mismos:

Carriles

Agent.where (nombre: 'James Bond'). Class # => ActiveRecord :: Relation

Objetos individuales

  • encontrar

Este método le permite proporcionar la identificación principal de un objeto y recupera ese único objeto para usted. Si proporciona una matriz de identificadores, también puede recuperar varios objetos.

Carriles

bond = Agent.find (7)

SQL

SELECCIONE "agentes". * DE "agentes" DONDE "agentes". "Id" =? LÍMITE 1 [["id", 7]]

Esta línea de SQL indica que desea seleccionar todo (*) atributos de la agentes tabla y "filtre" solo el registro con el id 7. Un límite hace que solo devuelva un solo registro de la base de datos.

  • primero, último

Como era de esperar, estos le darán los primeros y últimos registros que se pueden identificar por su clave principal. La parte interesante, sin embargo, es que puede proporcionar un número opcional que le devuelve el primero o el último de ese número de registros. 

Carriles

enemy_agents = SpectreAgent.first (10) enemy_agents = SpectreAgent.last (10)

Bajo el capó, está proporcionando un nuevo límite para el número que proporciona y ordenándolo en forma ascendente o descendente.

SQL

SELECCIONE "spectreagents". * FROM "spectreagents" ORDER BY "spectreagents". "Id" ASC LIMIT 10 SELECT "spectreagents". * FROM "spectreagents" ORDER BY "spectreagents". "Id" DESC LIMIT 10
  • encontrar por

Este buscador devuelve el primer objeto que coincide con la condición que proporcionó.

Carriles

bond = Agent.find_by (last_name: 'Bond')

SQL

SELECCIONE "agentes". * FROM "agentes" DONDE "agentes". "Last_name" =? LÍMITE 1 [["last_name", "Bond"]]

Objetos multiples

Obviamente, a menudo necesitamos iterar sobre una colección de objetos con alguna agenda. Recuperar un solo objeto o unos pocos seleccionados a mano es bueno, pero la mayoría de las veces, queremos que Active Record recupere objetos en lotes. 

Mostrar a los usuarios todo tipo de listas es el pan y la mantequilla para la mayoría de las aplicaciones de Rails. Lo que necesitamos es una herramienta poderosa con una API conveniente para recolectar estos objetos para nosotros, con suerte de una manera que nos permita evitar escribir el SQL involucrado la mayor parte del tiempo.

  • todos

Carriles

mi6_agents = Agentes.todos

SQL

SELECCIONE "agentes". * DE "agentes"

Este método es útil para colecciones relativamente pequeñas de objetos. Trate de imaginar hacer esto en una colección de todos los usuarios de Twitter. No, no es una buena idea. Lo que queremos en cambio es un enfoque más preciso para tamaños de mesa más grandes. 

¡Ir a buscar a toda la mesa no va a escalar! ¿Por qué? Como no solo pediríamos un montón de objetos, también tendríamos que crear un objeto por fila en esta tabla y colocarlos en una matriz en la memoria. Espero que esto no suene como una buena idea! Entonces, ¿cuál es la solución para esto? Lotes Estamos dividiendo estas colecciones en lotes que son más fáciles de procesar en la memoria. Woohoo!

Echemos un vistazo a encontrar y find_in_batches. Ambos son similares pero se comportan de manera diferente en la forma en que producen los objetos en bloques. Aceptan una opción para regular el tamaño del lote. El valor predeterminado es 1.000..

  • encontrar

Carriles

NewRecruit.find_each do | recluta | recruit.start_hellweek end

SQL

SELECCIONE "newrecruits". * FROM "newrruits" ORDER BY "newrecuits". "Id" ASC LIMIT 1000

En este caso, recuperamos un lote predeterminado de 1,000 nuevos reclutas, los entregamos al bloque y los enviamos al infierno semana, uno por uno. Debido a que los lotes están dividiendo las colecciones, también podemos decirles dónde comenzar a través de comienzo. Digamos que queremos procesar 3,000 posibles reclutas de una vez y queremos comenzar con 4,000.

Carriles

NewRecruit.find_each (inicio: 4000, batch_size: 3000) hacer | reclutar | recruit.start_hellweek end

SQL

SELECCIONE "newrecruits". * FROM "newrruits" WHERE ("newrruits". "Id"> = 4000) ORDENADO POR "newrruits". "Id" ASC LIMIT 3000

Para reiterar, primero recuperamos un lote de 3,000 objetos Ruby y luego los enviamos al bloque. comienzo nos permite especificar la identificación de los registros donde queremos comenzar a buscar este lote.

  • find_in_batches

Éste entrega su lote como una matriz al bloque; lo pasa a otro objeto que prefiere tratar con colecciones. El SQL es el mismo aquí..

Carriles

NewRecruit.find_in_batches (inicio: 2700, batch_size: 1350) do | reclutas | field_kitchen.prepare_food (reclutas) final

Condiciones

  • dónde

Necesitamos repasar dónde antes de seguir adelante Esto nos permite especificar condiciones que limitan el número de registros devueltos por nuestras consultas, un filtro para "dónde" recuperar registros de la base de datos. Si has jugado con SQL DÓNDE cláusulas, entonces es posible que simplemente te sientas como en casa, lo mismo con esta envoltura de Ruby. 

En SQL, esto nos permite especificar a qué fila de la tabla queremos afectar, básicamente cuando cumple con algún tipo de criterio. Esta es una cláusula opcional, por cierto. En el SQL sin formato a continuación, seleccionamos solo los reclutas que son huérfanos a través de DÓNDE

Seleccione una fila específica de una tabla.

SQL

SELECCIONAR * DE LOS RECLUTOS DONDE FamilyStatus = 'Huérfano';

Vía dónde, Puede especificar condiciones con cadenas, hashes o matrices. Al juntar todo esto, Active Record le permite filtrar condiciones como las siguientes:

Carriles

promising_candidates = Recruit.where ("family_status = 'orphan'")

SQL

SELECCIONE "reclutas". * DE "reclutas" DONDE (family_status = 'huérfano')

Bastante limpio, ¿verdad? Quiero mencionar que esto sigue siendo una operación de búsqueda; simplemente especificamos cómo queremos filtrar esta lista de inmediato. De la lista de todos los reclutas, esto devolverá una lista filtrada de candidatos huérfanos. Este ejemplo es una condición de cadena. Manténgase alejado de las condiciones de cadena pura, ya que no se consideran seguras debido a su vulnerabilidad a las inyecciones de SQL.

Seguridad de argumentos

En el ejemplo anterior, ponemos la huérfano Variable en la cadena con las condiciones. Esto se considera una mala práctica porque no es seguro. Necesitamos escapar de la variable para evitar esta vulnerabilidad de seguridad. Debería leer acerca de la inyección SQL si esta es una noticia total para usted: su base de datos podría depender de ello.

Carriles

promising_candidates = Recruit.where ("family_status =?", 'huérfano' ")

los ? será reemplazado como el valor de condición por el siguiente valor en la lista de argumentos. Así que el signo de interrogación es básicamente un marcador de posición. También puede especificar múltiples condiciones con múltiples ? y encadenarlos juntos. En un escenario de la vida real, usaríamos un hash params como este:

promising_candidates = Recruit.where ("family_status =?", params [: reclutas])

Si tiene un gran número de condiciones variables, debe usar condiciones de marcador de posición clave / valor.

Carriles

promising_candidates = Recruit.where ("family_status =: preferred_status AND iq> =: required_iq AND charming =: lady_killer", preferred_status: 'orphan', required_iq: 140, lady_killer: true)

SQL

SELECCIONE "reclutas". * FROM "reclutas" DONDE (family_status = 'huérfano' AND iq> = 140 AND lady_killer = true)

El ejemplo anterior es tonto, por supuesto, pero muestra claramente los beneficios de la notación de marcador de posición. La notación hash, en general, es definitivamente la más legible..

Carriles

promising_candidates = Recruit.where (estado_familia: 'huérfano') promising_candidates = Recruit.where ('charming': true)

Como puedes ver, puedes ir con símbolos o cadenas para ti. Cerremos esta sección con rangos y condiciones negativas a través de NO.

Carriles

promising_candidates = Recruit.where (cumpleaños: ('1994-01-01'… '2000-01-01'))

Dos puntos y puedes establecer cualquier rango que necesites..

promising_candidates = Recruit.where.not (carácter: 'cobarde')

Puedes meter el no sobre la dónde para filtrar a todos los cobardes y obtener solo resultados que no tengan ese atributo específico no deseado. Bajo el capó, una != niega el "filtro" DONDE.

SQL

SELECCIONE "reclutas". * DE "reclutas" DÓNDE ("reclutas". "Carácter"! =?) [["Carácter", "cobarde"]]

Ordenando

  • orden

Para no aburrirte hasta morir con esto, hagamos que esto sea rápido.

candidatos = Recruit.order (: date_of_birth)
candidatos = Recruit.order (: date_of_birth,: desc)

Aplicar : asc o : desc para ordenarlo en consecuencia. Eso es básicamente, así que sigamos adelante.!

Límites

  • límite

Puede reducir el número de registros devueltos a un número específico. Como se mencionó anteriormente, la mayoría de las veces no necesitará que se devuelvan todos los registros. El siguiente ejemplo le dará los primeros cinco reclutas en la base de datos, los primeros cinco id..

Carriles

five_candidates = Recruit.limit (5) 

SQL

SELECCIONE "reclutas". * DESDE "reclutas" LIMITE 5
  • compensar

Si alguna vez te has preguntado cómo funciona la paginación bajo el capó, límite y compensar-en conjunción-haz el trabajo duro. límite puede valerse por sí solo, pero compensar depende de la primera.

La configuración de un desplazamiento es principalmente útil para la paginación y le permite omitir el número deseado de filas en la base de datos. La página dos de una lista de candidatos podría verse así:

Carriles

Recruit.limit (20) .offset (20)

El SQL se vería así:

SELECCIONE "reclutas". * DE "reclutas" LÍMITE 20 DESPLAZAMIENTO 20

Una vez más, estamos seleccionando todas las columnas de la Recluta modelo de base de datos, que limita los registros devueltos a 20 objetos Ruby de Reclutamiento de clase y saltando sobre los primeros 20.

Grupo y tener

Digamos que queremos una lista de reclutas que se agrupan por sus IQ. En SQL, esto podría verse así..

SELECCIONE "reclutas". * FROM "recluta" GROUP BY "reclutas". "Iq"

Esto le proporcionará una lista en la que verá qué posibles reclutas tienen un IQ de, digamos, 120, y luego otro grupo de, digamos, 140, y así sucesivamente, sean cuales sean sus IQ y cuántos estarían en un número específico. Así que cuando dos reclutas tienen el mismo coeficiente intelectual de 130, se agruparán. 

Otra lista podría ser agrupada por posibles candidatos que sufren de claustrofobia, miedo a las alturas o que no son médicamente aptos para el buceo. La consulta de Registro Activo se vería así:

  • grupo

Carriles

Grupo de candidatos (: iq)

Cuando contamos el número de candidatos, obtenemos un hash muy útil.

Carriles

Candidate.group (: iq) .count # => 130 => 7, 134 => 4, 135 => 3, 138 => 2, 140 => 1, 141 => 1

Ahí vamos, tenemos siete posibles reclutas con un coeficiente intelectual de 130 y solo uno con 141. El SQL resultante se vería así:

SQL

SELECCIONAR COUNT (*) AS count_all, iq AS iq DE "candidatos" GRUPO POR "candidatos". "Iq"

La pieza importante es la AGRUPAR POR parte. Como puede ver, usamos la tabla de candidatos para obtener sus ID. Lo que también puede observar a partir de este sencillo ejemplo es cuánto más convenientemente leen y escriben las versiones de Active Record. Imagina hacer esto a mano en ejemplos más extravagantes. Claro, a veces tienes que hacerlo, pero todo el tiempo es claramente un dolor que con gusto podemos evitar..

  • teniendo

Podemos especificar este grupo aún más usando TENIENDO-una especie de filtro para el grupo. En ese sentido, teniendo es una especie de DÓNDE cláusula para GRUPO. En otras palabras, teniendo depende de usar grupo.

Carriles

Recruit.having ('iq>?', 134) .group (: iq)

SQL

SELECCIONE "reclutas". * DE "reclutas" GRUPO POR "reclutas". "Iq" TIENE iq> '134'

Ahora hemos agrupado a nuestros candidatos en listas de personas que tienen un coeficiente intelectual mínimo de 135. Vamos a contarlos para obtener algunas estadísticas:

Carriles

Recruit.having ('iq>?', 134) .group (: iq) .count # => 135 => 3, 138 => 2, 140 => 1, 141 => 1

SQL

SELECCIONAR CUENTA (*) AS count_all, iq AS iq FROM "reclutas" GROUP BY "reclutas". "Iq" TIENE iq> '134'

También podríamos mezclar y combinar estos y ver, por ejemplo, qué candidatos que tienen un coeficiente intelectual superior a 140 están vinculados o no en relaciones. 

Carriles

Recruit.having ('iq>?', 140) .group (: family_status)

SQL

SELECCIONE "reclutas". * FROM "recluta" GROUP BY "reclutas". "Estado_familia" QUE TIENE iq> '140'

Contar estos grupos ahora es muy fácil:

Carriles

Recruit.having ('iq>?', 140) .group (: family_status) .count # => "married" => 2, "single" => 1

SQL

SELECCIONAR CUENTA (*) AS count_all, family_status AS family_status DE "reclutas" GROUP BY "reclutas". "Family_status" HAVING iq> '140'

Pensamientos finales

Espero que este sea un primer vistazo útil a lo que Active Record tiene para ofrecer para que sus esfuerzos de consulta sean lo más legibles y convenientes posible. En general, diría que es un excelente envoltorio que evita que usted escriba SQL a mano la mayor parte del tiempo. 

En el siguiente artículo, veremos un par de buscadores más involucrados y ampliaremos lo que hemos aprendido hasta ahora..