Procedimientos almacenados SQL Server
-Yeiker Solano
-Mateo Giraldo
-Salome García
10/07/2025
INTRODUCCION
Un procedimiento almacenado (stored procedure) es un conjunto de
instrucciones SQL
que se almacena y se ejecuta directamente en el servidor de base de
datos. A diferencia
de las consultas ad hoc (realizadas directamente desde el cliente o una
aplicación), los
procedimientos almacenados se crean, almacenan y gestionan en el
propio motor de la
base de datos. Esto permite que las instrucciones SQL se reutilicen
fácilmente y se ejecuten
de manera optimizada.
Objetivos De Los Procedimientos Almacenados
Reutilización de código: Permiten definir un conjunto de instrucciones
SQL que pueden
ejecutarse repetidamente sin necesidad de reescribir el código.
Seguridad: Los procedimientos almacenados permiten definir permisos
específicos,
permitiendo que los usuarios accedan y ejecuten el procedimiento sin
acceder directamente
a las tablas subyacentes.
Optimización: El motor de la base de datos puede optimizar los
procedimientos
almacenados, mejorando el rendimiento al almacenar un plan de
ejecución.
Mantenimiento: Centralizar la lógica del negocio en la base de datos
facilita el
mantenimiento, ya que los cambios se realizan solo en el procedimiento
almacenado sin
modificar las aplicaciones clientes.
Reducción de tráfico: Al reducir el número de consultas entre el cliente y
el servidor,
disminuye el tráfico de red y aumenta la eficiencia.
-- Solución Ejercicio 1: Gestión de Clientes
CREATE PROCEDURE dbo.uspCustomerManager
@Action NVARCHAR(10),
@CustomerId INT = NULL,
@FirstName NVARCHAR(40) = NULL,
@LastName NVARCHAR(20) = NULL,
@Email NVARCHAR(60) = NULL,
@Company NVARCHAR(80) = NULL,
@Address NVARCHAR(70) = NULL,
@City NVARCHAR(40) = NULL,
@State NVARCHAR(40) = NULL,
@Country NVARCHAR(40) = NULL,
@PostalCode NVARCHAR(10) = NULL,
@Phone NVARCHAR(24) = NULL
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRY
-- Validaciones comunes
IF @Action NOT IN ('INSERT', 'UPDATE', 'DELETE', 'SELECT')
THROW 50000, 'Acción no válida. Use INSERT, UPDATE, DELETE o
SELECT.', 1;
-- Validar email único
IF @Action IN ('INSERT', 'UPDATE') AND @Email IS NOT NULL
BEGIN
IF EXISTS (
SELECT 1 FROM Customer
WHERE Email = @Email
AND (@CustomerId IS NULL OR CustomerId != @CustomerId)
THROW 50001, 'El email ya existe en la base de datos.', 1;
END
-- Procesar según la acción
IF @Action = 'INSERT'
BEGIN
INSERT INTO Customer (
FirstName, LastName, Email, Company,
Address, City, State, Country,
PostalCode, Phone
)
VALUES (
@FirstName, @LastName, @Email, @Company,
@Address, @City, @State, @Country,
@PostalCode, @Phone
);
SET @CustomerId = SCOPE_IDENTITY();
END
IF @Action = 'UPDATE'
BEGIN
UPDATE Customer
SET
FirstName = ISNULL(@FirstName, FirstName),
LastName = ISNULL(@LastName, LastName),
Email = ISNULL(@Email, Email),
Company = @Company,
Address = @Address,
City = @City,
State = @State,
Country = @Country,
PostalCode = @PostalCode,
Phone = @Phone
WHERE CustomerId = @CustomerId;
END
IF @Action = 'DELETE'
BEGIN
IF EXISTS (SELECT 1 FROM Invoice WHERE CustomerId = @CustomerId)
THROW 50003, 'No se puede eliminar el cliente porque tiene facturas
asociadas.', 1;
DELETE FROM Customer WHERE CustomerId = @CustomerId;
END
IF @Action = 'SELECT'
BEGIN
SELECT
c.*,
COUNT(i.InvoiceId) AS TotalInvoices,
SUM(i.Total) AS TotalPurchases,
MAX(i.InvoiceDate) AS LastPurchaseDate
FROM Customer c
LEFT JOIN Invoice i ON c.CustomerId = i.CustomerId
WHERE c.CustomerId = ISNULL(@CustomerId, c.CustomerId)
GROUP BY
c.CustomerId, c.FirstName, c.LastName,
c.Email, c.Company, c.Address, c.City,
c.State, c.Country, c.PostalCode, c.Phone,
c.Fax, c.SupportRepId;
END
END TRY
BEGIN CATCH
THROW;
END CATCH;
END;
GO
--Ejercicio 2--
CREATE PROCEDURE uspSalesReport
@FechaInicio DATE,
@FechaFin DATE,
@TopN INT = 5,
@MostrarCrecimiento BIT = 1
AS
BEGIN
SET NOCOUNT ON;
SELECT
c.Country,
SUM(i.Total) AS TotalVentas
FROM Invoice i
JOIN Customer c ON i.CustomerId = c.CustomerId
WHERE i.InvoiceDate BETWEEN @FechaInicio AND @FechaFin
GROUP BY c.Country
ORDER BY TotalVentas DESC;
SELECT TOP (@TopN)
ar.Name AS Artista,
COUNT(il.InvoiceLineId) AS CantidadVendida
FROM Invoice i
JOIN InvoiceLine il ON i.InvoiceId = il.InvoiceId
JOIN Track t ON il.TrackId = t.TrackId
JOIN Album al ON t.AlbumId = al.AlbumId
JOIN Artist ar ON al.ArtistId = ar.ArtistId
WHERE i.InvoiceDate BETWEEN @FechaInicio AND @FechaFin
GROUP BY ar.Name
ORDER BY CantidadVendida DESC;
SELECT TOP (@TopN)
g.Name AS Genero,
COUNT(il.InvoiceLineId) AS CantidadVendida
FROM Invoice i
JOIN InvoiceLine il ON i.InvoiceId = il.InvoiceId
JOIN Track t ON il.TrackId = t.TrackId
JOIN Genre g ON t.GenreId = g.GenreId
WHERE i.InvoiceDate BETWEEN @FechaInicio AND @FechaFin
GROUP BY g.Name
ORDER BY CantidadVendida DESC;
SELECT
COUNT(*) AS NumeroFacturas,
SUM(Total) AS SumaTotal,
AVG(Total) AS PromedioPorFactura
FROM Invoice
WHERE InvoiceDate BETWEEN @FechaInicio AND @FechaFin;
IF @MostrarCrecimiento = 1
BEGIN
DECLARE @Dias INT = DATEDIFF(DAY, @FechaInicio, @FechaFin) +
1;
DECLARE @FechaInicioAnterior DATE = DATEADD(DAY, -@Dias,
@FechaInicio);
DECLARE @FechaFinAnterior DATE = DATEADD(DAY, -1,
@FechaInicio);
SELECT
'CrecimientoTotalVentas' AS Tipo,
Anterior.TotalAnterior,
Actual.TotalActual,
CASE
WHEN Anterior.TotalAnterior = 0 THEN NULL
ELSE ROUND(((Actual.TotalActual - Anterior.TotalAnterior) *
100.0) / Anterior.TotalAnterior, 2)
END AS PorcentajeCrecimiento
FROM (
SELECT SUM(Total) AS TotalAnterior
FROM Invoice
WHERE InvoiceDate BETWEEN @FechaInicioAnterior AND
@FechaFinAnterior
) AS Anterior,
SELECT SUM(Total) AS TotalActual
FROM Invoice
WHERE InvoiceDate BETWEEN @FechaInicio AND @FechaFin
) AS Actual;
END
END;
----Ejercicio 3 Gestión de Playlist
CREATE PROCEDURE GestionPlaylist
@Genero NVARCHAR(20),
@duracion int,
@valor float
AS
BEGIN
SELECT Playlist.PlaylistId, Playlist.Name, Genre.Name AS GeneroNombre
FROM Playlist INNER JOIN PlaylistTrack ON Playlist.PlaylistId =
PlaylistTrack.PlaylistId
INNER JOIN Track ON Track.TrackId = PlaylistTrack.TrackId
INNER JOIN Genre ON Genre.GenreId = Track.GenreId
WHERE Genre.Name = @Genero
GROUP BY Playlist.PlaylistId, Playlist.Name, Genre.Name
SELECT
t.Name AS TrackName,
a.Title AS AlbumTitle,
g.Name AS Genre,
t.Milliseconds
FROM Track t
JOIN Album a ON t.AlbumId = a.AlbumId
JOIN Genre g ON t.GenreId = g.GenreId
WHERE t.Milliseconds <= @duracion -- duración máxima en
milisegundos (ej: 5 minutos)
ORDER BY g.Name, t.Milliseconds;
SELECT
SUM(t.Milliseconds) AS TotalDurationMilliseconds,
SUM(t.Milliseconds) / 60000.0 AS TotalDurationMinutes
FROM Track t
WHERE t.UnitPrice <= @valor; -- Cambia este valor según tu
presupuesto por canción
SELECT
SUM(UnitPrice) AS TotalCost
FROM (
SELECT DISTINCT Name, UnitPrice
FROM Track
) AS UniqueTracks;
end
exec GestionPlaylist @Genero='Rock', @duracion=3000, @valor=0.99
-- Ejercicio 4: Análisis de Empleados
ALTER PROCEDURE AnalisisDeEmpleados
@clientes INT
AS
BEGIN
-- Primer reporte: Ventas totales por empleado
SELECT
e.EmployeeId,
e.FirstName + ' ' + e.LastName AS EmployeeName,
COUNT(i.InvoiceId) AS TotalSales,
SUM(i.Total) AS TotalRevenue,
SUM(i.Total) * 0.05 AS Commission -- Cambia el 0.05 por el
porcentaje real de comisión
FROM
Employee e
JOIN Customer c ON e.EmployeeId = c.SupportRepId
JOIN Invoice i ON c.CustomerId = i.CustomerId
GROUP BY
e.EmployeeId, e.FirstName, e.LastName
ORDER BY
TotalRevenue DESC;
-- Segundo reporte: Ventas mensuales por empleado
SELECT
e.EmployeeId,
e.FirstName + ' ' + e.LastName AS EmployeeName,
YEAR(i.InvoiceDate) AS Year,
MONTH(i.InvoiceDate) AS Month,
COUNT(i.InvoiceId) AS TotalInvoices,
SUM(i.Total) AS TotalSales
FROM
Employee e
JOIN Customer c ON e.EmployeeId = c.SupportRepId
JOIN Invoice i ON c.CustomerId = i.CustomerId
GROUP BY
e.EmployeeId, e.FirstName, e.LastName, YEAR(i.InvoiceDate),
MONTH(i.InvoiceDate)
ORDER BY
Year, Month, TotalSales DESC;
-- Tercer reporte incompleto (se requiere más información para
completarlo)
-- Aquí puedes añadir otra consulta si es necesario
-- Ejemplo:
/*
SELECT
e.EmployeeId,
...
FROM
...
*/
END;
----Ejercicio 5: Gestión de Inventario Musical
CREATE PROCEDURE GestionDeInventarioMusical
AS
BEGIN
SELECT
t.Name AS TrackName,
COUNT(il.InvoiceLineId) AS TimesSold,
YEAR(i.InvoiceDate) AS Year,
MONTH(i.InvoiceDate) AS Month
FROM InvoiceLine il
JOIN Invoice i ON il.InvoiceId = i.InvoiceId
JOIN Track t ON il.TrackId = t.TrackId
GROUP BY
t.Name,
YEAR(i.InvoiceDate),
MONTH(i.InvoiceDate)
ORDER BY
Year,
Month,
TimesSold DESC;
SELECT
g.Name AS Genre,
c.Country,
COUNT(il.InvoiceLineId) AS TimesSold
FROM InvoiceLine il
JOIN Invoice i ON il.InvoiceId = i.InvoiceId
JOIN Customer c ON i.CustomerId = c.CustomerId
JOIN Track t ON il.TrackId = t.TrackId
JOIN Genre g ON t.GenreId = g.GenreId
GROUP BY
g.Name,
c.Country
ORDER BY
c.Country,
TimesSold DESC;
SELECT
t.TrackId,
t.Name AS TrackName,
COUNT(il.InvoiceLineId) AS TimesSold,
t.UnitPrice,
CASE
WHEN COUNT(il.InvoiceLineId) >= 10 THEN 'Alta demanda - Puede
subir precio'
WHEN COUNT(il.InvoiceLineId) BETWEEN 5 AND 9 THEN 'Demanda
media - Precio estable'
ELSE 'Baja demanda - Considerar bajar precio'
END AS PriceSuggestion
FROM Track t
LEFT JOIN InvoiceLine il ON t.TrackId = il.TrackId
GROUP BY
t.TrackId,
t.Name,
t.UnitPrice
ORDER BY TimesSold DESC;
SELECT
t.TrackId,
t.Name AS TrackName,
COUNT(il.InvoiceLineId) AS TimesSold,
CASE
WHEN COUNT(il.InvoiceLineId) = 0 THEN 'Sin movimiento'
WHEN COUNT(il.InvoiceLineId) >= 15 THEN 'Producto estrella'
ELSE 'En rotación normal'
END AS Alerta
FROM Track t
LEFT JOIN InvoiceLine il ON t.TrackId = il.TrackId
GROUP BY
t.TrackId,
t.Name
ORDER BY TimesSold ASC;
END
Análisis:
1. Implementación de Inteligencia de Negocio con SQL
Los procedimientos desarrollados son ejemplos funcionales de Business
Intelligence aplicados con SQL, ideales para ser integrados en
dashboards o informes automatizados.
Ofrecen datos valiosos para áreas como ventas, mercadeo, gestión de
talento y análisis musical.
2. Análisis de desempeño del equipo de trabajo
El procedimiento uspEmployeePerformance permite evaluar el
rendimiento de los empleados mediante cálculos de comisiones,
rankings y comparaciones internas.
Entre los resultados clave están:
Identificación de los empleados más productivos.
Detección de los clientes más frecuentes por cada trabajador.
Tendencias mensuales en desempeño.
Comparativas con el promedio del grupo.
3. Generación automática de playlists inteligentes
Con uspPlaylistGenerator se demuestra cómo crear listas musicales
basadas en parámetros definidos como duración total y presupuesto
disponible.
El ordenamiento por popularidad y la eliminación de duplicados
optimizan tanto la calidad del contenido como la experiencia del usuario.
4. Automatización de reportes comerciales
uspSalesReport genera informes detallados de ventas segmentadas por
país, género musical, artista, y métricas como promedio y crecimiento.
La incorporación de parámetros dinámicos (fechas, TopN, crecimiento) lo
convierte en una herramienta reutilizable y adaptable a distintos
análisis.
5. Aprovechamiento de relaciones entre datos
Gracias al diseño relacional de la base de datos Chinook, es posible
realizar consultas complejas cruzando datos de empleados, clientes,
ventas, canciones y artistas.
Estas conexiones permiten obtener insights valiosos como
comportamientos de consumo, rankings históricos y comparativas entre
periodos.
Conclusión: Este trabajo demuestra cómo, utilizando SQL Server y una
base de datos bien estructurada, se pueden crear soluciones prácticas
para el análisis de datos, la automatización de decisiones y la mejora
continua en contextos reales como la música, las ventas y la gestión de
personal. Estas habilidades son fundamentales en carreras de desarrollo
de software, análisis de datos y sistemas de información.
-