Cómo crear una tabla de historial de precios en Data Warehouse: paso a paso
Este tutorial muestra cómo crear una tabla de historial de precios en Data Warehouse para registrar cambios de precio de productos a lo largo del tiempo. Tener un historial de precios permite análisis temporales, calcular tendencias y reconstruir precios válidos para cualquier fecha.
Requisitos previos
- Conocimientos básicos de SQL (SELECT, INSERT, UPDATE)
- Entorno Data Warehouse con soporte SQL (p.ej.: Azure Synapse, SQL Server, Snowflake)
- Tabla de origen de productos con identificador y precio actual
- Permisos para crear tablas y triggers/procedures (o procesos ETL)
Paso 1: Elegir el modelo de historial (SCD vs timeline)
Hay dos enfoques comunes: usar SCD Tipo 2 (registros con date_effective/date_end y flag activo) o mantener una tabla de eventos de precio (cada cambio es un evento). Para historial de precios simple y consultas por fecha, el patrón SCD Tipo 2 o una timeline con date_effective es ideal. Aquí usamos una timeline con date_effective y date_end para consultas sencillas.
Paso 2: Crear la tabla de historial de precios
Crear la tabla que almacenará versiones de precio. Mantenemos campos esenciales: product_id, price, currency, date_effective (inicio de validez), date_end (fin de validez, NULL si actual), is_current.
CREATE TABLE price_history (
price_history_id BIGINT IDENTITY(1,1) PRIMARY KEY,
product_id INT NOT NULL,
price DECIMAL(18,4) NOT NULL,
currency VARCHAR(3) NOT NULL DEFAULT 'EUR',
date_effective DATE NOT NULL,
date_end DATE NULL,
is_current BIT NOT NULL DEFAULT 1,
created_at DATETIME2 NOT NULL DEFAULT SYSUTCDATETIME()
);
Paso 3: Población inicial desde la tabla de productos
Copie el estado actual de los precios a price_history con date_effective igual a la fecha de carga. Este paso garantiza que existe al menos un registro por producto.
INSERT INTO price_history (product_id, price, currency, date_effective, date_end, is_current)
SELECT p.product_id, p.price, p.currency, CAST(GETDATE() AS DATE), NULL, 1
FROM products p
WHERE p.price IS NOT NULL;
Paso 4: Procesar cambios de precio (ETL o trigger)
Cuando llega una nueva carga de precios (feed diario o evento), compare con price_history y cierre registros antiguos antes de insertar el nuevo. Abajo un ejemplo ETL en SQL que procesa la tabla staging_prices con los precios más recientes.
-- 1) Encontrar productos con cambio de precio
WITH changes AS (
SELECT s.product_id, s.price, s.currency
FROM staging_prices s
JOIN price_history ph ON ph.product_id = s.product_id AND ph.is_current = 1
WHERE ph.price <> s.price OR ph.currency <> s.currency
)
-- 2) Cierra los registros actuales, estableciendo date_end e is_current = 0
UPDATE ph
SET ph.date_end = CAST(GETDATE() AS DATE), ph.is_current = 0
FROM price_history ph
JOIN changes c ON ph.product_id = c.product_id
WHERE ph.is_current = 1;
-- 3) Inserta nuevos registros con la nueva validez
INSERT INTO price_history (product_id, price, currency, date_effective, date_end, is_current)
SELECT c.product_id, c.price, c.currency, CAST(GETDATE() AS DATE), NULL, 1
FROM changes c;
Paso 5: Consultas comunes y optimizaciones
Consulta del precio válido en una fecha, agregaciones de tendencia e índices para rendimiento. Cree índice por (product_id, is_current) y por (product_id, date_effective, date_end) si hay muchas búsquedas históricas.
-- Precio actual por producto
SELECT product_id, price
FROM price_history
WHERE product_id = 123 AND is_current = 1;
-- Precio válido en una fecha específica
SELECT price
FROM price_history
WHERE product_id = 123
AND date_effective <= '2026-10-01'
AND (date_end IS NULL OR date_end >= '2026-10-01');
-- Índice sugerido (SQL Server example)
CREATE INDEX IX_price_history_product_current ON price_history(product_id, is_current);
Verificar el resultado
Confirme que cada producto tiene exactamente un registro con is_current = 1 (si existe precio actual) y que los registros antiguos tienen date_end relleno. Ejecute muestras de consultas por fecha para validar valores históricos. Ejemplos:
-- Verificar unicity current
SELECT product_id, COUNT(*) as cnt
FROM price_history
WHERE is_current = 1
GROUP BY product_id
HAVING COUNT(*) > 1;
-- Verificar productos sin registro
SELECT p.product_id
FROM products p
LEFT JOIN price_history ph ON p.product_id = ph.product_id
WHERE ph.price_history_id IS NULL;
Conclusión
Una tabla de historial de precios bien diseñada permite análisis temporales y consistencia en los informes. Próximos pasos: automatizar el proceso ETL, gestionar fusiones de productos y administrar reglas de moneda. Consejo: probar el flujo con datos reales y comprobar edge cases como cambios múltiples en el mismo día.