Aspectos básicos de SQL

1. Antes de comenzar

Anteriormente, aprendiste a incorporar datos conectados en red a tu app y a usar corrutinas para procesar tareas simultáneas. En esta ocasión, aprenderás otra habilidad fundamental del desarrollo de Android que te permitirá compilar apps de calidad: la persistencia. Incluso si nunca escuchaste el término, es probable que te hayas topado con la persistencia antes, al usar una app. Desde escribir una lista de compras hasta desplazarte por imágenes de hace varios años en una app de fotos, o bien pausar y reanudar un juego, las apps usan la persistencia para brindar una experiencia del usuario fluida. Si bien es fácil que los usuarios den por sentadas estas funciones, la persistencia de datos es una habilidad esencial para que los desarrolladores compilen apps de alta calidad.

Más adelante en esta unidad, obtendrás más información sobre la persistencia en Android y sobre una biblioteca llamada Room que permite que tus apps lean y escriban desde una base de datos. Sin embargo, antes de profundizar en la persistencia en Android, es importante que te familiarices con los conceptos básicos de las bases de datos relacionales y cómo leer y manipular datos con algo llamado SQL (abreviatura de "Structured Query Language", o lenguaje de consulta estructurado). Si ya estás familiarizado con estos conceptos, usa esta lección a modo de repaso para asegurarte tenerlos presentes cuando obtengas información sobre Room. Si no es así, no hay problema. A esta altura, no es esperable que tengas conocimientos sobre las bases de datos. Cuando termines este codelab, tendrás todos los aspectos básicos necesarios para aprender a trabajar con bases de datos en una app para Android.

Requisitos previos

  • Navegar por un proyecto en Android Studio

Qué aprenderás

  • La estructura de una base de datos relacional: tablas, columnas y filas
  • Sentencias SELECT, lo que incluye cláusulas WHERE, ORDER BY, GROUP BY y LIMIT
  • Cómo insertar, actualizar y borrar filas con SQL

Requisitos

  • Una computadora que tenga Android Studio instalado

2. Descripción general de una base de datos relacional

¿Qué es una base de datos relacional?

En informática, una base de datos es simplemente un conjunto estructurado de datos que se pueden escribir y consultar de forma electrónica. Las bases de datos pueden almacenar cualquier tipo de información que puedas representar en una app mediante Kotlin. En dispositivos móviles, las bases de datos se suelen usar para ahorrar datos de una app en ejecución, de modo que se pueda acceder a ellos la próxima vez que se abra la app sin necesidad de recuperar los datos de otra fuente, como Internet. Esto se conoce como persistencia de datos.

Cuando se habla de la persistencia de datos, se suele escuchar el término "base de datos relacional". Una base de datos relacional es un tipo común de base de datos que organiza datos en tablas, columnas y filas. Cuando escribes código de Kotlin, creas clases que representan objetos. En una base de datos relacional, una tabla funciona de la misma manera. Además de representar datos, las tablas también pueden hacer referencia a otras tablas para que puedas establecer relaciones entre ellas. Un ejemplo clásico sería una tabla de "alumnos", "profesores" y "curso". Un curso debería tener un solo profesor, pero un alumno podría tener muchos cursos. Una base de datos puede representar las relaciones entre esas tablas, por lo que, a menudo, escucharás el término base de datos relacional.

ef61dd2663e4da82.png

Las relaciones reales se pueden representar con relaciones entre tablas.

Tablas, columnas y filas

Definir las tablas, o los datos que quieres representar, es solo el primer paso en la creación de una base de datos relacional. También debes pensar en qué información específica se almacena en cada tabla. Las propiedades específicas se representan mediante columnas. Una columna está compuesta por un nombre y un tipo de datos. Ya conoces las propiedades por haber trabajado con clases en Kotlin. Las tablas de SQL funcionan de la misma manera. Una tabla es como una definición de clase, en la que se describe el tipo de "cosa" que quieres representar. Una columna es una propiedad específica de la "cosa" creada con cada entrada de la tabla.

Planta

id

ENTERO

especie

TEXTO (string)

nombre

TEXTO (string)

color

TEXTO (string)

Jardín

id

ENTERO

nombre

TEXTO (string)

longitud

ENTERO

ancho

ENTERO

Las entradas de tablas individuales se denominan filas. Esto es similar a una instancia de una clase en Kotlin. Cada fila tiene datos que se corresponden con los de cada columna. La tabla proporciona la plantilla, pero las filas definen los datos reales almacenados en ella.

id

especie

nombre

color

1

Camellia Sinensis

Planta de té

verde

2

Echinacea Purpurea

Equinácea púrpura

púrpura

3

Ferula Foetida

Asafétida

verde

Clave primaria

En el ejemplo anterior, observa cómo hay una columna para una propiedad "id". Si bien es probable que las especies vegetales de la naturaleza, o lo que representes en tu base de datos, no tengan un id numerado de manera conveniente, es importante que las filas de una tabla de datos tengan algún tipo de identificador único. En general, esto se conoce como clave primaria y es única para cada fila de la tabla. Esto resulta útil si necesitas hacer referencia a filas de una tabla de datos a partir de otra tabla. Por ejemplo, supongamos que hay otra tabla llamada "jardín" en la que quieres asociar un jardín con todas las especies vegetales que contiene. Puedes usar la clave primaria de la tabla de plantas para hacer referencia a una planta a partir de una entrada de la tabla de jardín o cualquier otra tabla de la base de datos.

Las claves primarias posibilitan las relaciones en una base de datos relacional. Si bien no usarás bases de datos con más de una tabla en este curso, debes tener un ID único que te ayudará a consultar, actualizar y borrar elementos existentes de una tabla.

Tipos de datos

Al igual que con la definición de propiedades de las clases de Kotlin, las columnas de una base de datos pueden ser uno de muchos tipos de datos posibles. Una columna puede representar un carácter, una string, un número (con o sin un decimal) o datos binarios. Otros datos, como las fechas y horas, pueden representarse numéricamente o como una string según el caso de uso. Cuando utilices Room, trabajarás principalmente con tipos de Kotlin, pero serán asignados a tipos de SQL en segundo plano.

SQL

Cuando accedas a una base de datos relacional, ya sea por tu cuenta o mediante una biblioteca como Room, necesitarás algo llamado SQL.

¿Qué es SQL? SQL (pronunciado "secuel") significa Structured Query Language (lenguaje de consulta estructurado) y te permite leer y manipular datos en una base de datos relacional. No te preocupes, ya que no necesitarás aprender un lenguaje de programación completamente nuevo para implementar la persistencia en tu app. A diferencia de un lenguaje de programación como Kotlin, SQL consta solo de algunos tipos de sentencias para leer y escribir desde una base de datos. Una vez que aprendas el formato básico de cada una, solo deberás completar los espacios en blanco para la información específica que leas o escribas desde la base de datos.

A continuación, se muestran las sentencias de SQL más comunes y las que usarás para trabajar.

SELECT

Obtiene información específica de una tabla de datos, y los resultados se pueden filtrar y ordenar de varias maneras.

INSERT

Agrega una fila nueva a una tabla.

UPDATE

Actualiza una fila (o filas) existentes de una tabla.

DELETE

Quita una o más filas existentes de una tabla.

Ahora, para poder realizar cualquier operación en SQL, necesitarás una base de datos. En la siguiente pantalla, verás un proyecto de muestra que incluye una base de datos para practicar las consultas de SQL.

3. Código de inicio: base de datos de parques

El código de partida que descargarás es un poco diferente al de los codelabs anteriores. En lugar de compilar en un proyecto existente, te proporcionaremos un proyecto simple de Android Studio que crea una base de datos para practicar consultas de SQL. Después de ejecutar la app una vez, podrás acceder a la base de datos usando una herramienta de Android Studio llamada Inspector de bases de datos.

A fin de obtener el código necesario para este codelab y abrirlo en Android Studio, haz lo siguiente:

Obtén el código

  1. Haz clic en la URL proporcionada. Se abrirá la página de GitHub del proyecto en un navegador.
  2. En esa página, haz clic en el botón Code, que abre un cuadro de diálogo.

5b0a76c50478a73f.png

  1. En el cuadro de diálogo, haz clic en el botón Download ZIP para guardar el proyecto en tu computadora. Espera a que se complete la descarga.
  2. Ubica el archivo en tu computadora (probablemente en la carpeta Descargas).
  3. Haz doble clic en el archivo ZIP para descomprimirlo. Se creará una carpeta nueva con los archivos del proyecto.

Abre el proyecto en Android Studio

  1. Inicia Android Studio.
  2. En la ventana Welcome to Android Studio, haz clic en Open an existing Android Studio project.

36cc44fcf0f89a1d.png

Nota: Si Android Studio ya está abierto, selecciona la opción de menú File > New > Import Project.

21f3eec988dcfbe9.png

  1. En el cuadro de diálogo Import Project, navega hasta donde se encuentra la carpeta de proyecto descomprimido (probablemente en Descargas).
  2. Haz doble clic en la carpeta del proyecto.
  3. Espera a que Android Studio abra el proyecto.
  4. Haz clic en el botón Run 11c34fc5e516fb1c.png para compilar y ejecutar la app. Asegúrate de que funcione como se espera.
  5. Explora los archivos del proyecto en la ventana de herramientas Project para ver cómo se configuró la app.

Antes de pasar a la próxima sección, completa los siguientes pasos para asegurarte de tener listo el proyecto inicial.

  1. Ejecuta la aplicación. Debería mostrar una sola pantalla con este aspecto.

3c62c10fad7c0136.png

  1. En Android Studio, abre el Inspector de bases de datos desde View > Tool Windows > Database Inspector.
  2. Deberías ver una pestaña nueva en la parte inferior con la etiqueta "Database Inspector". Es posible que demore unos segundos en cargarse, pero deberías ver una lista a la izquierda con las tablas, que puedes seleccionar si deseas ejecutar consultas.

8c2b12249b4f652a.png

4. Sentencias SELECT básicas

Para los siguientes ejercicios, ejecutarás las consultas en el Inspector de bases de datos. Asegúrate de seleccionar la tabla correcta en el panel izquierdo (parks). Haz clic en el botón Open New Query Tab y verás un cuadro de texto en el que podrás escribir comandos de SQL.

bb06b5ce9ac4ba72.png

Una instrucción de SQL es un comando, similar a una línea de código, que accede a una base de datos (ya sea mediante la lectura o la escritura). La acción más básica que puedes hacer en SQL es obtener todos los datos de una tabla. Para ello, debes comenzar con la palabra SELECT, que significa que quieres leer datos. Luego, agrega un asterisco (*). Aquí es donde se especifican las columnas que deseas seleccionar, y se utiliza un asterisco para seleccionar todas las columnas. Luego, usa la palabra clave FROM seguida del nombre de la tabla de datos, park. Ejecuta el siguiente comando en el Inspector de bases de datos y observa toda la tabla con todas las filas y columnas.

SELECT * FROM park

Si solo deseas seleccionar una columna específica en lugar de todas las columnas de la tabla de datos, puedes especificar el nombre de una columna.

SELECT city FROM park

También puedes seleccionar varias columnas específicas, separadas por comas.

SELECT name, established, city FROM park

A veces, no es necesario seleccionar todas las filas de una base de datos por completo. Puedes agregar cláusulas a una sentencia de SQL para reducir aún más los resultados.

Una cláusula es LIMIT, que te permite establecer un límite para la cantidad de filas que se muestran. Por lo tanto, en lugar de mostrar los 23 resultados, la siguiente consulta solo muestra los primeros cinco.

SELECT name FROM park
LIMIT 5

Una de las cláusulas más comunes y útiles es WHERE. WHERE te permite filtrar resultados según una o más columnas.

SELECT name FROM park
WHERE type = "national_park"

También hay un operador "no es igual a" (!=). La siguiente consulta muestra una lista de todos los parques de más de 100,000 acres que no son recreation_area. Con las cláusulas WHERE, también puedes usar operadores booleanos, como AND o OR, para agregar más de una condición.

SELECT name FROM park
WHERE type != "recreation_area"
AND area_acres > 100000

Práctica

Las consultas de SQL pueden ser útiles para responder una variedad de preguntas sobre tus datos, y la mejor manera de practicar es escribir tus propias consultas. En los próximos pasos, tendrás la oportunidad de escribir una consulta para responder una pregunta en particular. Asegúrate de probarla en el Inspector de bases de datos antes de continuar.

Todos los ejercicios se basarán en el conocimiento acumulativo de todas las secciones anteriores, y habrá explicaciones al final del codelab para que revises tus respuestas.

5. Funciones comunes de SQL

La primera consulta que escribiste simplemente mostró todas las filas de la base de datos.

SELECT * FROM park

Sin embargo, es posible que no desees mostrar una lista larga de resultados. SQL también ofrece funciones de agregación que pueden ayudarte a reducir los datos a un único valor significativo. Por ejemplo, supongamos que deseas saber la cantidad de filas que hay en la tabla park. En lugar de SELECT * ..., usa la función COUNT() y pasa * (para todas las filas) o un nombre de columna, y la búsqueda mostrará un recuento de todas las filas.

SELECT COUNT(*) FROM park

Otra función de agregación útil es SUM(), que suma los valores de una columna. Esta consulta solo filtra los parques nacionales (ya que son las únicas entradas con una columna park_visitors que no es nula) y suma la cantidad total de visitantes por cada parque.

SELECT SUM(park_visitors) FROM park
WHERE type = "national_park"

Ten en cuenta que puedes usar SUM() en un valor nulo, pero a este solo se lo considerará como cero. La siguiente consulta mostrará el mismo resultado que la anterior. Sin embargo, se recomienda ser lo más específico posible para evitar errores cuando comiences a usar SQL en tus apps.

SELECT SUM(park_visitors) FROM park

Además de agregar valores, existen otras funciones útiles, como MAX() y MIN(), para obtener el valor más grande o más pequeño, respectivamente.

SELECT MAX(area_acres) FROM park
WHERE type = 'national_park'

Cómo obtener valores DISTINCT

Tal vez notes que en algunas filas la columna tiene el mismo valor que otras filas. Por ejemplo, la columna de tipo solo tiene un número finito de valores posibles. Puedes quitar valores duplicados de los resultados de tu consulta con la palabra clave DISTINCT. Por ejemplo, a fin de obtener todos los valores únicos de la columna de tipo, puedes usar la siguiente consulta.

SELECT DISTINCT type FROM park

También puedes usar DISTINCT en una función agregada, para que, en lugar de enumerar los type únicos y contarlos tú mismo, puedas simplemente mostrar el recuento.

SELECT COUNT(DISTINCT type) FROM park

Práctica

Tómate el tiempo necesario para aplicar lo que aprendiste y ver si puedes escribir las siguientes consultas. Asegúrate de usar el Inspector de bases de datos para verificar que funcione el código.

6. Cómo ordenar y agrupar resultados de consultas

En los ejemplos anteriores, puede que te haya resultado difícil encontrar una entrada específica. Afortunadamente, también puedes ordenar los resultados de una sentencia SELECT mediante una cláusula ORDER BY. Agrega una cláusula ORDER BY al final de la consulta después de WHERE (si existe) y simplemente especifica el nombre de la columna por el que deseas ordenar. En el siguiente ejemplo, se obtiene el nombre de cada parque que aparece en la base de datos, pero se ordenan los resultados en orden alfabético.

SELECT name FROM park
ORDER BY name

De forma predeterminada, los resultados se ordenan de manera ascendente, pero puedes agregar las palabras claves ASC o DESC al orden por cláusula para ordenarlos de forma ascendente o descendente. No es necesario que especifiques ASC, ya que la primera consulta muestra los resultados en orden ascendente, pero si deseas obtener resultados en orden descendente, agrega el parámetro DESC al final de la cláusula ORDER BY.

SELECT name FROM park
ORDER BY name DESC

Para facilitar la lectura de los resultados, también puedes agruparlos por columna. Antes de la cláusula ORDER BY (si la hay), puedes especificar GROUP BY y una columna de forma opcional. Eso hace que los resultados estén separados en un subconjunto específico de la columna en GROUP BY y, para cada columna, los resultados se filtrarán y se ordenarán según el resto de la consulta.

SELECT type, name FROM park
GROUP BY type
ORDER BY name

Para que se entienda mejor, veamos un ejemplo. En lugar de contar todos los parques de la base de datos, puedes ver cuántos parques de cada tipo están presentes y obtener un recuento individual para cada uno de ellos.

SELECT type, COUNT(*) FROM park
GROUP BY type
ORDER BY type

Práctica

Tómate el tiempo necesario para aplicar lo que aprendiste y ver si puedes escribir la siguiente consulta. Asegúrate de usar el Inspector de bases de datos para verificar que funcione el código.

Problema 4: Escribe una consulta de SQL para obtener los nombres de los 5 parques principales, junto con el recuento de la mayor cantidad de visitantes, en orden descendente.

7. Cómo insertar y borrar filas

Para poder aprovechar al máximo los datos persistentes en Android con Room, deberás escribir datos. Además de consultar una base de datos, también hay instrucciones de SQL para insertar, actualizar y borrar filas. Necesitarás tener un conocimiento básico de estas cuando aprendas a escribir datos con Room más adelante, en la ruta 2.

Sentencia INSERT

Para agregar una fila nueva, usa la sentencia INSERT. A la sentencia INSERT le siguen la palabra clave INTO y el nombre de la tabla en la que desea agregar una fila. Después de la palabra clave VALUES, debes proporcionar el valor de cada columna (en orden) entre paréntesis, cada una separada por una coma. El formato de una sentencia INSERT es el siguiente:

INSERT INTO table_name
VALUES (column1, column2, ...)

Para agregar una fila a la tabla park, la sentencia INSERT debería verse de la siguiente manera: Los valores coinciden con el orden en que las columnas se definen para la tabla park. Ten en cuenta que algunos de los datos no están especificados. Eso está bien por ahora, ya que puedes actualizar una fila después de insertarla.

INSERT INTO park
VALUES (null, 'Googleplex', 'Mountain View', 12, null, 0, '')

También ten en cuenta que pasas null para el ID. Si bien puedes proporcionar un número específico, esto no es conveniente, ya que la app tendría que hacer un seguimiento del ID más reciente para asegurarse de que no haya duplicados. Sin embargo, puedes configurar tu base de datos para que la clave primaria aumente automáticamente, como se hizo aquí. De esa manera, puedes pasar null, y se elige el siguiente ID de forma automática.

Verifica que se haya creado la entrada mediante una cláusula WHERE para especificar el parque llamado "Googleplex".

SELECT * FROM park
WHERE name = 'Googleplex'

Sentencia UPDATE

Después de que se crea una fila, puedes cambiar su contenido en cualquier momento. Puedes hacerlo mediante una sentencia UPDATE. Al igual que con todas las demás instrucciones de SQL que viste, primero debes especificar el nombre de la tabla. En la cláusula SET, solo establece cada columna que desees cambiar a su nuevo valor.

UPDATE table_name
SET column1 = ...,
column2 = ...,
...
WHERE column_name = ...
...

Para la entrada Googleplex, se actualiza una propiedad existente y se completan otros campos (anteriormente tenían un valor, pero era una string vacía, ""). Puedes actualizar varios (o todos) campos a la vez con una sentencia UPDATE.

UPDATE park
SET area_acres = 46,
established = 1088640000,
type = 'office'
WHERE name = 'Googleplex'

Mira las actualizaciones reflejadas en los resultados de la consulta.

SELECT * FROM park
WHERE name = 'Googleplex'

Sentencia DELETE

Por último, también puedes usar un comando de SQL para borrar filas de la base de datos. Nuevamente, especifica el nombre de la tabla y, tal como lo hiciste con las sentencias SELECT, usa una cláusula WHERE a fin de proporcionar criterios para las filas que deseas borrar. Como una cláusula WHERE puede coincidir con varias filas, puedes borrar varias filas con un solo comando.

DELETE FROM table_name
WHERE <column_name> = ...

Dado que Googleplex no es un parque nacional, prueba usar una sentencia DELETE para quitar esa entrada de la base de datos.

DELETE FROM park
WHERE name = 'Googleplex'

Asegúrate de que se borre la fila mediante una sentencia SELECT. La consulta no mostrará ningún resultado, lo que significa que se borraron correctamente todas las filas que tenían el nombre "Googleplex".

SELECT * FROM park
WHERE name = 'Googleplex'

Simplemente se trata de insertar, actualizar y borrar datos. Todo lo que necesitas saber es el formato del comando de SQL que deseas realizar, y especifica valores que coincidan con las columnas en la base de datos. Cuando presentemos Room en el siguiente codelab, te centrarás principalmente en leer desde una base de datos. La inserción, la actualización y la eliminación de datos se tratarán en detalle en la ruta 2.

8. Soluciones de los ejercicios

Esperamos que los ejercicios de práctica te hayan resultado útiles para perfeccionar la comprensión de los conceptos de SQL. Si tienes alguna dificultad o si quieres revisar las respuestas, no dudes en consultar nuestras respuestas a continuación:

Problema 1: Escribe una consulta de SQL para obtener los nombres de todos los parques con menos de 1,000,000 de visitantes.

En este problema, se solicitan los nombres de parques (una sola columna) con el requisito de que haya menos de 1,000,000 de visitantes, lo que se puede especificar en la cláusula WHERE.

SELECT name FROM park
WHERE park_visitors < 1000000

Problema 2: Escribe una consulta de SQL para obtener la cantidad de ciudades distintas en la tabla park.

El total de una columna se puede calcular con la función COUNT(), pero como solo deseas usar diferentes ciudades (ya que algunas tienen varios parques), puedes utilizar la palabra clave DISTINCT antes del nombre de la columna en la función COUNT().

SELECT COUNT(DISTINCT city) FROM park

Problema 3: Escribe una consulta de SQL para obtener la cantidad total de personas que visitaron parques de San Francisco.

La cantidad total de visitantes se puede calcular con la función SUM(). Además, necesitas una cláusula WHERE para especificar solo los parques ubicados en San Francisco.

SELECT SUM(park_visitors) FROM park
WHERE city = "San Francisco"

Problema 4: Escribe una consulta de SQL para obtener los 5 parques principales (solo los nombres), junto con el recuento de la mayor cantidad de visitantes, en orden descendente.

La consulta debe obtener las columnas name y park_visitors. Los resultados se ordenan en la columna park_visitors, en orden descendente con la cláusula ORDER BY. Como no quieres agrupar los resultados en otra columna y ordenarlos dentro de esos grupos, no se necesita una cláusula GROUP BY.

SELECT name, park_visitors FROM park
ORDER BY park_visitors DESC
LIMIT 5

9. Felicitaciones

Resumen:

  • Las bases de datos relacionales te permiten almacenar datos organizados en tablas, columnas y filas.
  • Puedes recuperar datos de una base de datos mediante la instrucción SELECT de SQL.
  • Puedes usar varias cláusulas en una sentencia SELECT, incluidas WHERE, GROUP BY, ORDER BY y LIMIT, para que tus consultas sean más específicas.
  • Puedes usar funciones de agregación para combinar datos de varias filas en una sola columna.
  • Puedes agregar, actualizar y borrar filas en una base de datos mediante las instrucciones INSERT, UPDATE y DELETE de SQL, respectivamente.

Más información