Ruta práctica de SQL en 5 niveles
Una ruta interactiva para aprender y practicar T-SQL directamente desde el navegador, con laboratorios integrados en los cinco niveles y una versión acotada de Northwind.
Cómo utilizar este curso
Revisa el modelo de datos y avanza nivel por nivel. Cada sección de ejercicios incorpora su propio laboratorio SQL y prepara Northwind Acotada automáticamente.
Comprende el modelo
Revisa las tablas, claves y relaciones de Northwind Acotada antes de comenzar las consultas.
Avanza por nivel
Estudia fundamentos y ejemplos. Luego entra a Ejercicios para abrir el laboratorio integrado del nivel.
Practica y verifica
Ejecuta SQL en el navegador, comprueba la estructura de tu solución y registra automáticamente tu progreso.
Mapa de la ruta
Northwind Acotada
El modelo conserva las entidades esenciales para practicar consultas relacionales sin cargar toda la Northwind tradicional.
Haz clic sobre la imagen para ampliarla.
OrderDetails (sin espacio).Entidades del script de práctica
| Tabla | Clave primaria | Qué representa | Relación principal |
|---|---|---|---|
Categories | CategoryID | Clasificación de productos | Products.CategoryID |
Suppliers | SupplierID | Proveedores de productos | Products.SupplierID |
Products | ProductID | Catálogo, precio, stock y estado | OrderDetails.ProductID |
Customers | CustomerID | Clientes y localización | Orders.CustomerID |
Employees | EmployeeID | Empleados responsables de pedidos | Orders.EmployeeID |
Orders | OrderID | Cabecera del pedido, fechas y despacho | OrderDetails.OrderID |
OrderDetails | OrderID + ProductID | Líneas del pedido, cantidad, precio y descuento | Orders / Products |
Relaciones 1 a muchos
OrderDetails tiene clave primaria compuesta por OrderID + ProductID. Eso evita repetir el mismo producto dos veces como una línea idéntica dentro de un pedido y, al mismo tiempo, conecta pedidos con productos.
Script Northwind Acotada
Los laboratorios cargan este script automáticamente. Aquí puedes revisarlo, copiarlo o descargarlo para utilizarlo también en un entorno SQL Server externo.
El script limpia tablas anteriores, crea las 7 tablas con PK/FK, inserta datos y deja consultas de verificación y ejemplo.
Fundamentos de SQL y primeras consultas
Modelo relacional, tablas, filas, columnas, claves y estructura SELECT / FROM.
¿Qué aprenderás?
La estructura mínima de una consulta
SELECT indica qué columnas deseas ver y FROM indica desde qué tabla se obtienen.
SELECT ProductName, UnitPrice
FROM Products;
Paso 1: buscamos dos columnas. Paso 2: SQL Server lee la tabla Products. Paso 3: devuelve una fila por producto.
SELECT * versus columnas específicas
SELECT * es útil para explorar una tabla, pero en consultas reales conviene indicar las columnas que necesitas.
SELECT *
FROM Customers;
SELECT CompanyName, City, Country
FROM Customers;
Ejemplo 1 · Explorar clientes
SELECT CustomerID, CompanyName, City, Country
FROM Customers;
- Se consulta la tabla
Customers. - Se seleccionan cuatro columnas.
- Cada fila representa un cliente.
Ejemplo 2 · Ver catálogo de productos
SELECT ProductID, ProductName, UnitPrice, UnitsInStock
FROM Products;
Permite reconocer el identificador del producto, su nombre, precio y stock disponible.
Ejemplo 3 · Alias de salida
SELECT ProductName AS Producto,
UnitPrice AS Precio
FROM Products;
El alias cambia el encabezado mostrado en el resultado; no modifica la estructura de la tabla.
Practica sin salir de Lógica y Datos
Selecciona una práctica. El curso prepara Northwind Acotada y abre el editor SQL Server integrado para este nivel.
Motor de ejecución: OneCompiler integrado en la página.Ejercicio 1 · Explorar categorías
Muestra todos los registros de la tabla Categories.
Uso seguro: utiliza únicamente datos ficticios de práctica. No ingreses contraseñas, claves API ni información confidencial.
Categories.Ver solución
SELECT * FROM Categories;Ver solución
SELECT CompanyName, Country
FROM Suppliers;Ver solución
SELECT FirstName, LastName, Title
FROM Employees;Ver solución
SELECT OrderID, OrderDate, ShipCountry
FROM Orders;Desafío final · Reconocer la base
Construye tres consultas distintas que permitan presentar: clientes, productos y pedidos. En cada consulta selecciona sólo las columnas que serían útiles en un reporte inicial.
Una posible solución
SELECT CustomerID, CompanyName, City, Country
FROM Customers;
SELECT ProductID, ProductName, UnitPrice, UnitsInStock
FROM Products;
SELECT OrderID, CustomerID, EmployeeID, OrderDate, ShipCountry
FROM Orders;
Filtrado, búsqueda y ordenamiento
WHERE, operadores, AND / OR / NOT, BETWEEN, LIKE, IN, TOP y ORDER BY.
Filtrar no es lo mismo que ordenar
WHERE decide qué filas participan en el resultado. ORDER BY decide en qué orden se muestran.
= <> > >= < <=BETWEEN incluye ambos extremos.LIKE 'A%' busca valores que comienzan con A.AND, OR, NOT combinan condiciones.Orden lógico de una consulta básica
SELECT columnas
FROM tabla
WHERE condición
ORDER BY columna ASC;
Primero se identifica la tabla, luego se filtran las filas y finalmente se ordena el resultado.
Ejemplo 1 · Productos sobre $30
SELECT ProductName, UnitPrice
FROM Products
WHERE UnitPrice > 30
ORDER BY UnitPrice DESC;
Paso 1: seleccionamos nombre y precio. Paso 2: conservamos sólo precios sobre 30. Paso 3: mostramos primero los más caros.
Ejemplo 2 · Rango de precios
SELECT ProductName, UnitPrice
FROM Products
WHERE UnitPrice BETWEEN 15 AND 40
ORDER BY UnitPrice;
BETWEEN equivale conceptualmente a UnitPrice >= 15 AND UnitPrice <= 40.
Ejemplo 3 · Dos países
SELECT CompanyName, Country
FROM Customers
WHERE Country IN ('Germany', 'Brazil')
ORDER BY Country, CompanyName;
IN simplifica varios criterios de igualdad unidos con OR.
Practica sin salir de Lógica y Datos
Selecciona una práctica. El curso prepara Northwind Acotada y abre el editor SQL Server integrado para este nivel.
Motor de ejecución: OneCompiler integrado en la página.Ejercicio 1 · Clientes de Germany
Muestra CompanyName, City y Country de los clientes de Germany.
Uso seguro: utiliza únicamente datos ficticios de práctica. No ingreses contraseñas, claves API ni información confidencial.
Ver solución
SELECT CompanyName, City, Country
FROM Customers
WHERE Country = 'Germany';Ver solución
SELECT ProductName, UnitsInStock
FROM Products
WHERE UnitsInStock <= 15;Ver solución
SELECT ProductName, UnitPrice, Discontinued
FROM Products
WHERE UnitPrice > 20 AND Discontinued = 0;Ver solución
SELECT CompanyName, Country
FROM Customers
WHERE CompanyName LIKE 'A%';Ver solución
SELECT TOP 5 ProductName, UnitPrice
FROM Products
ORDER BY UnitPrice DESC;Ver solución
SELECT OrderID, OrderDate, ShipCountry
FROM Orders
WHERE ShipCountry IN ('Brazil', 'Germany')
ORDER BY OrderDate;Desafío final · Catálogo priorizado
Obtén los 7 productos activos de mayor precio que tengan stock disponible y precio igual o superior a 20.
Ver solución razonada
SELECT TOP 7
ProductName,
UnitPrice,
UnitsInStock
FROM Products
WHERE Discontinued = 0
AND UnitsInStock > 0
AND UnitPrice >= 20
ORDER BY UnitPrice DESC;
El filtro elimina descontinuados y productos sin stock; TOP limita el resultado después de ordenar por precio descendente.
Funciones y columnas calculadas
Texto, números, fechas, conversión de tipos y expresiones calculadas.
Las funciones transforman la salida
En este nivel no necesitamos modificar los datos almacenados: usamos funciones para presentar, calcular o analizar información.
UPPER, LOWER, LEN, CONCATROUND y operaciones aritméticas.YEAR, MONTH, DAY, DATEDIFF.CAST y CONVERT.Crear una columna calculada
SELECT ProductName,
UnitPrice,
UnitsInStock,
UnitPrice * UnitsInStock AS ValorInventario
FROM Products;
La columna ValorInventario se calcula al ejecutar la consulta; no se guarda físicamente en Products.
Ejemplo 1 · Formatear nombres
SELECT CompanyName,
UPPER(CompanyName) AS EmpresaMayuscula,
LEN(CompanyName) AS LargoNombre
FROM Customers;
Dos funciones se aplican a la misma columna y generan salidas independientes.
Ejemplo 2 · Nombre completo del empleado
SELECT EmployeeID,
CONCAT(FirstName, ' ', LastName) AS Empleado,
Title
FROM Employees;
CONCAT combina valores de texto en una sola columna de salida.
Ejemplo 3 · Días hasta despacho
SELECT OrderID,
OrderDate,
ShippedDate,
DATEDIFF(DAY, OrderDate, ShippedDate) AS DiasDespacho
FROM Orders
WHERE ShippedDate IS NOT NULL;
Se excluyen pedidos aún no despachados para evitar interpretar un NULL como una fecha.
Practica sin salir de Lógica y Datos
Selecciona una práctica. El curso prepara Northwind Acotada y abre el editor SQL Server integrado para este nivel.
Motor de ejecución: OneCompiler integrado en la página.Ejercicio 1 · Mayúsculas y longitud
Muestra ProductName en mayúsculas y la longitud de cada nombre.
Uso seguro: utiliza únicamente datos ficticios de práctica. No ingreses contraseñas, claves API ni información confidencial.
Ver solución
SELECT UPPER(ProductName) AS Producto,
LEN(ProductName) AS Largo
FROM Products;Ver solución
SELECT ProductName,
UnitPrice,
UnitsInStock,
UnitPrice * UnitsInStock AS ValorInventario
FROM Products;Ver solución
SELECT ProductName,
UnitPrice,
ROUND(UnitPrice, 0) AS PrecioRedondeado
FROM Products;Ver solución
SELECT OrderID,
YEAR(OrderDate) AS Anio,
MONTH(OrderDate) AS Mes
FROM Orders;Ver solución
SELECT OrderID,
CONVERT(VARCHAR(10), OrderDate, 103) AS FechaPedido
FROM Orders;Ver solución
SELECT OrderID,
ProductID,
Quantity,
UnitPrice,
Quantity * UnitPrice AS TotalBruto
FROM OrderDetails;Desafío final · Valor del inventario
Genera un reporte con producto en mayúsculas, precio, precio redondeado, stock y valor de inventario. Incluye sólo productos con stock y ordena de mayor a menor valor.
Ver solución
SELECT UPPER(ProductName) AS Producto,
UnitPrice AS Precio,
ROUND(UnitPrice, 0) AS PrecioRedondeado,
UnitsInStock AS Stock,
UnitPrice * UnitsInStock AS ValorInventario
FROM Products
WHERE UnitsInStock > 0
ORDER BY ValorInventario DESC;Consultas de múltiples tablas
PK/FK, INNER JOIN, LEFT JOIN, alias, joins múltiples, UNION y UNION ALL.
Relacionar datos
Northwind separa clientes, pedidos, empleados, productos y detalles. Las claves permiten reconstruir una vista de negocio sin duplicar información en una sola tabla.
INNER JOIN
Devuelve filas que tienen coincidencia en ambas tablas.
SELECT o.OrderID, c.CompanyName, o.OrderDate
FROM Orders AS o
INNER JOIN Customers AS c
ON o.CustomerID = c.CustomerID;
LEFT JOIN
Mantiene todas las filas de la tabla izquierda aunque no exista coincidencia a la derecha. En esta base es ideal para detectar clientes sin pedidos.
Ejemplo 1 · Pedido + cliente
SELECT o.OrderID,
o.OrderDate,
c.CompanyName AS Cliente
FROM Orders AS o
INNER JOIN Customers AS c
ON o.CustomerID = c.CustomerID
ORDER BY o.OrderDate;
La FK Orders.CustomerID se compara con la PK Customers.CustomerID.
Ejemplo 2 · Pedido + cliente + empleado
SELECT o.OrderID,
c.CompanyName AS Cliente,
CONCAT(e.FirstName, ' ', e.LastName) AS Empleado
FROM Orders AS o
INNER JOIN Customers AS c
ON o.CustomerID = c.CustomerID
INNER JOIN Employees AS e
ON o.EmployeeID = e.EmployeeID;
Dos relaciones permiten mostrar quién compró y qué empleado gestionó el pedido.
Ejemplo 3 · Producto vendido por pedido
SELECT od.OrderID,
p.ProductName,
od.Quantity,
od.UnitPrice,
od.Quantity * od.UnitPrice AS Subtotal
FROM OrderDetails AS od
INNER JOIN Products AS p
ON od.ProductID = p.ProductID;
OrderDetails actúa como tabla puente entre Orders y Products.
Practica sin salir de Lógica y Datos
Selecciona una práctica. El curso prepara Northwind Acotada y abre el editor SQL Server integrado para este nivel.
Motor de ejecución: OneCompiler integrado en la página.Ejercicio 1 · Pedido + cliente
Lista cada OrderID con CompanyName del cliente.
Uso seguro: utiliza únicamente datos ficticios de práctica. No ingreses contraseñas, claves API ni información confidencial.
Ver solución
SELECT o.OrderID, c.CompanyName
FROM Orders o
INNER JOIN Customers c
ON o.CustomerID = c.CustomerID;Ver solución
SELECT p.ProductName, c.CategoryName
FROM Products p
INNER JOIN Categories c
ON p.CategoryID = c.CategoryID;Ver solución
SELECT p.ProductName, s.CompanyName AS Proveedor
FROM Products p
INNER JOIN Suppliers s
ON p.SupplierID = s.SupplierID;Ver solución
SELECT c.CustomerID, c.CompanyName, o.OrderID
FROM Customers c
LEFT JOIN Orders o
ON c.CustomerID = o.CustomerID;Ver solución
SELECT c.CustomerID, c.CompanyName
FROM Customers c
LEFT JOIN Orders o
ON c.CustomerID = o.CustomerID
WHERE o.OrderID IS NULL;Ver solución
SELECT City, Country
FROM Customers
UNION
SELECT NULL AS City, Country
FROM Suppliers
ORDER BY Country, City;La tabla Suppliers acotada no tiene columna City; se usa NULL para mantener dos columnas compatibles y conservar el ejemplo de UNION.
Desafío final · Detalle completo de ventas
Construye una consulta que muestre pedido, fecha, cliente, empleado, producto, cantidad, precio unitario y total bruto de línea.
Ver solución
SELECT o.OrderID,
o.OrderDate,
c.CompanyName AS Cliente,
CONCAT(e.FirstName, ' ', e.LastName) AS Empleado,
p.ProductName AS Producto,
od.Quantity,
od.UnitPrice,
od.Quantity * od.UnitPrice AS TotalBruto
FROM Orders o
INNER JOIN Customers c
ON o.CustomerID = c.CustomerID
INNER JOIN Employees e
ON o.EmployeeID = e.EmployeeID
INNER JOIN OrderDetails od
ON o.OrderID = od.OrderID
INNER JOIN Products p
ON od.ProductID = p.ProductID
ORDER BY o.OrderID, p.ProductName;Agrupamiento y reportes
COUNT, SUM, AVG, MIN, MAX, DISTINCT, GROUP BY, HAVING y reportes con JOIN.
De registros a indicadores
Las funciones agregadas resumen muchas filas en uno o varios resultados. GROUP BY define el nivel de agrupación y HAVING filtra grupos ya calculados.
WHERE y HAVING: diferencia esencial
WHERE filtra filas antes de agrupar. HAVING filtra el resultado de los grupos.
SELECT ShipCountry, COUNT(*) AS TotalPedidos
FROM Orders
GROUP BY ShipCountry
HAVING COUNT(*) >= 2
ORDER BY TotalPedidos DESC;
Ejemplo 1 · Clientes por país
SELECT Country,
COUNT(*) AS TotalClientes
FROM Customers
GROUP BY Country
ORDER BY TotalClientes DESC;
Cada país forma un grupo y COUNT(*) calcula cuántos clientes contiene.
Ejemplo 2 · Total vendido por pedido con descuento
SELECT OrderID,
SUM(Quantity * UnitPrice * (1 - Discount)) AS TotalPedido
FROM OrderDetails
GROUP BY OrderID
ORDER BY TotalPedido DESC;
La expresión se calcula por línea y luego SUM consolida todas las líneas del mismo pedido.
Ejemplo 3 · Ventas por cliente
SELECT c.CompanyName AS Cliente,
SUM(od.Quantity * od.UnitPrice * (1 - od.Discount)) AS TotalVendido
FROM Customers c
INNER JOIN Orders o
ON c.CustomerID = o.CustomerID
INNER JOIN OrderDetails od
ON o.OrderID = od.OrderID
GROUP BY c.CompanyName
ORDER BY TotalVendido DESC;
El JOIN identifica a quién pertenece cada pedido; luego el agrupamiento consolida las ventas por cliente.
Practica sin salir de Lógica y Datos
Selecciona una práctica. El curso prepara Northwind Acotada y abre el editor SQL Server integrado para este nivel.
Motor de ejecución: OneCompiler integrado en la página.Ejercicio 1 · Total de productos
Cuenta los registros de Products.
Uso seguro: utiliza únicamente datos ficticios de práctica. No ingreses contraseñas, claves API ni información confidencial.
Ver solución
SELECT COUNT(*) AS TotalProductos
FROM Products;Ver solución
SELECT MIN(UnitPrice) AS Minimo,
MAX(UnitPrice) AS Maximo,
AVG(UnitPrice) AS Promedio
FROM Products;Ver solución
SELECT DISTINCT Country
FROM Customers
ORDER BY Country;Ver solución
SELECT c.CategoryName,
COUNT(p.ProductID) AS TotalProductos
FROM Categories c
LEFT JOIN Products p
ON c.CategoryID = p.CategoryID
GROUP BY c.CategoryName
ORDER BY TotalProductos DESC;Ver solución
SELECT CONCAT(e.FirstName, ' ', e.LastName) AS Empleado,
COUNT(o.OrderID) AS TotalPedidos
FROM Employees e
LEFT JOIN Orders o
ON e.EmployeeID = o.EmployeeID
GROUP BY e.FirstName, e.LastName
ORDER BY TotalPedidos DESC;Ver solución
SELECT ShipCountry,
COUNT(*) AS TotalPedidos
FROM Orders
GROUP BY ShipCountry
HAVING COUNT(*) >= 2
ORDER BY TotalPedidos DESC;Ver solución
SELECT p.ProductName,
SUM(od.Quantity * od.UnitPrice * (1 - od.Discount)) AS TotalVendido
FROM Products p
INNER JOIN OrderDetails od
ON p.ProductID = od.ProductID
GROUP BY p.ProductName
ORDER BY TotalVendido DESC;Desafío final · Reporte ejecutivo de clientes
Para cada cliente que tenga pedidos, muestra país, cantidad de pedidos, total neto vendido y ticket promedio por línea. Ordena por venta total descendente.
Ver solución
SELECT c.CompanyName AS Cliente,
c.Country AS Pais,
COUNT(DISTINCT o.OrderID) AS TotalPedidos,
SUM(od.Quantity * od.UnitPrice * (1 - od.Discount)) AS TotalVendido,
AVG(od.Quantity * od.UnitPrice * (1 - od.Discount)) AS PromedioLinea
FROM Customers c
INNER JOIN Orders o
ON c.CustomerID = o.CustomerID
INNER JOIN OrderDetails od
ON o.OrderID = od.OrderID
GROUP BY c.CompanyName, c.Country
ORDER BY TotalVendido DESC;