(+351) 21 24 10006  ·  info@bconcepts.pt
Carnaxide, Lisboa

Cómo crear una tabla de hechos sin hechos (factless) en SQL

João Barros 11 de July de 2026 4 min de lectura

Una tabla de hechos sin hechos (en inglés, factless fact table) es una tabla de hechos que no almacena medidas numéricas — solo las claves foráneas que enlazan con las dimensiones. A primera vista, tener una tabla de hechos "vacía" parece una contradicción, pero es la forma más elegante de registrar que un evento ocurrió (una asistencia, un clic, una inscripción) y de responder a preguntas sobre conteos y, sobre todo, sobre lo que no ocurrió.

Requisitos previos

  • Un data warehouse o base de datos relacional (los ejemplos usan SQL Server, pero el patrón es igual en cualquier motor).
  • Nociones básicas de modelado dimensional: qué son las dimensiones, los hechos y el esquema en estrella.
  • Permisos para crear tablas e insertar datos.

Paso 1: Entender cuándo usarla

Existen dos escenarios clásicos para este patrón. El primero es el registro de eventos: cada fila indica que algo ocurrió en un momento dado — un alumno asistió a una clase, un usuario vio una página, una credencial se validó en la entrada. El segundo es la cobertura (o elegibilidad): la tabla registra relaciones que podrían ocurrir para poder medir lo que no ocurrió. Sin esas filas es imposible responder a "¿quién faltó?", porque una ausencia, por su propia naturaleza, no genera ningún registro.

Paso 2: Modelar las dimensiones

Usaremos el ejemplo de asistencia a clases. Necesitamos tres dimensiones: el alumno, la clase y la fecha. Suponemos que ya existen en el modelo, cada una con su propia clave subrogada (surrogate key).

-- Dimensiones (simplificadas)
CREATE TABLE DimAluno (
    AlunoKey   INT PRIMARY KEY,
    Nome       NVARCHAR(100)
);

CREATE TABLE DimTurma (
    TurmaKey   INT PRIMARY KEY,
    Disciplina NVARCHAR(100)
);

CREATE TABLE DimData (
    DataKey    INT PRIMARY KEY,   -- p. ej., 20260711
    Data       DATE
);

Paso 3: Crear la tabla de hechos sin hechos

La tabla de asistencias contiene solo claves. No hay columnas de cantidad ni de importe — de ahí el nombre "sin hechos". El grano (el nivel de detalle de cada fila) es claro: una fila por alumno, por clase y por día. Cada fila representa, por tanto, una asistencia confirmada.

CREATE TABLE FactPresenca (
    AlunoKey INT  NOT NULL REFERENCES DimAluno(AlunoKey),
    TurmaKey INT  NOT NULL REFERENCES DimTurma(TurmaKey),
    DataKey  INT  NOT NULL REFERENCES DimData(DataKey),
    CONSTRAINT PK_FactPresenca PRIMARY KEY (AlunoKey, TurmaKey, DataKey)
);
Consejo: algunos modeladores añaden una columna constante con el valor 1 (por ejemplo Presencas) para simplificar las sumas. Es opcional; contar filas da exactamente el mismo resultado.

Paso 4: Cargar algunos datos

INSERT INTO FactPresenca (AlunoKey, TurmaKey, DataKey) VALUES
    (1, 10, 20260711),
    (2, 10, 20260711),
    (3, 10, 20260711);

Estas tres filas indican que los alumnos 1, 2 y 3 asistieron a la clase 10 el 11 de julio de 2026.

Paso 5: Contar eventos

Para saber cuántos alumnos asistieron a cada clase, contamos las filas. Como no hay ninguna medida que sumar, usamos COUNT(*) en lugar de SUM — esa es la diferencia fundamental respecto a una tabla de hechos tradicional.

SELECT t.Disciplina,
       d.Data,
       COUNT(*) AS TotalPresencas
FROM   FactPresenca f
JOIN   DimTurma t ON t.TurmaKey = f.TurmaKey
JOIN   DimData  d ON d.DataKey  = f.DataKey
GROUP BY t.Disciplina, d.Data;

Paso 6: Responder a las ausencias con cobertura

Aquí está el verdadero poder del patrón. Para saber quién faltó, necesitamos saber quién debería haber asistido. Una segunda tabla sin hechos — la de cobertura — registra las inscripciones: qué alumno está inscrito en qué clase.

-- Cobertura: inscripciones (quién debería asistir)
CREATE TABLE FactInscricao (
    AlunoKey INT NOT NULL,
    TurmaKey INT NOT NULL,
    CONSTRAINT PK_FactInscricao PRIMARY KEY (AlunoKey, TurmaKey)
);

Con un LEFT JOIN entre los inscritos y los presentes, las filas sin correspondencia revelan las ausencias de un día:

SELECT i.AlunoKey
FROM   FactInscricao i
LEFT   JOIN FactPresenca p
       ON p.AlunoKey = i.AlunoKey
      AND p.TurmaKey = i.TurmaKey
      AND p.DataKey  = 20260711
WHERE  i.TurmaKey = 10
  AND  p.AlunoKey IS NULL;

Verificar el resultado

Si el alumno 4 está inscrito en la clase 10 pero no tiene fila en FactPresenca para el día 11, la última consulta devuelve su AlunoKey. Confirme también que el conteo del Paso 5 coincide con el número de filas insertadas — tres asistencias para la clase 10. Si ambos números cuadran, el modelo es correcto.

Conclusión

Una tabla de hechos sin hechos resuelve, de forma sencilla, dos problemas que de otro modo serían complicados: contar eventos y medir lo que no ocurrió. El siguiente paso es aplicar el mismo patrón a otros dominios — clics en campañas, promociones activas por tienda o visitas a páginas. Queda la pregunta: ¿qué evento importante de tu negocio aún no se está registrando solo porque "no tiene ninguna medida que guardar"?