Schema físico
El schema físico es donde le dice a Mondrian sobre los objetos de base de datos que están debajo de sus cubos: qué tablas existen, cómo se tipan sus columnas, cómo se relacionan las tablas unas con otras, y qué joins son seguros de atravesar automáticamente. Todo en <PhysicalSchema> es descripción puramente estructural — sin semántica analítica aún, solo la fontanería.
Table
Una table es un uso nombrado de una tabla de base de datos. La declara con un elemento <Table> dentro de <PhysicalSchema>. El único atributo requerido es name; si la tabla vive en un schema de base de datos distinto al por defecto, añada el atributo schema:
- name: "sales_fact_1997" schema: "Foodmart"<Table schema="Foodmart" name="sales_fact_1997"/>Claves primarias
Mondrian necesita conocer la clave primaria de cada tabla de dimensión para poder unir correctamente. Puede declararla en línea en el elemento usando keyColumn (una sola columna) o con un elemento anidado <Key> (clave compuesta):
# single-column shorthand- name: "product" key_column: "product_id"
# composite key- name: "time_by_day" key: - "the_year" - "quarter"<!-- single-column shorthand --><Table name="product" keyColumn="product_id"/>
<!-- composite key --><Table name="time_by_day"> <Key> <Column name="the_year"/> <Column name="quarter"/> </Key></Table>Las tablas de hechos no necesitan una declaración <Key>.
Columnas y columnas calculadas
Dentro de un <Table> opcionalmente puede definir una sección <ColumnDefs>. Si la omite, Mondrian lee las definiciones de columna de JDBC — que es adecuado para la mayoría de las situaciones. Cuando necesita tipado preciso o columnas computadas, decláralas explícitamente:
<Table name="customer"> <ColumnDefs> <ColumnDef name="customer_id" type="Integer" internalType="int"/> <ColumnDef name="fname"/> <ColumnDef name="lname"/> <CalculatedColumnDef name="full_name" type="String"> <ExpressionView> <SQL dialect="mysql"> CONCAT(<Column name="fname"/>, ' ', <Column name="lname"/>) </SQL> <SQL dialect="generic"> <Column name="fullname"/> </SQL> </ExpressionView> </CalculatedColumnDef> </ColumnDefs></Table>(Este ejemplo se muestra solo en XML: el formato YAML del schema lleva columnas calculadas pero no metadatos de tipado de <ColumnDef> simples, y codifica el SQL de columna calculada como una cadena por dialecto en lugar de la forma de elemento <Column> anidado mostrada arriba. Una columna calculada cuyo cuerpo es SQL plano — consulte Columnas calculadas en medidas — hace round-trip fielmente a través de YAML.)
<ColumnDef> declara que una columna física existe y cómo interpretarla:
type— el tipo de dato Mondrian (String,Integer,Numeric,Boolean,Date,Time,Timestamp). Esto controla el orden de clasificación y el tipo MDX de las expresiones construidas a partir de la columna.internalType— el tipo Java que Mondrian usa para almacenar valores en memoria (por ejemplo,intcausa llamadas aResultSet.getInt()y almacenamientoint). Use con moderación; el por defecto suele ser correcto.
<CalculatedColumnDef> define una columna virtual como una expresión SQL. Puede suministrar cuerpos SQL por dialecto — Mondrian elige el correcto en tiempo de consulta, lo que es invaluable cuando se entrega un schema que debe ejecutarse en varios backends de base de datos. Dentro de cada cuerpo SQL, referencie columnas con <Column name="..."/> (o <Column table="..." name="..."/> para referencias cross-tabla); Mondrian las cualifica y entrecomilla apropiadamente para el dialecto.
Inline table
<InlineTable> le permite incrustar un pequeño conjunto de datos de búsqueda directamente en el archivo de schema — sin tabla de base de datos requerida. Declara los nombres y tipos de columna, luego lista las filas. Mondrian materializa esto como si fuera una tabla real:
<Dimension name="Severity"> <Hierarchy hasAll="true" primaryKey="severity_id"> <InlineTable alias="severity"> <ColumnDefs> <ColumnDef name="id" type="Numeric"/> <ColumnDef name="desc" type="String"/> </ColumnDefs> <Rows> <Row> <Value column="id">1</Value> <Value column="desc">High</Value> </Row> <Row> <Value column="id">2</Value> <Value column="desc">Medium</Value> </Row> <Row> <Value column="id">3</Value> <Value column="desc">Low</Value> </Row> </Rows> </InlineTable> <Level name="Severity" column="id" nameColumn="desc" uniqueMembers="true"/> </Hierarchy></Dimension>Esto se comporta idénticamente a tener una tabla severity en su base de datos con tres filas. Para representar un valor NULL para una celda, simplemente omita el elemento <Value> para esa columna.
(Mostrado solo en XML — <InlineTable> no es parte del formato YAML del schema. Cree conjuntos de datos de búsqueda en línea en XML, o proporcione las filas como una tabla real de base de datos.)
Query
Un elemento <Query> define una tabla virtual envolviendo una sentencia SQL — como una vista en línea. Le pone un nombre para que otros elementos del schema puedan referenciarlo, y puede suministrar SQL por dialecto:
<Query name="american_customers"> <ExpressionView> <SQL dialect="generic"> SELECT * FROM customer WHERE country = 'USA' </SQL> </ExpressionView></Query>La consulta puede entonces usarse en cualquier sitio donde se acepte un <Table>. Esto es útil para filtrar filas, pre-computar columnas, o unir varias tablas antes de que Mondrian las vea.
(Mostrado solo en XML. En Mondrian 4 una <Query> se identifica por un atributo alias — <Query alias="american_customers"> — en lugar de name. Escrita con alias, la consulta hace round-trip a través del formato YAML del schema como una entrada queries: llevando la expression por dialecto.)
Link (joins tabla a tabla)
Un elemento <Link> en el schema físico declara una relación de join dirigida entre dos tablas — el equivalente físico de una clave foránea. Mondrian usa los enlaces declarados para resolver automáticamente rutas de join cuando construye dimensiones copo de nieve.
Aquí está cómo definir el enlace entre una tabla emp y una tabla dept:
tables:- name: "emp" key_column: "empno"- name: "dept" key_column: "deptno"links:- source: "dept" target: "emp" foreign_key_column: "deptno"<Table name="emp" keyColumn="empno"/><Table name="dept" keyColumn="deptno"/><Link target="emp" source="dept" foreignKeyColumn="deptno"/>La tabla source lleva la clave foránea; target es la tabla a la que se está uniendo. También puede usar hijos <ForeignKey><Column .../></ForeignKey> para claves foráneas compuestas en lugar de la abreviatura foreignKeyColumn.
Importante: los elementos físicos <Link> se usan para rutas copo de nieve dentro de tablas de dimensión. Conectar la tabla de hechos de un grupo de medidas a sus tablas de dimensión requiere un elemento de enlace de dimensión explícito dentro de <DimensionLinks> — consulte Enlaces de dimensión en grupos de medidas abajo.
Enlaces de dimensión en grupos de medidas
Cada <MeasureGroup> declara cómo se relaciona con cada dimensión en el cubo mediante un bloque <DimensionLinks>. Mondrian 4 admite cinco tipos de enlace estándar, más un <BridgeLink> específico de Saiku para relaciones many-to-many — seis en total, cada uno con un propósito específico:
ForeignKeyLink
El join estándar: la tabla de hechos tiene una columna de clave foránea apuntando al atributo clave de la dimensión. Esto cubre la gran mayoría de los diseños de cubo en schema estrella.
- type: "foreign_key" dimension: "Store" foreign_key_column: "store_id"<ForeignKeyLink dimension="Store" foreignKeyColumn="store_id"/>Atributos:
| Atributo | Requerido | Descripción |
|---|---|---|
dimension | sí | Nombre de la dimensión a enlazar |
foreignKeyColumn | uno de estos | Columna FK única en la tabla de hechos |
<ForeignKey><Column/></ForeignKey> | o este | Elemento hijo para claves foráneas compuestas |
attribute | no | Nombre del atributo destino cuando la FK no apunta a la clave de la dimensión |
Ejemplo con una FK compuesta y un atributo destino explícito:
- type: "foreign_key" dimension: "Time" foreign_key: - "time_id" attribute: "Date"<ForeignKeyLink dimension="Time" attribute="Date"> <ForeignKey> <Column name="time_id"/> </ForeignKey></ForeignKeyLink>CopyLink
Usado solo en grupos de medidas agregados (type="aggregate"). La tabla agregada ya contiene columnas clave de dimensión preagregadas — no hay join necesario porque los datos de la dimensión se han copiado en la tabla agregada. Declara qué columnas de dimensión corresponden a qué columnas de la tabla agregada:
<CopyLink dimension="Time" attribute="Month"> <Column table="time_by_day" name="the_year" aggColumn="time_year"/> <Column table="time_by_day" name="quarter" aggColumn="quarter"/> <Column table="time_by_day" name="month_of_year" aggColumn="month_of_year"/></CopyLink>Cada hijo <Column> mapea una columna de dimensión (table + name) a su columna correspondiente en la tabla agregada (aggColumn). El atributo attribute en <CopyLink> es un no-op en Mondrian y no afecta al comportamiento de consulta.
(Mostrado solo en XML. Los mapeos de columna de un copy link sí hacen round-trip a través de YAML como una lista column_refs:, pero el attribute no-op es descartado por el conversor YAML — así que para evitar mostrar un gemelo YAML que silenciosamente perdió un atributo que el XML fuente todavía lleva, este bloque se deja como XML.)
NoLink
Declara explícitamente que este grupo de medidas no se relaciona con la dimensión nombrada. Mondrian devuelve NULL para las medidas en este grupo cuando una consulta filtra por la dimensión no enlazada. Usar <NoLink> se recomienda (en lugar de omitir la entrada) cuando el atributo de schema missingLink está definido en warning — que es el por defecto.
- type: "no_link" dimension: "Warehouse"<NoLink dimension="Warehouse"/>| Atributo | Requerido | Descripción |
|---|---|---|
dimension | sí | Nombre de la dimensión que no tiene enlace |
FactLink
Declara que la tabla de dimensión y la tabla de hechos son la misma tabla física — lo que a veces se llama una dimensión degenerada. No se genera ningún join; las columnas de dimensión se leen directamente de las filas de la tabla de hechos.
- type: "fact" dimension: "Store Type"<FactLink dimension="Store Type"/>| Atributo | Requerido | Descripción |
|---|---|---|
dimension | sí | Nombre de la dimensión degenerada |
Esto es común para dimensiones como “Has coffee bar” o “Payment method” que se almacenan como columnas en la tabla de hechos en lugar de en una búsqueda separada.
ReferenceLink
Declara que la dimensión se alcanza indirectamente a través del atributo de otra dimensión — una ruta puente o copo de nieve que no toca la tabla de hechos directamente. La FK une a la clave de un atributo especificado de una dimensión intermedia especificada, no a la tabla de hechos.
- type: "reference" dimension: "Store" via_dimension: "Employee" via_attribute: "Store Id"<ReferenceLink dimension="Store" viaDimension="Employee" viaAttribute="Store Id"/>| Atributo | Requerido | Descripción |
|---|---|---|
dimension | sí | La dimensión que se enlaza indirectamente |
viaDimension | no | La dimensión intermedia cuyo atributo actúa como puente |
viaAttribute | no | El atributo en viaDimension que contiene la clave de join |
Esto aparece en el cubo HR de FoodMart, donde la dimensión Store se alcanza a través del atributo Store Id de la dimensión Employee en lugar de a través de una FK directa en la tabla de hechos salary.
BridgeLink
Una extensión de Saiku a Mondrian 4. Enlaza una dimensión many-to-many a través de una tabla puente separada, para que una sola fila de hechos pueda pertenecer a varios miembros de dimensión a la vez (una cuenta conjunta propiedad de dos clientes, un ticket con varias etiquetas). Saiku resuelve el join de fan-out de forma segura — fullCount deduplica el total general mediante un agregado simétrico, weighted divide cada valor por una columna de asignación.
dimension_links:- type: "bridge" dimension: "Customer" bridge_table: "account_owner" fact_foreign_key_column: "account_id" bridge_fact_key_column: "account_id" bridge_dimension_key_column: "customer_id" aggregation: "weighted" weight_column: "weight"<BridgeLink dimension="Customer" bridgeTable="account_owner" factForeignKeyColumn="account_id" bridgeFactKeyColumn="account_id" bridgeDimensionKeyColumn="customer_id" aggregation="weighted" weightColumn="weight"/>| Atributo | Requerido | Descripción |
|---|---|---|
dimension | sí | La dimensión many-to-many que se enlaza |
bridgeTable | sí | Tabla física que mapea filas de hechos a miembros de dimensión |
factForeignKeyColumn | sí | Columna clave a granularidad de hecho en la tabla de hechos |
bridgeFactKeyColumn | sí | Columna del puente que coincide con factForeignKeyColumn |
bridgeDimensionKeyColumn | sí | Columna del puente que coincide con la clave de la dimensión |
aggregation | no | fullCount (por defecto) o weighted |
weightColumn | no | Columna de peso de asignación; requerida para weighted |
La tabla de hechos debe declarar su granularidad como una <Key>, y las consultas con puente requieren el backend Calcite. Consulte Dimensiones puente (many-to-many) para el tratamiento completo, la semántica de asignación, y un ejemplo trabajado.
Hints de tabla
Mondrian admite un pequeño conjunto de hints de optimizador específicos de base de datos en los elementos <Table>. Estos se pasan a las consultas SQL generadas:
| Base de datos | Tipo de hint | Valores permitidos | Efecto |
|---|---|---|---|
| MySQL | force_index | Nombre de un índice en la tabla | Fuerza el índice nombrado al seleccionar valores de nivel |
<Table name="automotive_dim"> <Hint type="force_index">my_index</Hint></Table>(Mostrado solo en XML — <Hint> no es parte del formato YAML del schema. Exprese los hints del optimizador en XML cuando los necesite.)
Los hints son opcionales y no portables. Solo úselos cuando el perfilado muestra un problema de plan específico que el hint arregla.
Schemas estrella y copo de nieve
El diseño de cubo más simple — una tabla de hechos unida a varias tablas de dimensión — se llama un schema estrella. Cada tabla de dimensión se une directamente a la tabla de hechos, y las conecta con entradas <ForeignKeyLink> en el grupo de medidas.
Un schema copo de nieve extiende esto permitiendo que una dimensión abarque varias tablas. En lugar de una sola tabla de dimensión, tiene una cadena: la tabla de hechos se une a la primera tabla de dimensión, que se une a una segunda, y así sucesivamente. En Mondrian 4 modela esto declarando elementos físicos <Link> entre las tablas de dimensión en <PhysicalSchema>, y Mondrian resuelve la ruta de join automáticamente. Las tablas de dimensión copo de nieve se referencian desde elementos <Attribute> usando su atributo table.
Por ejemplo, una dimensión Product que abarca las tablas product y product_class requiere:
physical_schema: tables: - name: "product" key_column: "product_id" - name: "product_class" key_column: "product_class_id" links: - source: "product" target: "product_class" foreign_key_column: "product_class_id"shared_dimensions: Product: table: "product" key: "Product Id" attributes: - name: "Product Id" table: "product" key_column: "product_id" has_hierarchy: false - name: "Product Name" table: "product" key_column: "product_id" - name: "Product Category" table: "product_class" key_column: "product_class_id"<!-- Physical schema links --><Table name="product" keyColumn="product_id"/><Table name="product_class" keyColumn="product_class_id"/><Link target="product_class" source="product" foreignKeyColumn="product_class_id"/>
<!-- Dimension attributes referencing both tables --><Dimension name="Product" table="product" key="Product Id"> <Attributes> <Attribute name="Product Id" table="product" keyColumn="product_id"/> <Attribute name="Product Name" table="product" keyColumn="product_id"/> <Attribute name="Product Category" table="product_class" keyColumn="product_class_id"/> </Attributes></Dimension>Mondrian camina el grafo físico <Link> para construir la cadena correcta de SQL JOIN. No necesita un elemento <Join> dentro de la definición de la dimensión — ese era un patrón de Mondrian 3. Consulte Dimensiones para el modelo completo de dimensión.
Dimensiones compartidas
Una dimensión compartida se declara a nivel de schema (fuera de cualquier cubo) y la reutilizan varios cubos. Como no tiene clave foránea fija, el enlace se establece por cubo dentro del <DimensionLinks> de cada grupo de medidas.
En Mondrian 4, una dimensión compartida aparece en el bloque <Dimensions> de un cubo con un atributo source:
shared_dimensions: Store: table: "store" key: "Store Id" attributes: - name: "Store Id" key_column: "store_id" has_hierarchy: false - name: "Store Country" key_column: "store_country" has_hierarchy: false - name: "Store State" key_column: "store_state" has_hierarchy: false - name: "Store City" key_column: "store_city" has_hierarchy: false - name: "Store Name" key_column: "store_name"cubes: Sales: dimensions: - source: "Store" measure_groups: - name: "Sales" table: "sales_fact_1997" dimension_links: - type: "foreign_key" dimension: "Store" foreign_key_column: "store_id" Warehouse: dimensions: - source: "Store" measure_groups: - name: "Warehouse" table: "inventory_fact_1997" dimension_links: - type: "foreign_key" dimension: "Store" foreign_key_column: "warehouse_store_id"<!-- Shared dimension declared at schema level --><Dimension name="Store" table="store" key="Store Id"> <Attributes> <Attribute name="Store Id" keyColumn="store_id"/> <Attribute name="Store Country" keyColumn="store_country" hasHierarchy="false"/> <Attribute name="Store State" keyColumn="store_state" hasHierarchy="false"/> <Attribute name="Store City" keyColumn="store_city" hasHierarchy="false"/> <Attribute name="Store Name" keyColumn="store_name"/> </Attributes></Dimension>
<!-- Cube 1 — Sales --><Cube name="Sales"> <Dimensions> <Dimension source="Store"/> <!-- reuse shared dimension --> </Dimensions> <MeasureGroups> <MeasureGroup name="Sales" table="sales_fact_1997"> <DimensionLinks> <ForeignKeyLink dimension="Store" foreignKeyColumn="store_id"/> </DimensionLinks> </MeasureGroup> </MeasureGroups></Cube>
<!-- Cube 2 — Warehouse --><Cube name="Warehouse"> <Dimensions> <Dimension source="Store"/> <!-- same shared dimension, different FK --> </Dimensions> <MeasureGroups> <MeasureGroup name="Warehouse" table="inventory_fact_1997"> <DimensionLinks> <ForeignKeyLink dimension="Store" foreignKeyColumn="warehouse_store_id"/> </DimensionLinks> </MeasureGroup> </MeasureGroups></Cube>La referencia source="Store" trae las definiciones completas de atributo y jerarquía de la dimensión compartida. La columna FK que la conecta a la tabla de hechos se declara en el <ForeignKeyLink>, no en la dimensión en sí.
Nota Mondrian 3: M3 usaba
<DimensionUsage source="..." foreignKey="..."/>para referenciar dimensiones compartidas. En Mondrian 4 esto se reemplaza por<Dimension source="..."/>en el bloque<Dimensions>del cubo y un<ForeignKeyLink>en el grupo de medidas. Consulte Dimensiones para el modelo completo de dimensión basado en atributos.
Adaptado de la guía del schema del proyecto Mondrian (EPL v1.0).