Cómo analizar inventarios con SQL para controlar stock, costes y rotación

Cómo analizar inventarios con SQL para controlar stock, costes y rotación

Analizar inventarios con SQL permite entender qué productos se mueven, cuáles están parados, qué referencias están por debajo del stock mínimo y cuánto dinero hay inmovilizado en existencias. Para una microempresa o un pequeño negocio, esta información puede marcar la diferencia entre comprar con criterio o acumular productos sin control.

El inventario no es solo una lista de artículos. Es una parte crítica de la operativa: afecta a ventas, compras, atención al cliente, liquidez, almacenamiento y planificación. Un inventario mal analizado puede generar roturas de stock, exceso de mercancía, compras duplicadas o decisiones basadas en intuiciones incompletas.

SQL ayuda a convertir movimientos, productos y cantidades en indicadores útiles. No hace falta montar una plataforma compleja para empezar. Una base de datos ligera, bien estructurada y unas consultas claras pueden ofrecer una visión mucho más fiable que varias hojas de cálculo dispersas.

La clave está en formular preguntas prácticas: qué falta, qué sobra, qué rota rápido, qué no se mueve, qué producto concentra más valor y qué cambios deben revisarse antes de comprar más.

Índice

Por qué analizar inventarios con SQL

SQL permite consultar datos de inventario de forma repetible, clara y auditable. En lugar de revisar manualmente una hoja de cálculo cada vez, se pueden crear consultas que respondan siempre a las mismas preguntas con el mismo criterio.

Esto es especialmente útil en pequeños negocios donde el inventario suele gestionarse con soluciones improvisadas: una hoja de cálculo, notas sueltas, exportaciones de una tienda online, listados de proveedores o registros manuales.

Analizar inventarios con SQL ayuda a:

  • Detectar productos que deben reponerse.
  • Identificar referencias sin movimiento.
  • Calcular el valor económico del stock.
  • Ver qué productos concentran más ventas.
  • Reducir compras innecesarias.
  • Mejorar la planificación de almacén.
  • Evitar decisiones basadas solo en percepción.

Este enfoque encaja con una forma práctica de mejorar decisiones con datos, porque convierte el inventario en una fuente de información operativa, no solo en un listado administrativo.

Qué datos necesitas para analizar un inventario

Antes de escribir consultas SQL conviene revisar qué información existe y si está suficientemente ordenada. Un análisis de inventario depende de la calidad de los datos de partida.

Datos de productos

La tabla de productos debe contener la información básica de cada referencia. Por ejemplo:

  • Identificador del producto.
  • Nombre del producto.
  • Referencia interna o SKU.
  • Categoría.
  • Proveedor.
  • Coste unitario.
  • Precio de venta.
  • Stock mínimo.
  • Estado activo o inactivo.

El identificador es importante porque el nombre del producto puede cambiar, repetirse o escribirse de forma diferente. Una buena estructura evita errores posteriores.

Datos de movimientos

Para analizar el inventario de verdad no basta con saber el stock actual. Conviene registrar movimientos: entradas, salidas, ajustes, devoluciones o regularizaciones.

Una tabla de movimientos permite reconstruir la historia del inventario y detectar patrones. Por ejemplo, qué productos salen con más frecuencia o cuándo se producen ajustes anómalos.

Datos temporales

La fecha es esencial. Sin fechas fiables no se puede analizar rotación, evolución mensual, productos parados ni comportamiento por periodos.

Conviene usar un formato homogéneo y evitar mezclar fechas escritas de forma manual con formatos diferentes.

Datos limpios

Antes de construir informes o dashboards, hay que revisar duplicados, referencias repetidas y categorías mal escritas. Un producto duplicado puede hacer que el análisis sea engañoso.

Si ya existen inconsistencias, conviene aplicar una revisión previa como la explicada en cómo detectar datos duplicados.

Modelo básico de tablas para inventario

Un modelo sencillo de inventario puede empezar con dos tablas principales: productos y movimientos de inventario. Esta estructura permite controlar existencias sin crear una arquitectura innecesariamente compleja.

Tabla de productos

La tabla de productos contiene una fila por cada producto o referencia gestionada:

CREATE TABLE productos (
    id_producto INTEGER PRIMARY KEY,
    referencia TEXT,
    nombre TEXT,
    categoria TEXT,
    proveedor TEXT,
    coste_unitario REAL,
    precio_venta REAL,
    stock_minimo INTEGER,
    activo INTEGER
);

Esta tabla define qué productos existen y qué información básica se conoce de ellos.

Tabla de movimientos

La tabla de movimientos registra cada entrada, salida o ajuste:

CREATE TABLE movimientos_inventario (
    id_movimiento INTEGER PRIMARY KEY,
    id_producto INTEGER,
    fecha_movimiento TEXT,
    tipo_movimiento TEXT,
    cantidad INTEGER,
    observaciones TEXT,
    FOREIGN KEY (id_producto) REFERENCES productos(id_producto)
);

El campo tipo_movimiento puede tener valores como entrada, salida, ajuste, devolución o regularización. Lo importante es mantener un criterio estable.

Por qué separar productos y movimientos

Separar productos y movimientos evita mezclar datos permanentes con eventos. El producto es la referencia; el movimiento es algo que ocurre en una fecha concreta.

Esta separación permite responder preguntas como:

  • Cuánto stock queda ahora.
  • Cuántas unidades han entrado este mes.
  • Cuántas unidades han salido en los últimos 90 días.
  • Qué productos no han tenido movimientos recientes.
  • Qué categorías concentran más valor.

Si el negocio está empezando a ordenar información desde cero, una base ligera como la descrita en SQLite para pequeños negocios puede ser suficiente para esta primera capa de gestión.

Cómo calcular el stock actual

El stock actual puede calcularse sumando entradas y restando salidas. Para hacerlo bien, conviene definir cómo se representará cada tipo de movimiento.

Una forma sencilla es guardar las entradas como cantidades positivas y las salidas como cantidades negativas. Otra opción es guardar siempre cantidades positivas y usar el tipo de movimiento para decidir si suman o restan.

Stock actual con cantidades firmadas

Si las entradas son positivas y las salidas negativas, la consulta puede ser muy simple:

SELECT p.id_producto,
       p.referencia,
       p.nombre,
       SUM(m.cantidad) AS stock_actual
FROM productos p
LEFT JOIN movimientos_inventario m ON m.id_producto = p.id_producto
GROUP BY p.id_producto, p.referencia, p.nombre
ORDER BY p.nombre;

Esta consulta muestra el stock actual calculado a partir del historial de movimientos.

Stock actual separando entradas y salidas

Si las cantidades se guardan siempre positivas, se puede usar una condición:

SELECT p.id_producto,
       p.referencia,
       p.nombre,
       SUM(
           CASE
               WHEN m.tipo_movimiento = 'entrada' THEN m.cantidad
               WHEN m.tipo_movimiento = 'salida' THEN -m.cantidad
               WHEN m.tipo_movimiento = 'devolucion' THEN m.cantidad
               WHEN m.tipo_movimiento = 'ajuste' THEN m.cantidad
               ELSE 0
           END
       ) AS stock_actual
FROM productos p
LEFT JOIN movimientos_inventario m ON m.id_producto = p.id_producto
GROUP BY p.id_producto, p.referencia, p.nombre
ORDER BY p.nombre;

Este enfoque es más explícito y permite distinguir mejor el origen de cada movimiento.

Productos sin movimientos

Es importante usar LEFT JOIN para no perder productos que todavía no tienen movimientos registrados. Un producto sin movimientos también puede ser relevante: quizá está mal creado, pendiente de recibir o inactivo.

Cómo detectar productos bajo stock mínimo

Una de las consultas más útiles en inventario es detectar productos cuyo stock actual está por debajo del mínimo definido. Esta información permite generar alertas de reposición.

SELECT producto,
       referencia,
       stock_actual,
       stock_minimo
FROM (
    SELECT p.nombre AS producto,
           p.referencia,
           p.stock_minimo,
           SUM(
               CASE
                   WHEN m.tipo_movimiento = 'entrada' THEN m.cantidad
                   WHEN m.tipo_movimiento = 'salida' THEN -m.cantidad
                   WHEN m.tipo_movimiento = 'devolucion' THEN m.cantidad
                   WHEN m.tipo_movimiento = 'ajuste' THEN m.cantidad
                   ELSE 0
               END
           ) AS stock_actual
    FROM productos p
    LEFT JOIN movimientos_inventario m ON m.id_producto = p.id_producto
    WHERE p.activo = 1
    GROUP BY p.id_producto, p.nombre, p.referencia, p.stock_minimo
)
WHERE stock_actual <= stock_minimo
ORDER BY stock_actual ASC;

Esta consulta puede convertirse en una lista de revisión semanal o en una alerta dentro de un dashboard.

Por qué el stock mínimo debe revisarse

El stock mínimo no debería ser un número fijo decidido una vez y olvidado para siempre. Debe adaptarse a la rotación, el plazo de entrega, la importancia del producto y el coste de quedarse sin existencias.

Un producto con alta rotación necesita un mínimo distinto a un producto que apenas se vende. SQL puede ayudar a detectar esas diferencias con datos reales.

Cómo analizar la rotación de inventario

La rotación indica cómo se mueve un producto durante un periodo. Un producto con buena rotación sale con frecuencia. Un producto con baja rotación ocupa espacio, inmoviliza dinero y puede quedar obsoleto.

Salidas por producto en los últimos 90 días

SELECT p.referencia,
       p.nombre,
       SUM(m.cantidad) AS unidades_vendidas
FROM productos p
JOIN movimientos_inventario m ON m.id_producto = p.id_producto
WHERE m.tipo_movimiento = 'salida'
  AND m.fecha_movimiento >= date('now', '-90 days')
GROUP BY p.id_producto, p.referencia, p.nombre
ORDER BY unidades_vendidas DESC;

Esta consulta muestra qué productos han salido más en los últimos 90 días.

Rotación por categoría

También puede analizarse la rotación por categoría:

SELECT p.categoria,
       SUM(m.cantidad) AS unidades_vendidas
FROM productos p
JOIN movimientos_inventario m ON m.id_producto = p.id_producto
WHERE m.tipo_movimiento = 'salida'
  AND m.fecha_movimiento >= date('now', '-90 days')
GROUP BY p.categoria
ORDER BY unidades_vendidas DESC;

Esto permite detectar qué líneas de producto tienen más actividad y cuáles requieren revisión.

Interpretar la rotación con cuidado

Una alta rotación no siempre significa alta rentabilidad. Un producto puede vender muchas unidades y dejar poco margen. Otro puede vender menos, pero aportar más valor económico.

Por eso conviene combinar unidades vendidas, margen, coste de reposición y valor inmovilizado.

Cómo detectar productos sin movimiento

Los productos sin movimiento son una señal importante. Pueden indicar referencias obsoletas, errores de catálogo, exceso de stock, mala visibilidad comercial o productos que simplemente no encajan con la demanda.

Productos sin movimientos registrados

SELECT p.id_producto,
       p.referencia,
       p.nombre
FROM productos p
LEFT JOIN movimientos_inventario m ON m.id_producto = p.id_producto
WHERE m.id_movimiento IS NULL
  AND p.activo = 1
ORDER BY p.nombre;

Esta consulta muestra productos activos que nunca han tenido movimientos.

Productos sin movimiento reciente

SELECT p.id_producto,
       p.referencia,
       p.nombre,
       MAX(m.fecha_movimiento) AS ultimo_movimiento
FROM productos p
LEFT JOIN movimientos_inventario m ON m.id_producto = p.id_producto
WHERE p.activo = 1
GROUP BY p.id_producto, p.referencia, p.nombre
HAVING ultimo_movimiento IS NULL
   OR ultimo_movimiento < date('now', '-180 days')
ORDER BY ultimo_movimiento;

Esta consulta ayuda a localizar productos que llevan demasiado tiempo sin entradas ni salidas.

Qué hacer con productos sin movimiento

Detectar el producto parado es solo el primer paso. Después conviene decidir:

  • Si debe liquidarse.
  • Si debe eliminarse del catálogo.
  • Si necesita mejor presentación comercial.
  • Si debe agruparse con otro producto.
  • Si su stock mínimo debe ajustarse a cero.

La decisión no debe ser automática. SQL muestra señales; la interpretación requiere criterio de negocio.

Cómo calcular el valor del inventario

El inventario tiene un impacto financiero directo. Cada producto almacenado representa dinero inmovilizado. Calcular el valor del inventario ayuda a entender cuánto capital está retenido en existencias.

Valor total del inventario

SELECT SUM(stock_actual * coste_unitario) AS valor_total_inventario
FROM (
    SELECT p.id_producto,
           p.coste_unitario,
           SUM(
               CASE
                   WHEN m.tipo_movimiento = 'entrada' THEN m.cantidad
                   WHEN m.tipo_movimiento = 'salida' THEN -m.cantidad
                   WHEN m.tipo_movimiento = 'devolucion' THEN m.cantidad
                   WHEN m.tipo_movimiento = 'ajuste' THEN m.cantidad
                   ELSE 0
               END
           ) AS stock_actual
    FROM productos p
    LEFT JOIN movimientos_inventario m ON m.id_producto = p.id_producto
    WHERE p.activo = 1
    GROUP BY p.id_producto, p.coste_unitario
);

Este dato permite ver el valor económico total del stock activo.

Valor por categoría

SELECT categoria,
       SUM(stock_actual * coste_unitario) AS valor_categoria
FROM (
    SELECT p.id_producto,
           p.categoria,
           p.coste_unitario,
           SUM(
               CASE
                   WHEN m.tipo_movimiento = 'entrada' THEN m.cantidad
                   WHEN m.tipo_movimiento = 'salida' THEN -m.cantidad
                   WHEN m.tipo_movimiento = 'devolucion' THEN m.cantidad
                   WHEN m.tipo_movimiento = 'ajuste' THEN m.cantidad
                   ELSE 0
               END
           ) AS stock_actual
    FROM productos p
    LEFT JOIN movimientos_inventario m ON m.id_producto = p.id_producto
    WHERE p.activo = 1
    GROUP BY p.id_producto, p.categoria, p.coste_unitario
)
GROUP BY categoria
ORDER BY valor_categoria DESC;

Este análisis ayuda a detectar qué categorías concentran más inversión.

Productos con más capital inmovilizado

SELECT referencia,
       nombre,
       stock_actual,
       coste_unitario,
       stock_actual * coste_unitario AS valor_stock
FROM (
    SELECT p.referencia,
           p.nombre,
           p.coste_unitario,
           SUM(
               CASE
                   WHEN m.tipo_movimiento = 'entrada' THEN m.cantidad
                   WHEN m.tipo_movimiento = 'salida' THEN -m.cantidad
                   WHEN m.tipo_movimiento = 'devolucion' THEN m.cantidad
                   WHEN m.tipo_movimiento = 'ajuste' THEN m.cantidad
                   ELSE 0
               END
           ) AS stock_actual
    FROM productos p
    LEFT JOIN movimientos_inventario m ON m.id_producto = p.id_producto
    WHERE p.activo = 1
    GROUP BY p.id_producto, p.referencia, p.nombre, p.coste_unitario
)
ORDER BY valor_stock DESC
LIMIT 20;

Esta consulta es muy útil para revisar dónde está concentrado el dinero del inventario.

Alertas útiles para gestión diaria

SQL puede servir para construir alertas operativas. No hace falta esperar a una revisión mensual si se pueden detectar situaciones críticas con consultas recurrentes.

Productos con stock negativo

Un stock negativo suele indicar un error de registro, una salida sin entrada previa o una regularización pendiente:

SELECT referencia,
       nombre,
       stock_actual
FROM (
    SELECT p.referencia,
           p.nombre,
           SUM(
               CASE
                   WHEN m.tipo_movimiento = 'entrada' THEN m.cantidad
                   WHEN m.tipo_movimiento = 'salida' THEN -m.cantidad
                   WHEN m.tipo_movimiento = 'devolucion' THEN m.cantidad
                   WHEN m.tipo_movimiento = 'ajuste' THEN m.cantidad
                   ELSE 0
               END
           ) AS stock_actual
    FROM productos p
    LEFT JOIN movimientos_inventario m ON m.id_producto = p.id_producto
    GROUP BY p.id_producto, p.referencia, p.nombre
)
WHERE stock_actual < 0;

Productos activos sin proveedor

SELECT referencia,
       nombre
FROM productos
WHERE activo = 1
  AND (proveedor IS NULL OR proveedor = '');

Este control ayuda a revisar datos incompletos antes de necesitar una reposición urgente.

Movimientos sin observaciones en ajustes

SELECT id_movimiento,
       id_producto,
       fecha_movimiento,
       cantidad
FROM movimientos_inventario
WHERE tipo_movimiento = 'ajuste'
  AND (observaciones IS NULL OR observaciones = '');

Los ajustes de inventario deberían estar justificados. Si no se documentan, se pierde trazabilidad.

Entradas o salidas anómalas

También pueden buscarse movimientos con cantidades inusualmente altas:

SELECT *
FROM movimientos_inventario
WHERE cantidad > 100
ORDER BY fecha_movimiento DESC;

El umbral dependerá del negocio. Lo importante es detectar operaciones que merecen revisión.

Estas alertas pueden alimentar un panel de control como los descritos en cómo crear dashboards con SQL.

Errores comunes al analizar inventarios con SQL

SQL puede aportar mucha claridad, pero también puede producir resultados engañosos si los datos o las consultas están mal diseñados.

No registrar movimientos

Si solo se guarda el stock actual, se pierde la historia. Sin movimientos no se puede analizar rotación, detectar tendencias ni revisar errores pasados.

El stock actual importa, pero el historial explica cómo se ha llegado a él.

Mezclar productos con movimientos

Guardar entradas y salidas directamente en la tabla de productos puede parecer sencillo, pero limita mucho el análisis. Es mejor separar la definición del producto de sus movimientos.

No controlar duplicados

Dos productos duplicados pueden dividir ventas, stock y movimientos. Esto hace que el análisis parezca correcto, pero esté repartido entre varias referencias.

La detección de duplicados debería formar parte del mantenimiento del inventario.

No definir bien los tipos de movimiento

Si cada persona escribe los movimientos de una manera distinta, las consultas se vuelven poco fiables. Por ejemplo: “salida”, “venta”, “vendido” y “baja” pueden acabar significando cosas parecidas, pero SQL las tratará como valores distintos.

Conviene usar un conjunto limitado y documentado de tipos de movimiento.

Ignorar productos inactivos

Los productos inactivos pueden distorsionar informes si se mezclan con referencias activas. Es útil conservar histórico, pero los dashboards operativos deberían distinguir claramente entre productos activos e inactivos.

Confundir unidades con valor

Vender muchas unidades no siempre significa aportar más valor. Para analizar bien el inventario hay que mirar unidades, coste, margen, rotación y valor inmovilizado.

No validar las consultas

Antes de confiar en un informe SQL conviene contrastar algunos resultados manualmente. Una unión incorrecta entre tablas puede duplicar cantidades o excluir movimientos.

Los errores en consultas de inventario pueden llevar a comprar de más, comprar de menos o interpretar mal la rentabilidad de una línea de producto.

Conclusión: SQL convierte el inventario en una herramienta de decisión

Analizar inventarios con SQL permite pasar de un control reactivo a una gestión más ordenada. En lugar de mirar una lista de productos cuando surge un problema, el negocio puede revisar alertas, rotación, valor inmovilizado y movimientos relevantes con consultas repetibles.

Para una microempresa, esto no requiere empezar con una solución compleja. Una estructura básica de productos y movimientos, una base de datos ligera y varias consultas bien diseñadas pueden aportar una mejora enorme frente a hojas de cálculo dispersas.

El objetivo no es tener una base de datos por tenerla. El objetivo es saber qué falta, qué sobra, qué se mueve, qué no se vende y dónde está concentrado el dinero del inventario.

Un inventario bien analizado ayuda a comprar mejor, reducir errores, liberar capital, mejorar el servicio y tomar decisiones con menos improvisación.

SQL no sustituye al criterio empresarial, pero lo refuerza. Convierte los datos operativos en señales claras para actuar.

Preguntas frecuentes sobre análisis de inventarios con SQL

¿SQL sirve para controlar inventarios pequeños?

Sí. SQL puede ser muy útil incluso en inventarios pequeños, porque permite calcular stock, revisar movimientos, detectar productos bajo mínimo y analizar rotación sin depender de revisiones manuales.

¿Necesito una base de datos grande para analizar inventarios?

No necesariamente. Para muchos pequeños negocios puede bastar una base SQLite o una base sencilla con tablas de productos y movimientos. Lo importante es que los datos estén bien estructurados.

¿Qué es más importante: el stock actual o los movimientos?

Ambos son importantes, pero los movimientos aportan contexto. El stock actual indica cuánto queda; los movimientos explican entradas, salidas, ajustes y evolución del inventario.

¿Cómo detecto productos que debo reponer?

Se pueden detectar comparando el stock actual calculado con el stock mínimo definido para cada producto. Los productos cuyo stock esté por debajo o igual al mínimo deberían revisarse.

¿Cómo encuentro productos que no se venden?

Puede consultarse la última fecha de movimiento o las salidas de un periodo concreto. Si un producto activo no ha tenido salidas durante muchos meses, conviene revisar su continuidad, promoción o stock.

¿Qué errores debo evitar al usar SQL para inventario?

Los errores más comunes son no registrar movimientos, tener productos duplicados, mezclar tipos de movimiento, no validar consultas y analizar solo unidades sin considerar coste, margen o valor inmovilizado.