Sitemap

Taller teórico práctico de SQL

19 min readJul 24, 2026

--

Estas semanas estuve trabajando en un taller de SQL, donde vimos estos temas:

  • Conceptos básicos de bases de datos.
  • Consultas básicas como SELECT.
  • En la segunda parte seguimos con consultas avanzadas como funciones de ventana y CTE.

Dejo el material junto a las clases grabadas con el fin de que más personas pueden aprender sobre este tema, al final hay una serie de ejercicios prácticos para repasar lo aprendido.

Clase 1: https://www.youtube.com/watch?v=n5BiLSgem_4

Get Valentindome’s stories in your inbox

Join Medium for free to get updates from this writer.

Clase 2: https://www.youtube.com/watch?v=YX-cVQG3LGg

Introducción

¿Qué es una Base de Datos?

Una base de datos es un conjunto estructurado de información de cualquier naturaleza, independientemente del soporte físico o digital en el que se encuentre (directorio telefónico, un catálogo de fichas bibliográficas, un registro de inventario). Una base de datos computarizada, la cual se define como un conjunto de datos organizados de manera lógica y almacenados en la memoria de una computadora. Para que los usuarios y las aplicaciones puedan interactuar con esta información, se requiere un Sistema de Administración de Bases de Datos (DBMS, por sus siglas en inglés, Database Management System).

¿Qué es un DBMS?

Es la herramienta de software especializada encargada de estructurar el almacenamiento físico, garantizar la seguridad y permitir la recuperación eficiente de los datos, abstrayendo la complejidad técnica del hardware subyacente.

¿Qué tipo de Bases de Datos existen?

La clasificación principal e histórica se divide entre bases de datos relacionales (RDBMS) y no relacionales (NoSQL), pero el ecosistema moderno es mucho más amplio. Las relacionales organizan la información en tablas estrictamente estructuradas con filas y columnas interconectadas mediante relaciones lógicas. Las NoSQL ofrecen modelos flexibles adaptados a necesidades específicas, destacando las

  • documentales (basadas en estructuras tipo JSON)
  • clave-valor (altamente veloces para caché)
  • grafos (para mapear relaciones complejas como redes sociales)
  • orientadas a columnas anchas (para analítica masiva).

¿Cuándo conviene usar cada una de ellas?

  • Bases de datos relacionales: Esquema de datos estable, requerimiento de una estricta consistencia transaccional y cumplimiento riguroso de las propiedades ACID (Atomicidad, Consistencia, Aislamiento y Durabilidad). Son el estándar indiscutible en sistemas financieros, de facturación o plataformas ERP.
  • Bases de datos NoSQL: Manejo de volúmenes masivos de datos no estructurados o semiestructurados, necesidad de una alta escalabilidad horizontal para soportar millones de usuarios concurrentes y esquemas que evolucionan con rapidez. Ideales para catálogos de e-commerce dinámicos, plataformas de streaming, redes sociales y análisis de Big Data en tiempo real.

¿Cuáles son las bases de datos más utilizadas a día de hoy?

https://nubecolectiva.com/blog/10-bases-datos-febrero-2026/

¿Cómo se compone una base de datos?

A nivel lógico, una base de datos relacional se organiza mediante esquemas que contienen tablas, las cuales representan entidades del mundo real. Cada tabla está compuesta por filas (también llamadas registros o tuplas) que contienen los datos individuales, y columnas (campos o atributos) que definen la naturaleza y el tipo de dato que se puede almacenar (enteros, texto, fechas, etc.). Para asegurar la consistencia, el sistema se apoya en restricciones de integridad, principalmente claves primarias, que identifican de forma unívoca a cada registro, y claves foráneas, que establecen los vínculos y la integridad referencial entre las distintas tablas del sistema.

¿Qué es SQL?

SQL, que significa Structured Query Language (Lenguaje de Consulta Estructurado), es el lenguaje de programación estándar internacional diseñado para interactuar, administrar y manipular sistemas de gestión de bases de datos relacionales. Se diferencia de los lenguajes tradicionales por ser de naturaleza declarativa: el desarrollador escribe una sentencia especificando con exactitud qué conjunto de datos desea obtener o qué acción quiere realizar, delegando por completo en el motor de la base de datos la tarea de descifrar el algoritmo y los pasos mecánicos necesarios para ejecutar la orden de la manera más eficiente posible.

¿Cuáles son las ventajas y desventajas del SQL?

Su principal ventaja radica en su estandarización universal, lo que permite aplicar los mismos conocimientos en casi cualquier motor relacional, sumado a su inigualable madurez para realizar consultas analíticas complejas y garantizar la consistencia absoluta de los datos sin riesgo de corrupción. Sus desventajas se centran en la rigidez de sus esquemas predefinidos, que requieren costosas migraciones cuando el negocio cambia rápidamente, y en su dificultad intrínseca para escalar de forma horizontal (añadiendo más servidores en malla) en comparación con los sistemas NoSQL, obligando usualmente a un escalado vertical que resulta más costoso.

¿Cuáles son las implementaciones más utilizadas de SQL? ¿Cuándo usar cada una?

Las implementaciones más extendidas y consolidadas son PostgreSQL, MySQL (junto a su rama comunitaria MariaDB), Microsoft SQL Server y Oracle Database. PostgreSQL debe elegirse para proyectos complejos que exigen alta concurrencia, extensibilidad, cumplimiento estricto de estándares y manejo de tipos de datos avanzados; MySQL es la opción ideal para aplicaciones web estándar, blogs y plataformas donde la velocidad de lectura y la simplicidad de despliegue sean prioritarias; SQL Server es el estándar natural para entornos corporativos integrados con la infraestructura y herramientas de Microsoft; mientras que Oracle queda reservado para misiones críticas de grandes corporaciones que demandan la máxima seguridad, soporte dedicado y rendimiento extremo a gran escala.

¿Cómo es la arquitectura de una BD?

  • 1. Gestor de Conexiones: Recibe la consulta desde la aplicación, verifica la identidad del usuario y valida que tenga los permisos necesarios para acceder a los datos.
  • 2. Analizador (Parser): Revisa la “ortografía” (sintaxis) del código SQL y verifica en su diccionario que las tablas y columnas mencionadas realmente existan (semántica).
  • 3. Optimizador de Consultas: Es el “cerebro” del sistema. Analiza todas las formas posibles de encontrar los datos y elige el camino más rápido y eficiente, creando un Plan de Ejecución.
  • 4. Motor de Ejecución: Actúa como director. Toma el plan elegido por el optimizador y da las órdenes paso a paso para ir a buscar la información.
  • 5. Gestor de Almacenamiento y Caché: Busca los datos. Primero se fija si ya están cargados en la memoria rápida (RAM/Caché) por una consulta reciente; si no están, va a leerlos físicamente al disco duro (que es más lento) y los sube a la memoria.
  • 6. Retorno de Resultados: Toma los datos encontrados, les da el formato final solicitado y se los devuelve al usuario o a la aplicación.

¿Qué es el DDL (Data Definition Language)?

Es el subconjunto de comandos de SQL que se utiliza para definir, modificar o eliminar las estructuras de la base de datos (como las tablas, los índices o los esquemas). No maneja los datos en sí, sino el “molde” que los contiene. Sus comandos principales son CREATE (crear), ALTER (modificar) y DROP (eliminar).

¿Qué es el DML (Data Manipulation Language)?

Es el subconjunto de comandos utilizado para manipular la información que vive dentro de las estructuras creadas por el DDL. Permite insertar, actualizar o borrar registros específicos de una tabla. Sus comandos principales son INSERT (agregar filas), UPDATE (modificar valores de filas existentes) y DELETE (borrar filas). (Nota técnica: el comando SELECT a veces se clasifica dentro de su propia categoría llamada DQL - Data Query Language, pero históricamente se lo engloba en el uso diario del DML).

SELECT

¿Para qué sirve un SELECT?

La cláusula SELECT es la más utilizada de SQL, diseñada para recuperar, proyectar y transformar los datos almacenados en una o más tablas de la base de datos. Funciona como un mecanismo de extracción que permite al usuario seleccionar qué columnas específicas desea visualizar, aplicar alias para renombrar los campos sobre la marcha, realizar cálculos matemáticos combinando columnas y formatear la salida final de la información para que sea consumida de forma clara por una aplicación o reporte.

¿Cuál es la sintaxis y parámetros para un SELECT?

SELECT 
DISTINCT
A AS a
, B AS b
, C AS c
FROM
X
ORDER BY
A DESC
, B ASC
LIMIT
10;
SELECT 
DISTINCT
usuarios.email AS MAIL DE USUARIO
, usuarios.usauario AS NOMBRE
FROM
usuarios -- tabla
, compras
;
  • 1. Palabra Clave SELECT (Obligatorio): Esta es la instrucción inicial que define la operación de extracción de datos y especifica cuáles serán las columnas que se presentarán en el resultado final.
  • 2. Lista de Columnas/Atributos (Obligatorio): Se detallan los nombres específicos de las columnas que se desean recuperar de la base de datos, separándolas por comas. Alternativamente, se puede utilizar el asterisco (*) como un atajo para indicar que se deben extraer la totalidad de los atributos o columnas existentes en la tabla de origen.
  • 3. Palabra Clave FROM (Obligatorio): Esta cláusula es esencial para identificar y especificar la fuente de los datos, indicando el nombre exacto de la tabla de origen de donde se extraerá la información solicitada.
  • 4. Parámetro Opcional DISTINCT: Este modificador opcional se incorpora inmediatamente después de la palabra clave SELECT y antes de la lista de columnas. Su función exclusiva es filtrar el conjunto de resultados para eliminar cualquier registro duplicado, devolviendo únicamente valores únicos.
  • 5. Cláusulas de Limitación de Filas (Opcional): Dependiendo del motor de base de datos relacional utilizado, se pueden incorporar cláusulas específicas para acotar la cantidad máxima de filas devueltas en el resultado, lo cual es útil para la paginación o la visualización de muestras de datos:
  • LIMIT (Motores como PostgreSQL o MySQL): Esta palabra clave se añade al final de la consulta y especifica un número entero para limitar el total de registros devueltos.

https://www.db-fiddle.com/

-- DDL: Creación de la estructura de la tabla con métricas defensivas
CREATE TABLE estadisticas_mundial (
puesto_ranking INT,
equipo VARCHAR(50),
confederacion VARCHAR(20),
puntos_fifa INT,
goles_anotados INT,
partidos_jugados INT,
porterias_a_cero INT,
goles_recibidos INT,
paradas_portero INT
);
-- DML: Inserción de los datos combinados en la tabla
INSERT INTO estadisticas_mundial
(puesto_ranking, equipo, confederacion, puntos_fifa, goles_anotados, partidos_jugados, porterias_a_cero, goles_recibidos, paradas_portero)
VALUES
(1, 'España', 'UEFA', 1554, 4, 7, 2, 0, 3),
(2, 'Portugal', 'UEFA', 1443, 6, 5, 1, 1, 3),
(4, 'Suiza', 'UEFA', 1322, 5, 5, 1, 2, 5),
(6, 'Alemania', 'UEFA', 1275, 9, 6, 0, 2, 2),
(7, 'Francia', 'UEFA', 1188, 6, 6, 1, 1, 2),
(8, 'Marruecos', 'CAF', 1179, 2, 4, 1, 1, 5),
(9, 'Ecuador', 'CONMEBOL', 1176, 0, 3, 1, 1, 9),
(10, 'Argentina', 'CONMEBOL', 1142, 5, 5, 2, 0, 1),
(11, 'Uruguay', 'CONMEBOL', 1140, 3, 4, 0, 3, 4),
(12, 'República de Corea', 'AFC', 1135, 2, 4, 0, 2, 8),
(13, 'Inglaterra', 'UEFA', 1134, 4, 5, 1, 2, 3),
(14, 'EE. UU.', 'CONCACAF', 1133, 6, 4, 1, 1, 3),
(16, 'Colombia', 'CONMEBOL', 1082, 4, 5, 1, 1, 3),
(17, 'Brasil', 'CONMEBOL', 1065, 4, 5, 1, 1, 6),
(19, 'Canadá', 'CONCACAF', 1044, 7, 6, 2, 1, 2),
(20, 'Senegal', 'CAF', 1025, 3, 4, 0, 6, 8),
(21, 'México', 'CONCACAF', 979, 3, 4, 2, 0, 4),
(23, 'Países Bajos', 'UEFA', 976, 7, 5, 0, 3, 9),
(26, 'Japón', 'AFC', 949, 6, 4, 1, 2, 7);
CREATE TABLE jugadores_destacados (
id_jugador INT,
nombre VARCHAR(100),
equipo VARCHAR(50),
fecha_nacimiento DATE,
altura_metros DECIMAL(3,2),
es_capitan BOOLEAN,
valor_mercado_millones DECIMAL(5,1)
);
-- DML: Inserción de datos variados
INSERT INTO jugadores_destacados (id_jugador, nombre, equipo, fecha_nacimiento, altura_metros, es_capitan, valor_mercado_millones) VALUES
(1, 'Lionel Messi', 'Argentina', '1987-06-24', 1.70, TRUE, 35.5),
(2, 'Kylian Mbappé', 'Francia', '1998-12-20', 1.78, FALSE, 180.0),
(3, 'Manuel Neuer', 'Alemania', '1986-03-27', 1.93, TRUE, 12.0),
(4, 'Virgil van Dijk', 'Países Bajos', '1991-07-08', 1.95, TRUE, 30.0),
(5, 'Alphonso Davies', 'Canadá', '2000-11-02', 1.85, FALSE, 70.0),
(6, 'Yassine Bounou', 'Marruecos', '1991-04-05', 1.95, FALSE, 18.0),
(7, 'Christian Pulisic', 'EE. UU.', '1998-09-18', 1.77, TRUE, 32.0),
(8, 'Enner Valencia', 'Ecuador', '1989-11-04', 1.77, TRUE, 3.0),
(9, 'Pedri', 'España', '2002-11-25', 1.74, FALSE, 90.0);

Ejercicios

1. Realizar una consulta para obtener todas las columnas de todos los equipos del mundial.

SELECT 
*
FROM
estadisticas_mundial;

2. Realizar una consulta que devuelva únicamente el nombre del equipo, su confederación y los goles anotados (en ese orden).

SELECT 
equipo,
confederacion,
goles_anotados
FROM
estadisticas_mundial;

3. Tomando la consulta anterior, ordenar los resultados para que el equipo con más goles anotados aparezca primero.

SELECT 
equipo,
confederacion,
goles_anotados
FROM
estadisticas_mundial
ORDER BY
goles_anotados DESC;

4. Obtener el nombre y los puntos FIFA del equipo con menor cantidad de puntos en la tabla.

SELECT 
equipo,
puntos_fifa
FROM
estadisticas_mundial
ORDER BY
puntos_fifa ASC
LIMIT 1;

5. Realizar una consulta que devuelva el Top 3 de los equipos con mayor cantidad de puntos FIFA.

SELECT 
equipo,
puntos_fifa
FROM
estadisticas_mundial
ORDER BY
puntos_fifa DESC
LIMIT 3;

WHERE

¿Para qué sirve un WHERE?

La cláusula WHERE cumple la función esencial de filtrar los registros de una consulta, imponiendo una o varias condiciones lógicas que cada fila de la tabla debe cumplir estrictamente para ser incluida en el resultado final.

¿Cuál es la sintaxis y parámetros para un WHERE?

Se posiciona de forma obligatoria inmediatamente después de la cláusula FROM y antes de cualquier instrucción de ordenamiento o limitación. Se compone de expresiones condicionales donde se utilizan operadores de comparación clásicos (=, <>, >, <, >=, <=), operadores lógicos para encadenar criterios (AND, OR, NOT), filtros de rangos (BETWEEN), evaluación de listas de valores (IN), validación de nulos (IS NOT NULL) y coincidencia de patrones de texto (LIKE o ILIKE).

SELECT 
A
, B
FROM
X
WHERE
A = 'valor_buscado'
AND B > 50
AND C IN (1, 2, 3);

¿Qué sucede en el motor de la base de datos al utilizar un WHERE?

El motor aplica el filtro WHERE en las primeras etapas del proceso de ejecución, operando directamente sobre las filas individuales de la tabla identificada en el FROM. Al evaluar los criterios fila por fila (o aprovechando estructuras aceleradoras si existen), el sistema descarta de forma inmediata todos los registros cuya condición resulte falsa o de valor desconocido; esto impide que filas innecesarias ocupen memoria temporal o tiempo de procesamiento en las fases subsiguientes de ordenamiento, agrupamiento o proyección de columnas, actuando como el principal filtro reductor de la consulta.

Ejercicios

6. Obtener el Top 3 de los equipos con mayor puntaje FIFA, pero considerando *solo* a los equipos de la confederación ‘CONMEBOL’.

SELECT 
equipo,
puntos_fifa
FROM
estadisticas_mundial
WHERE
confederacion = 'CONMEBOL'
ORDER BY
puntos_fifa DESC
LIMIT 3;

Realizar una consulta que devuelva únicamente el nombre del equipo y sus puntos FIFA, filtrando los registros para mostrar solo aquellos que pertenecen a la confederación ‘UEFA’.

SELECT 
equipo,
puntos_fifa
FROM
estadisticas_mundial
WHERE
confederacion = 'UEFA';

Realizar una consulta para obtener el nombre, el equipo al que pertenecen y el valor de mercado de los jugadores que sean capitanes de su selección y tengan un valor de mercado que supere los 20 millones.

SELECT 
nombre,
equipo,
valor_mercado_millones
FROM
jugadores_destacados
WHERE
es_capitan = TRUE
AND valor_mercado_millones > 20;

Realizar una consulta que liste el equipo, las porterías a cero y los goles anotados de aquellos países que hayan mantenido su arco en cero 2 o más veces, o que hayan anotado más de 6 goles en total.

SELECT 
equipo,
porterias_a_cero,
goles_anotados
FROM
estadisticas_mundial
WHERE
porterias_a_cero >= 2
OR goles_anotados > 6;

Índices

¿Qué es un índice en una base de datos?

Un índice es una estructura de datos física y auxiliar, mantenida internamente por el motor de la base de datos de forma paralela a las tablas, cuyo propósito exclusivo es optimizar y acelerar drásticamente la velocidad de búsqueda y recuperación de registros. Funciona de manera análoga al índice alfabético situado al final de un libro técnico: en lugar de obligar al motor a leer la tabla entera página por página de principio a fin (operación costosa conocida como Full Table Scan), el índice le indica con precisión la ubicación exacta en el almacenamiento donde se aloja la fila solicitada, transformando búsquedas masivas en accesos directos casi instantáneos.

¿Cómo se configura un índice en una base de datos?

La creación de un índice se gestiona mediante instrucciones de definición de datos (DDL), donde el desarrollador define un nombre identificativo para el índice, la tabla sobre la cual se va a construir y la columna o conjunto de columnas cuyos valores se indexarán para agilizar las búsquedas frecuentes. Es importante destacar que cuando se define una clave primaria (PRIMARY KEY) o una restricción de unicidad (UNIQUE), la inmensa mayoría de los motores de bases de datos generan automáticamente un índice subyacente sin necesidad de intervención manual.

CREATE INDEX 
idx_busqueda_rapida
ON
nombre_tabla (columna_frecuente);

¿Qué tener en cuenta al momento de hacer una consulta en una base de datos con índices?

El aspecto crítico a ponderar es el equilibrio de rendimiento: los índices aceleran sustancialmente las operaciones de lectura, pero penalizan el rendimiento de cualquier operación de escritura (INSERT, UPDATE, DELETE), debido a que el motor se ve obligado a reorganizar y actualizar el índice en tiempo real cada vez que los datos sufren alteraciones. Asimismo, para asegurar que una consulta aproveche eficazmente un índice existente, se debe evitar por completo la aplicación de funciones, modificaciones o cálculos sobre la columna indexada dentro de la cláusula WHERE, dado que esto destruye la capacidad del motor de realizar una búsqueda indexada y lo fuerza a escanear la tabla completa. En resumen, se deben indexar solo los campos que se consultan con frecuencia y mantener las columnas limpias de transformaciones en los filtros.

Ejercicios

Escribir la instrucción DDL necesaria para crear un índice llamado `idx_equipo_estadisticas` sobre la columna `equipo` en la tabla `estadisticas_mundial`.

CREATE INDEX idx_equipo_estadisticas
ON estadisticas_mundial (equipo);

Escribir la instrucción DDL para crear un índice llamado `idx_jugador_mercado` aplicado a la columna `valor_mercado_millones` dentro de la tabla `jugadores_destacados`.

CREATE INDEX idx_jugador_mercado
ON jugadores_destacados (valor_mercado_millones);

Funciones de Agregación

¿Qué es una función de agregación?

Una función de agregación es una operación matemática especial que toma como entrada un conjunto de valores distribuidos en múltiples filas de una columna específica y los procesa para consolidarlos en un único valor resumido de salida. Son las herramientas primordiales utilizadas en la base de datos para realizar análisis cuantitativos, resúmenes estadísticos y generación de indicadores clave de rendimiento globales sin necesidad de extraer registros individuales hacia la aplicación.

¿Cómo funcionan las funciones de agregación?

Estas funciones operan examinando secuencialmente el bloque de registros seleccionado y acumulando los valores de forma interna; por norma estándar internacional, ignoran de manera automática los valores nulos (NULL) para no distorsionar los cálculos matemáticos (con la excepción parcial del conteo genérico de filas). Al finalizar el recorrido del conjunto de datos, colapsan la individualidad de las filas de origen y devuelven una única fila que contiene el resultado matemático final del cálculo ejecutado, reduciendo múltiples filas a una sola métrica representativa.

¿Cuáles son las funciones de agregación?

Las cinco funciones de agregación estandarizadas por el estándar ANSI SQL y soportadas de manera universal por todos los motores relacionales son:

  • COUNT(): Computa el número total de registros o valores no nulos presentes en la selección.
  • SUM(): Realiza la adición o suma total de todos los valores numéricos de la columna.
  • AVG(): Calcula la media o promedio aritmético simple de los valores numéricos evaluados.
  • MAX(): Examina el conjunto y extrae el valor máximo o más alto (numérico, alfabético o cronológico).
  • MIN(): Examina el conjunto y extrae el valor mínimo o más bajo registrado.

HAVING

¿Para qué sirve un HAVING?

La cláusula HAVING está diseñada exclusivamente para filtrar los resultados de una consulta basándose en condiciones aplicadas a funciones de agregación o campos que han sido consolidados mediante un agrupamiento. Representa la contraparte del WHERE para el análisis grupal: mientras que el WHERE discrimina filas individuales antes de que se realice cualquier operación de resumen, HAVING actúa como un filtro secundario y tardío que evalúa y descarta grupos enteros creados por la instrucción GROUP BY que no cumplan con los umbrales métricos definidos.

¿Cuál es la sintaxis y parámetros para un HAVING?

Su ubicación sintáctica es sumamente rígida y debe colocarse de forma obligatoria después de la cláusula GROUP BY y antes de la ordenación final (ORDER BY). Dentro de sus parámetros, acepta las mismas funciones de agregación explicadas anteriormente combinadas con operadores lógicos y de comparación para validar los resultados calculados.

SELECT 
columna_agrupamiento,
AVG(columna_numerica)
FROM
nombre_tabla
GROUP BY
columna_agrupamiento
HAVING
AVG(columna_numerica) > 250;

¿Qué sucede en el motor de la base de datos al utilizar un HAVING?

Durante el ciclo de procesamiento de la consulta, el motor ejecuta primero el filtrado de filas base con WHERE, luego toma los registros supervivientes y los distribuye en grupos lógicos según los campos del GROUP BY, procediendo a computar las funciones de agregación correspondientes para cada uno de esos bloques. Una vez que los grupos están totalmente estructurados en la memoria de trabajo y sus métricas calculadas, el motor ejecuta el filtro HAVING fila por fila de resumen, eliminando de forma drástica aquellos grupos calculados que no superen la condición matemática antes de enviar los datos a las fases de ordenamiento visual o entrega final al usuario.

Ejercicios de Funciones de Agregación

Realizar una consulta que cuente la cantidad total de equipos registrados en la tabla `estadisticas_mundial` y devuelva el resultado bajo el alias `cantidad_equipos`.

SELECT 
COUNT(*) AS cantidad_equipos
FROM
estadisticas_mundial;

Realizar una consulta que determine la altura máxima y la altura mínima entre todos los jugadores destacados, utilizando los alias `altura_maxima` y `altura_minima`.

SELECT 
MAX(altura_metros) AS altura_maxima,
MIN(altura_metros) AS altura_minima
FROM
jugadores_destacados;

Realizar una consulta que calcule la suma total del valor de mercado de todos los jugadores destacados y renombre la columna resultante como `valor_total_mercado`.

SELECT 
SUM(valor_mercado_millones) AS valor_total_mercado
FROM
jugadores_destacados;

Realizar una consulta que muestre el nombre de la confederación y la suma de todos los goles anotados por sus equipos correspondientes, filtrando para que el reporte devuelva exclusivamente a las confederaciones que acumulen más de 5 goles en total.

SELECT 
confederacion,
SUM(goles_anotados) AS total_goles
FROM
estadisticas_mundial
GROUP BY
confederacion
HAVING
SUM(goles_anotados) > 5;

Realizar una consulta que agrupe los datos por `confederacion` y calcule el promedio de sus puntos FIFA, mostrando en el resultado final solo aquellas confederaciones cuyo promedio supere los 1100 puntos.

SELECT 
confederacion,
AVG(puntos_fifa) AS promedio_puntos
FROM
estadisticas_mundial
GROUP BY
confederacion
HAVING
AVG(puntos_fifa) > 1100;

Realizar una consulta que agrupe a los jugadores destacados dependiendo de si son capitanes o no, calcule la suma total del valor de mercado para cada grupo y muestre únicamente el grupo cuya suma total de mercado sea mayor a 100 millones.

SELECT 
es_capitan,
SUM(valor_mercado_millones) AS suma_valor_mercado
FROM
jugadores_destacados
GROUP BY
es_capitan
HAVING
SUM(valor_mercado_millones) > 100;

DML

CREATE TABLE productos_tienda (
id_producto INTEGER PRIMARY KEY,
nombre TEXT NOT NULL,
categoria VARCHAR(50) NOT NULL CHECK (categoria IN ('Plantas','Macetas','Insumos','Accesorios')),
precio NUMERIC(12,2) NOT NULL CHECK (precio >= 0),
costo NUMERIC(12,2) NOT NULL CHECK (costo >= 0),
stock INTEGER NOT NULL CHECK (stock >= 0),
fecha_alta DATE NOT NULL,
proveedor_codigo VARCHAR(30)
);
CREATE TABLE ordenes_envio (
id_orden INTEGER PRIMARY KEY,
cliente TEXT NOT NULL,
fecha_pedido DATE NOT NULL,
monto_total NUMERIC(14,2) NOT NULL CHECK (monto_total >= 0),
peso_gramos INTEGER NOT NULL CHECK (peso_gramos >= 0),
codigo_seguimiento VARCHAR(30),
fecha_entrega DATE,
CONSTRAINT chk_fecha_entrega CHECK (fecha_entrega IS NULL OR fecha_entrega >= fecha_pedido)
);
INSERT INTO productos_tienda (id_producto, nombre, categoria, precio, costo, stock, fecha_alta, proveedor_codigo) VALUES
(1, ' Potus N-Joy ', 'Plantas', 1500.00, 600.00, 45, '2026-01-10', 'PROV-BIO'),
(2, 'Alocasia Amazonica', 'Plantas', 4500.50, 2100.00, 12, '2026-02-15', 'PROV-BIO'),
(3, 'Maceta de Barro N18', 'Macetas', 1200.00, 500.00, 80, '2025-11-20', NULL),
(4, 'Sustrato Premium 5L', 'Insumos', 2500.00, 1100.00, 150, '2026-03-01', 'PROV-MATE'),
(5, 'Fertilizante Orgánico Líquido', 'Insumos', 3200.00, 1500.00, 0, '2026-04-12', 'PROV-MATE'),
(6, 'Regadera Plástica Vintage', 'Accesorios', 5800.00, 3000.00, 8, '2025-12-05', NULL),
(7, 'Tijera de Poda Profesional', 'Accesorios', 12500.99, 6200.00, 5, '2026-05-22', 'PROV-TOOL'),
(8, ' Lámpara LED Cultivo ', 'Accesorios', 28000.00, 14000.00, 3, '2026-01-20', 'PROV-ELEC'),
(9, 'Monstera Deliciosa', 'Plantas', 6500.00, 3000.00, 20, '2026-03-15', 'PROV-BIO'),
(10, 'Sansevieria Trifasciata', 'Plantas', 2200.00, 900.00, 60, '2026-04-01', 'PROV-BIO'),
(11, ' Ficus Lyrata ', 'Plantas', 8900.00, 4100.00, 5, '2026-05-10', 'PROV-BIO'),
(12, 'Maceta Cerámica Esmaltada', 'Macetas', 4500.00, 2000.00, 35, '2025-10-15', 'PROV-DECO'),
(13, 'Humus de Lombriz 2KG', 'Insumos', 1800.00, 750.00, 100, '2026-01-05', 'PROV-MATE'),
(14, 'Perlita Agrícola 10L', 'Insumos', 3100.00, 1200.00, 40, '2026-02-28', 'PROV-MATE'),
(15, 'Tutor de Fibra de Coco', 'Accesorios', 2500.00, 900.00, 50, '2026-06-01', NULL),
(16, 'Termohigrómetro Digital', 'Accesorios', 15000.00, 8000.00, 15, '2026-04-20', 'PROV-ELEC'),
(17, ' Pilea Peperomioides', 'Plantas', 3500.00, 1500.00, 0, '2026-05-05', 'PROV-BIO'),
(18, 'Plato Recolector N18', 'Macetas', 500.00, 150.00, 200, '2025-09-10', NULL),
(19, 'Kit Herramientas Bonsái', 'Accesorios', 22000.00, 11000.00, 4, '2026-06-15', 'PROV-TOOL'),
(20, 'Aceite de Neem 100ml', 'Insumos', 4200.00, 1800.00, 75, '2026-03-22', 'PROV-MATE');
INSERT INTO ordenes_envio (id_orden, cliente, fecha_pedido, monto_total, peso_gramos, codigo_seguimiento, fecha_entrega) VALUES
(101, 'Carlos Gómez', '2026-06-01', 7500.00, 2500, 'AR-9874-A', '2026-06-05'),
(102, 'Ana María Silva', '2026-06-10', 12000.50, 5800, 'BR-1122-X', '2026-06-15'),
(103, 'Juan Pérez', '2026-06-14', 3200.00, 850, 'AR-3344-B', NULL),
(104, 'mariana lópez', '2026-06-18', 28000.00, 4200, 'US-5566-Z', '2026-06-22'),
(105, 'Luis Fernández', '2026-06-25', 1500.00, 1200, 'AR-7788-C', NULL),
(106, 'Sofía Rodríguez', '2026-06-28', 5800.00, 3100, NULL, NULL),
(107, ' Diego Maradona ', '2026-01-15', 45000.00, 8500, 'AR-1010-D', '2026-01-20'),
(108, 'Lucía Méndez', '2026-02-14', 2200.00, 500, 'AR-4455-E', '2026-02-18'),
(109, 'esteban quito', '2026-03-05', 15500.00, 3500, 'CH-9988-F', '2026-03-12'),
(110, 'Valeria Lynch', '2026-03-20', 8900.00, 2100, NULL, NULL),
(111, 'Roberto Carlos', '2026-04-01', 32000.00, 6200, 'BR-7777-G', '2026-04-10'),
(112, 'Marta Minujín', '2026-04-15', 4500.00, 1100, 'AR-1234-H', '2026-04-18'),
(113, ' julio bocca', '2026-05-10', 18000.00, 4000, 'AR-5678-I', '2026-05-15'),
(114, 'Florencia Peña', '2026-05-25', 500.00, 200, NULL, NULL),
(115, 'Ricardo Darín', '2026-06-05', 65000.00, 12000, 'AR-9999-J', '2026-06-12'),
(116, 'Susana Giménez', '2026-06-29', 12500.99, 1500, 'UY-4321-K', NULL),
(117, 'Charly García', '2026-06-30', 2500.00, 800, 'AR-6666-L', NULL),
(118, 'Fito Páez', '2025-12-20', 14000.00, 3200, 'AR-0001-M', '2025-12-28');

Ejercicios de Consultas SQL

Obtener todos los datos de los productos que pertenezcan a la categoría ‘Plantas’.

SELECT * FROM productos_tienda 
WHERE categoria = 'Plantas';

Mostrar el nombre y el precio de los productos cuyo precio sea mayor o igual a 10000.

SELECT nombre, precio 
FROM productos_tienda
WHERE precio >= 10000;

Listar los productos que tengan un stock estrictamente menor a 10 unidades o que no tengan stock (0).

SELECT nombre, stock 
FROM productos_tienda
WHERE stock < 10;

Encontrar todos los productos cuyo costo se encuentre entre 1000 y 3000 inclusive.

SELECT nombre, costo 
FROM productos_tienda
WHERE costo BETWEEN 1000 AND 3000;

Obtener los productos que pertenezcan a los proveedores ‘PROV-TOOL’, ‘PROV-DECO’ o ‘PROV-ELEC’.

SELECT nombre, proveedor_codigo 
FROM productos_tienda
WHERE proveedor_codigo IN ('PROV-TOOL', 'PROV-DECO', 'PROV-ELEC');

Mostrar todas las órdenes de envío que aún no tengan un código de seguimiento asignado.

SELECT id_orden, cliente 
FROM ordenes_envio
WHERE codigo_seguimiento IS NULL;

Listar las órdenes que ya fueron entregadas (que tengan una fecha de entrega registrada) y cuyo monto total supere los 15000.

SELECT id_orden, cliente, monto_total 
FROM ordenes_envio
WHERE fecha_entrega IS NOT NULL
AND monto_total > 15000;

Calcular el valor de venta total potencial de cada producto multiplicando el precio por el stock actual. Llamar a la columna resultante valor_potencial.

SELECT nombre, 
(precio * stock) AS valor_potencial
FROM productos_tienda;

Determinar el margen de ganancia porcentual de cada producto. La fórmula es ((precio - costo) / costo) * 100. Nombrar la columna margen_porcentual.

SELECT nombre, 
((precio - costo) / costo) * 100 AS margen_porcentual
FROM productos_tienda;

El costo de envío es de $50 por cada 100 gramos de peso. Calcular el costo de envío estimado para cada orden.

SELECT id_orden, 
cliente,
(peso_gramos / 100) * 50 AS costo_envio_estimado
FROM ordenes_envio;

Mostrar los nombres de los productos eliminando los espacios en blanco que puedan tener al principio o al final.

SELECT BTRIM(nombre) AS nombre_corregido 
FROM productos_tienda;

Listar los clientes asegurando que sus nombres se muestren con la primera letra de cada palabra en mayúscula.

SELECT INITCAP(cliente) AS cliente_formato_correcto 
FROM ordenes_envio;

Encontrar todos los productos cuyo nombre contenga la palabra ‘Maceta’ en cualquier parte de la cadena.

SELECT nombre, categoria 
FROM productos_tienda
WHERE nombre LIKE '%Maceta%';

Mostrar el nombre del cliente y la longitud (cantidad de caracteres) de su nombre, ordenado de mayor a menor longitud.

SELECT BTRIM(cliente) AS cliente, 
LENGTH(BTRIM(cliente)) AS largo_nombre
FROM ordenes_envio
ORDER BY largo_nombre DESC;

Generar un código de producto concatenando las tres primeras letras de la categoría en mayúsculas, un guion y el ID del producto.

SELECT id_producto, 
nombre,
UPPER(SUBSTRING(categoria FROM 1 FOR 3)) || '-' || id_producto AS codigo_generado
FROM productos_tienda;

Extraer únicamente el año de la fecha de pedido de todas las órdenes.

SELECT id_orden, 
EXTRACT(YEAR FROM fecha_pedido) AS anio_pedido
FROM ordenes_envio;

Encontrar los productos que fueron dados de alta en la base de datos durante el mes de marzo (mes 3) de cualquier año.

SELECT nombre, fecha_alta 
FROM productos_tienda
WHERE EXTRACT(MONTH FROM fecha_alta) = 3;

Calcular la cantidad de días exactos que demoró el envío de cada orden que ya fue entregada, restando la fecha de pedido a la fecha de entrega.

SELECT id_orden, 
cliente,
(fecha_entrega - fecha_pedido) AS dias_demora
FROM ordenes_envio
WHERE fecha_entrega IS NOT NULL;

Mostrar la fecha de alta de los productos en formato ‘DD/MM/YYYY’.

SELECT nombre, 
TO_CHAR(fecha_alta, 'DD/MM/YYYY') AS fecha_formateada
FROM productos_tienda;

Identificar si hay algún cliente cuyo nombre o apellido comience con la letra ‘M’, sin importar si está escrita en mayúscula o minúscula en la base de datos.

SELECT cliente 
FROM ordenes_envio
WHERE LOWER(BTRIM(cliente)) LIKE 'm%';

--

--