Volver a la lista de artículos Artículos
Lectura de 15 minutos

Ejercicios SQL para la base de datos Northwind

Cuando se trata de aprender SQL, a muchos principiantes les cuesta adquirir una experiencia práctica que pueda trasladarse a escenarios del mundo real. Abordaremos este reto presentando ejercicios SQL de la base de datos Northwind con soluciones y explicaciones.

En este artículo, cubriremos 16 ejercicios de Northwind extraídos de nuestro curso práctico de SQL Básico Ejercicio de SQL: Una Tienda. Como habrás adivinado, el curso utiliza la clásica base de datos Northwind de Microsoft. Se trata de una base de datos de una pequeña empresa comercial diseñada para que los alumnos de SQL adquieran experiencia práctica. Incluye datos realistas de clientes, productos, proveedores y pedidos, lo que la convierte en un punto de partida ideal para quienes deseen adquirir conocimientos básicos de SQL.

Cada día se generan más datos, por lo que las bases de datos son imprescindibles para almacenar la información de forma segura y organizada. Con SQL, o Lenguaje de Consulta Estructurado, podemos extraer información de los datos contenidos en estas bases de datos. SQL nos permite extraer, analizar y manipular datos para nuestro análisis. Aunque aprender SQL puede parecer un reto al principio, el dominio aumenta con la práctica, como ocurre con cualquier otro idioma.

El propio curso Basic Ejercicio de SQL: A Store ofrece 169 ejercicios interactivos que cubren una amplia gama de temas de SQL. Para familiarizarnos con el tipo de contenido del curso, veremos las preguntas de cada área. Estos ejercicios te ayudarán a mejorar tus habilidades y confianza en SQL.

Visión general de la base de datos Northwind

Comencemos echando un vistazo a la base de datos que utilizaremos:

Ejercicios SQL para la base de datos Northwind

Cada una de las tablas de esta base de datos representa un objeto único y está vinculada a otras tablas mediante claves primarias y externas. La clave primaria identifica de forma exclusiva cada registro de una tabla (por ejemplo, customer_id en la tabla Customer ), lo que nos permite conectar tablas y obtener datos relevantes creando un puente entre ellas.

Veamos brevemente cada tabla:

  • employee: Esta tabla contiene datos sobre los empleados de la empresa, que pueden ser útiles para el análisis de RR.HH. y del rendimiento de los empleados.
  • purchase: Esta tabla contiene los datos de las transacciones entre los clientes y la empresa. Esta información puede ayudar en el análisis de compras, la planificación de existencias y el análisis de ubicación.
  • customer: Esta tabla contiene datos sobre los clientes de la empresa. Puede ser útil para identificar el público objetivo y personalizar los servicios.
  • purchase_item: Esta tabla relaciona las compras con los productos y permite obtener información sobre los precios y las cantidades de los distintos productos de cada pedido.
  • product: Esta tabla muestra datos sobre los productos de la oferta de la empresa. Esto puede proporcionar información sobre los productos más populares y sus detalles.
  • category: Esta tabla contiene información sobre las diferentes categorías de productos, lo que puede ayudar a obtener información sobre las categorías más populares.

Ejercicios sobre la base de datos Northwind

Ahora vamos a sumergirnos en algunos ejercicios prácticos de SQL basados en esta base de datos. Vamos a dividir este artículo en seis secciones que introducen diferentes habilidades SQL. Tenga a mano nuestra hoja de referencia SQL para consultar rápidamente las funciones y la sintaxis que utilizaremos.

1. Consultas de Tabla Única

Para empezar, comenzaremos con lo básico: filtrar y recuperar datos de una única tabla. En estos ejercicios, utilizarás las cláusulas SELECT y WHERE para extraer información específica de tablas individuales. El dominio de estos comandos SQL esenciales sentará una base sólida para construir consultas más complejas más adelante.

Ejercicio 1: Todos los Productos

Muestre todos los datos presentes en la tabla product tabla.

Solución:

SELECT *
FROM product;

Explicación:

En esta consulta

  • SELECT * ordena a SQL que seleccione todas las columnas de la product
  • FROM product especifica la tabla que estamos utilizando.
  • Esta consulta devuelve todas las filas y columnas de la product tabla, proporcionando una vista completa de los detalles de cada producto en la base de datos.

Ejercicio 2: Productos más caros que 3,5

Mostrar los nombres de los productos con un precio unitario mayor o igual a 3,5.

Solución:

SELECT product_name
FROM product
WHERE unit_price >= 3.5;

Explicación:

En esta consulta

  • SELECT product_name especifica que sólo queremos los nombres de los productos.
  • FROM product especifica la tabla que estamos utilizando.
  • WHERE unit_price >= 5 limita la salida a los productos de precio igual o superior a 3,5.
  • Esta consulta es útil para identificar los artículos más caros de la tienda.

Ejercicio 3: Nombres y correos electrónicos de los clientes

Muestra los nombres y correos electrónicos de los clientes. Encontrará el nombre en la columna contact_name y el correo electrónico en la columna contact_email de la tabla customer tabla. Cambie el nombre de las columnas a name y email, respectivamente.

Solución:

SELECT
  contact_name AS name,
  contact_email AS email
FROM customer;

Explicación:

En esta consulta

  • SELECT nombre_contacto AS nombre toma la columna nombre_contacto y la renombra en la salida como nombre. Hace lo mismo con contact_email AS email.
  • FROM cliente especifica la tabla que estamos utilizando.
  • Aliasing con AS hace que los resultados sean más legibles y fáciles de usar. Esto puede ser especialmente útil a la hora de compartir resultados con partes interesadas no técnicas.

Puede encontrar más ejemplos de ejercicios prácticos y sencillos en nuestro artículo Ejercicio de SQL para principiantes: Ejercicios AdventureWorks.

2. Consultas desde múltiples tablas

Esta sección se centra en la combinación de datos de diferentes tablas utilizando JOINs; esto nos permite crear consultas más complejas.

Ejercicio 4: Producto y Categorías

Seleccione nombres de productos junto con sus categorías. Muestre dos columnas: product_name y category_name.

Solución:

SELECT
  product_name,
  category.name AS category_name
FROM product
JOIN category
  ON product.category_id = category.category_id;

Explicación:

En esta consulta

  • SELECT product_name, category.name AS category_name identifica las columnas que se solicitan en nuestra salida.
  • FROM product especifica la primera tabla en nuestro JOIN.
  • JOIN category especifica la segunda tabla en nuestro JOIN.
  • ON product.category_id = category.category_id actúa como puente entre ambas tablas y garantiza que sólo las filas con ID de categoría coincidentes se incluyan en el conjunto de resultados.

Ejercicio 5: Empleados y Compras, Parte 1

Muestre los apellidos y nombres de los empleados que gestionan las compras junto con la dirección shipped_date de la compra.

Solución:

SELECT
  last_name,
  first_name,
  shipped_date
FROM employee
JOIN purchase
  ON employee.employee_id = purchase.employee_id;

Explicación:

En esta consulta

  • SELECT last_name, first_name, shipped_date identifica las columnas que se muestran en nuestra salida.
  • FROM employee indica la primera tabla en nuestro JOIN.
  • JOIN purchase indica la segunda tabla en nuestro JOIN.
  • ON employee.employee_id = purchase.employee_id une ambas tablas en las filas en las que el ID de empleado de la tabla employee coincide con el ID de empleado de la tabla purchase Sólo las filas con IDs coincidentes se incluyen en el conjunto de resultados.

Ejercicio 6: Empleados y Compras, Parte 2

Para cada empleado, muestre su last_name, first_name, y el ID de la(s) compra(s) que ha gestionado (si existen). Asegúrese de mostrar los datos de todos los empleados, aunque no hayan participado en ninguna compra.

Solución:

SELECT
  last_name,
  first_name,
  purchase_id
FROM employee
LEFT JOIN purchase
  ON employee.employee_id = purchase.employee_id;

Explicación:

En esta consulta

  • SELECT last_name, first_name, purchase_id especifica las columnas que se incluirán en la salida.
  • FROM empleado designa la primera tabla de nuestra LEFT JOIN, que contiene datos sobre los empleados.
  • LEFT JOIN purchase designa la segunda tabla de la unión, que contiene datos sobre las compras. El uso de LEFT JOIN garantiza que se incluyan todas las filas de la tabla employee aunque no haya ningún registro coincidente en la tabla purchase
  • ON employee.employee_id = purchase.employee_id conecta ambas tablas, haciendo coincidir los registros en función del ID del empleado. Si un empleado no tiene un registro correspondiente en la tabla purchase se incluirán en el resultado y los campos de compra del conjunto de resultados aparecerán como NULL.

Como puede ver, escribir consultas realmente desarrolla sus habilidades SQL. Otra gran manera de desarrollar sus habilidades SQL es a través de proyectos como los que se encuentran en Proyecto SQL para Principiantes: Tablero de Ventas AdventureWorks.

3. ORDENAR POR: Ordenando sus resultados

La cláusula ORDER BY en SQL es particularmente útil para organizar los resultados de una manera más legible y significativa. Si desea ordenar los datos numéricamente, alfabéticamente o por fecha, esto es muy útil en la presentación de informes y análisis.

Ejercicio 7: Empleados por fecha de nacimiento

Muestre los apellidos, nombres y fechas de nacimiento de los empleados. Ordene los empleados por su fecha de nacimiento en orden descendente (es decir, las fechas más recientes van primero).

Solución:

SELECT
  last_name,
  first_name,
  birth_date
FROM employee
ORDER BY birth_date DESC;

Explicación:

En esta consulta

  • SELECT last_name, first_name, birth_date especifica las columnas que se incluirán en la salida.
  • FROM employee indica la tabla de la que se recuperan los datos.
  • ORDER BY birth_date DESC ordena los resultados por la columna birth_date en orden descendente, de forma que los empleados con las fechas de nacimiento más recientes aparecen en primer lugar. Esto supera la limitación de no tener una columna de edad.

Ejercicio 8: Nombres y precios de los productos

Muestre los nombres y precios unitarios de todos los productos. Muestre primero los productos más baratos.

Solución:

SELECT
  product_name,
  unit_price
FROM product
ORDER BY unit_price;

Explicación:

En esta consulta

  • SELECT product_name, unit_price especifica las columnas que se incluirán en la salida.
  • FROM product identifica la tabla de la que se toman los datos.
  • ORDER BY unit_price ordena los resultados por la columna unit_price en orden ascendente, lo que significa que los productos con los precios más bajos aparecerán primero.
  • Como puede ver, no necesitamos especificar explícitamente ASCending; es el orden por defecto.

Ejercicio 9: Datos de Compras

Mostrar todos los datos de todas las compras. Ordene las filas por la ciudad de envío en orden ascendente y por la fecha de envío en orden descendente.

Solución:

SELECT *
FROM purchase
ORDER BY
  ship_city ASC,
  shipped_date DESC;

Explicación:

En esta consulta

  • SELECT * especifica que todas las columnas de la tabla deben incluirse en la salida.
  • FROM purchase identifica la tabla de la que se recuperan los datos.
  • ORDER BY ship_city ASC, shipped_date DESC ordena los resultados primero por ship_city en orden ascendente (de modo que las ciudades se ordenan alfabéticamente). A continuación, dentro de cada ciudad, los resultados se ordenan además por shipped_date en orden descendente (de modo que las fechas de envío más recientes aparecen en primer lugar para cada ciudad).

Si quieres encontrar más ejercicios con ORDER BY, consulta nuestro artículo 10 Ejercicios para principiantes de Ejercicio de SQL con soluciones.

4. Funciones GROUP BY y Agregado

La siguiente sección ayuda a desarrollar la habilidad de resumir datos a través de la agrupación y la agregación. Utilizando funciones de agregación, podemos obtener información combinando filas y realizando cálculos dentro de cada grupo.

Ejercicio 10: Número de categorías

Cuente el número de categorías presentes en la category tabla. Nombre la columna number_of_categories.

Solución:

SELECT
  COUNT(category_id) AS number_of_categories
FROM category;

Explicación:

En esta consulta

  • SELECT COUNT(category_id) AS number_of_categories cuenta el número de filas que no tienen NULLs en la category_id Muestra la columna resultante como number_of_categories.
  • FROM category Especifica la tabla de la que se recuperan los datos.

Ejercicio 11: Compras de Clientes

Cuente el número de compras realizadas por cada cliente. Muestra las columnas customer_id, contact_name, y purchases_number. Ignora los clientes que no estén presentes en la purchase tabla.

Solución:

SELECT
  purchase.customer_id,
  contact_name,
  COUNT(*) AS purchases_number
FROM purchase
JOIN customer
  ON purchase.customer_id = customer.customer_id
GROUP BY
  purchase.customer_id,
  contact_name;

Explicación:

En esta consulta

  • SELECT purchase.customer_id, contact_name, COUNT(*) AS purchases_number especifica las columnas que se incluirán en la salida. Recupera el ID y el nombre de contacto de cada cliente, junto con el recuento de sus compras (etiquetado purchases_number).
  • FROM purchase identifica la primera tabla en el JOIN.
  • JOIN customer especifica la segunda tabla en JOIN.
  • ON purchase.customer_id = customer.customer_id vincula las dos tablas, haciendo coincidir las filas en las que el customer_id es el mismo en ambas tablas y garantizando que sólo se incluyen las compras asociadas a los clientes existentes.
  • GROUP BY purchase.customer_id, contact_name agrupa los resultados por ID y nombre de contacto de cada cliente, permitiendo que la función COUNT(*) calcule el número de compras de cada cliente.

Ejercicio 12: Ingresos para pares de clientes y empleados

Para cada cliente y empleado, encuentre el precio total de todas las compras que realizaron y a las que está asignado un empleado determinado. Muestre tres columnas: customer_id, employee_id, y el precio total de las compras. Cambie el nombre de la tercera columna a total_purchases_price.

Solución:

SELECT
  customer_id,
  employee_id,
  SUM(total_price) AS total_purchases_price
FROM purchase
GROUP BY
  customer_id,
  employee_id;

Explicación:

En esta consulta:

  • SELECT customer_id, employee_id, SUM(total_price) AS total_purchases_price especifica las columnas a incluir en el resultado, mostrando cada customer_id y employee_id junto con la suma de total_price para las compras asociadas a cada combinación. Esta suma se etiqueta como total_purchases_price en el resultado.
  • FROM compra identifica la tabla de la que se toman los datos.
  • GROUP BY customer_id, employee_id agrupa los resultados por customer_id y employee_id, permitiendo a la función SUM(total_price) calcular el precio total de compra para cada combinación única de cliente-empleado.

5. Otras funciones SQL

A continuación, cubriremos brevemente algunas funciones SQL más que se incluyen en el curso pero que también son útiles en el análisis cotidiano. Estas funciones, cuando se combinan con otras discutidas en este artículo, pueden ayudarle a mejorar sus habilidades SQL.

Ejercicio 13: Fecha de Contratación Desconocida

Mostrar los apellidos y nombres de los empleados con fecha de contratación desconocida.

Solución:

SELECT
  last_name,
  first_name
FROM employee
WHERE hire_date IS NULL;

Explicación:

En esta consulta:

  • SELECT last_name, first_name especifica las columnas a incluir en la salida.
  • FROM employee indica la tabla de la que se toman los datos.
  • WHERE hire_date IS NULL filtra los resultados para incluir sólo a los empleados cuya hire_date es NULL, lo que significa que su fecha de contratación es desconocida.

Ejercicio 14: Precio medio por categoría

Para cada categoría, cuente el precio medio de sus productos. Muestra sólo las categorías cuyo precio unitario medio sea mayor que el precio unitario medio global. Nombre las columnas category_name y average_price.

Solución:

SELECT
  category.name AS category_name,
  AVG(unit_price) AS average_price
FROM product
JOIN category
  ON product.category_id = category.category_id
GROUP BY category.name
HAVING AVG(unit_price) > (
  SELECT AVG(unit_price)
  FROM product
);

Explicación:

En esta consulta

  • SELECT category.name AS category_name, AVG(unit_price) AS average_price especifica las columnas que se incluirán en la salida.
  • FROM product identifica la primera tabla en el JOIN.
  • JOIN category especifica la segunda tabla en JOIN.
  • ON product.category_id = category.category_id vincula las dos tablas, garantizando que sólo se incluyan los productos con ID de categoría coincidentes de ambas tablas.
  • GROUP BY category.name agrupa los resultados por nombre de categoría, lo que permite a la función AVG(unit_price) calcular el precio medio de cada categoría.
  • HAVING AVG(unit_price) > (SELECT AVG(unit_price) FROM product) filtra los resultados agrupados para incluir sólo las categorías en las que el precio medio de los productos es superior al precio medio global de todos los productos de la categoría. product
  • La subconsulta (SELECT AVG(unit_price) FROM product) calcula el precio unitario medio global de todos los productos.

6. SQL avanzado Características

Por último, veremos algunas consultas SQL más avanzadas. Mientras lo hacemos, haremos referencia a las funciones que hemos visto anteriormente.

Ejercicio 15: Categorías con productos activos

Para cada categoría, muestre el número de sus productos que no están discontinuados (continúan o hay un NULL en la columna discontinued ). Muestre las columnas denominadas category_name y products_number. Muestre sólo las filas para las cuales el número de tales productos es mayor que 1. Además, no muestre la fila para la categoría Otros.

Solución:

SELECT
  category.name AS category_name,
  COUNT(product_id) AS products_number
FROM product
JOIN category
  ON product.category_id = category.category_id
WHERE category.name <> 'Other'
  AND discontinued IS NOT TRUE
GROUP BY category.name
HAVING COUNT(product_id) > 1;

Explicación:

En esta consulta:

  • SELECT category.name AS category_name, COUNT(product_id) AS products_number especifica las columnas que se incluirán en la salida.
  • FROM product identifica la primera tabla en JOIN.
  • JOIN category especifica la segunda tabla en JOIN.
  • ON product.category_id = category.category_id vincula las dos tablas, garantizando que sólo se incluyan los productos con ID de categoría coincidentes de ambas tablas.
  • WHERE category.name <> 'Other' AND discontinued IS NOT TRUE filtra los resultados para excluir las categorías denominadas "Other" y para incluir únicamente los productos que no están descatalogados.
  • GROUP BY category.name agrupa los resultados por nombre de categoría, lo que permite a la función COUNT(product_id) contar el número de productos de cada categoría.
  • HAVING COUNT(product_id) > 1 filtra los resultados agrupados para incluir sólo las categorías con más de un producto.

Ejercicio 16: Estadísticas de vinos

Todos los vinos de la tabla de productos tienen un nombre que empieza por Wine. Halla el:

  • Número de productos de este tipo en la tabla (products_number).
  • Número total de unidades en stock (units_number).
  • Precio medio del producto (average_price).
  • Relación entre el precio máximo y el precio mínimo (max_to_min_ratio).
  • Diferencia entre el precio máximo y el precio medio (max_to_average).
  • Diferencia entre el precio medio y el precio mínimo (average_to_min).

Redondea las cuatro últimas columnas a dos decimales.

Solución:

SELECT
  COUNT(*) AS products_number,
  SUM(units_in_stock) AS units_number,
  ROUND(AVG(unit_price), 2) AS average_price,
  ROUND(MAX(unit_price) / MIN(unit_price), 2) AS max_to_min_ratio,
  ROUND(MAX(unit_price) - AVG(unit_price), 2) AS max_to_average,
  ROUND(AVG(unit_price) - MIN(unit_price), 2) AS average_to_min
FROM product
WHERE product_name LIKE 'Wine%';

Explicación:

En esta consulta:

  • Primero vamos a desglosar la sentencia SELECT:
    • products_number calcula el número total de productos.
    • units_number suma el número total de unidades en stock.
    • average_price obtiene el precio unitario medio de los productos, redondeado a 2 decimales.
    • max_to_min_ratio halla la relación entre el precio unitario máximo y el precio unitario mínimo, redondeado a 2 decimales.
    • max_to_average halla la diferencia entre el precio unitario máximo y el precio unitario medio, redondeada a 2 decimales.
    • average_to_min calcula la diferencia entre el precio unitario medio y el precio unitario mínimo, redondeada a 2 decimales.
  • FROM product identifica la tabla de la que se extraen los datos.
  • WHERE product_name LIKE 'Wine%' filtra los resultados para incluir sólo los productos cuyos nombres empiezan por 'Wine'.

¿Quieres más ejercicios Northwind para estudiantes de SQL?

Practicando la escritura de consultas, puedes fortalecer tus habilidades SQL. Trabajar con datos del mundo real, como la Base de Datos Northwind, te da experiencia accediendo y evaluando información rápidamente.

Si los ejercicios Northwind de este artículo te han parecido interesantes, no dejes de consultar el curso Básico Ejercicio de SQL: Una Tienda. Encontrarás muchos más ejercicios interactivos. También tenemos Proyecto SQL para Portafolio: Northwind Store, un artículo sobre la construcción de un proyecto basado en la base de datos Northwind. ¡Feliz aprendizaje!