Physisches Schema
Das physische Schema ist der Ort, an dem Sie Mondrian über die Datenbankobjekte unter Ihren Cubes informieren: welche Tabellen existieren, wie ihre Spalten typisiert sind, wie Tabellen miteinander in Beziehung stehen und welche Joins sicher automatisch traversiert werden können. Alles in <PhysicalSchema> ist reine strukturelle Beschreibung – noch keine analytische Semantik, nur die Infrastruktur.
Tabelle
Eine Tabelle ist eine benannte Nutzung einer Datenbanktabelle. Sie deklarieren sie mit einem <Table>-Element innerhalb von <PhysicalSchema>. Das einzige erforderliche Attribut ist name; wenn die Tabelle in einem anderen Datenbankschema als dem Standard liegt, fügen Sie das schema-Attribut hinzu:
- name: "sales_fact_1997" schema: "Foodmart"<Table schema="Foodmart" name="sales_fact_1997"/>Primärschlüssel
Mondrian muss den Primärschlüssel jeder Dimensionstabelle kennen, um korrekt joinen zu können. Sie können ihn inline auf dem Element selbst mit keyColumn (Einzelspalte) oder mit einem verschachtelten <Key>-Element (zusammengesetzter Schlüssel) deklarieren:
# 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>Faktentabellen benötigen keine <Key>-Deklaration.
Spalten und berechnete Spalten
Innerhalb einer <Table> können Sie optional einen <ColumnDefs>-Abschnitt definieren. Wenn Sie ihn weglassen, liest Mondrian die Spaltendefinitionen aus JDBC – was für die meisten Situationen ausreichend ist. Wenn Sie präzise Typisierung oder berechnete Spalten benötigen, deklarieren Sie sie explizit:
<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>(Dieses Beispiel wird nur in XML gezeigt: Das YAML-Schemaformat trägt
berechnete Spalten, aber keine reine <ColumnDef>-Typisierungs-Metadaten,
und kodiert SQL für berechnete Spalten als Per-Dialekt-String statt
in der verschachtelten <Column>-Element-Form oben. Eine berechnete
Spalte, deren Körper reines SQL ist – siehe Berechnete Spalten in
Measures –
durchläuft YAML treu im Round-Trip.)
<ColumnDef> deklariert, dass eine physische Spalte existiert und wie sie zu interpretieren ist:
type– der Mondrian-Datentyp (String,Integer,Numeric,Boolean,Date,Time,Timestamp). Dies steuert die Sortierreihenfolge und den MDX-Typ von Ausdrücken, die aus der Spalte gebildet werden.internalType– der Java-Typ, den Mondrian verwendet, um Werte im Speicher abzulegen (z. B.intbewirktResultSet.getInt()-Aufrufe undint-Speicherung). Sparsam verwenden; der Standard ist meist korrekt.
<CalculatedColumnDef> definiert eine virtuelle Spalte als SQL-Ausdruck. Sie können Per-Dialekt-SQL-Körper liefern – Mondrian wählt zur Abfragezeit den richtigen, was unbezahlbar ist beim Versand eines Schemas, das auf mehreren Datenbank-Backends laufen muss. Innerhalb jedes SQL-Körpers referenzieren Sie Spalten mit <Column name="..."/> (oder <Column table="..." name="..."/> für Cross-Table-Referenzen); Mondrian qualifiziert und quotet sie entsprechend für den Dialekt.
Inline-Tabelle
<InlineTable> erlaubt es, einen kleinen Lookup-Datensatz direkt in der Schema-Datei einzubetten – ohne Datenbanktabelle. Sie deklarieren die Spaltennamen und Typen und listen dann die Zeilen auf. Mondrian materialisiert dies, als wäre es eine echte Tabelle:
<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>Das verhält sich identisch zu einer severity-Tabelle in Ihrer Datenbank mit drei Zeilen. Um einen NULL-Wert für eine Zelle darzustellen, lassen Sie einfach das <Value>-Element für diese Spalte weg.
(Nur in XML gezeigt – <InlineTable> ist nicht Teil des
YAML-Schemaformats. Verfassen Sie Inline-Lookup-Datensätze in XML oder
liefern Sie die Zeilen als echte Datenbanktabelle.)
Query
Ein <Query>-Element definiert eine virtuelle Tabelle, indem es eine SQL-Anweisung umschließt – wie eine Inline-View. Sie geben ihr einen Namen, sodass andere Schema-Elemente sie referenzieren können, und können Per-Dialekt-SQL bereitstellen:
<Query name="american_customers"> <ExpressionView> <SQL dialect="generic"> SELECT * FROM customer WHERE country = 'USA' </SQL> </ExpressionView></Query>Die Query kann dann überall verwendet werden, wo eine <Table> akzeptiert wird. Dies ist nützlich, um Zeilen auszufiltern, Spalten vorzuberechnen oder mehrere Tabellen zu vereinigen, bevor Mondrian sie sieht.
(Nur in XML gezeigt. In Mondrian 4 wird eine <Query> durch ein
alias-Attribut identifiziert – <Query alias="american_customers"> –
statt durch name. Mit alias geschrieben, durchläuft die Query das
YAML-Schemaformat treu als queries:-Eintrag, der die Per-Dialekt-
expression trägt.)
Link (Tabelle-zu-Tabelle-Joins)
Ein <Link>-Element im physischen Schema deklariert eine gerichtete Join-Beziehung zwischen zwei Tabellen – das physische Äquivalent eines Fremdschlüssels. Mondrian verwendet deklarierte Links, um Join-Pfade automatisch aufzulösen, wenn Sie Schneeflocken-Dimensionen bauen.
So definieren Sie den Link zwischen einer emp-Tabelle und einer dept-Tabelle:
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"/>Die source-Tabelle enthält den Fremdschlüssel; target ist die Tabelle, mit der gejoint wird. Sie können auch <ForeignKey><Column .../></ForeignKey>-Kinder für zusammengesetzte Fremdschlüssel anstelle der foreignKeyColumn-Kurzform verwenden.
Wichtig: Physische <Link>-Elemente werden für Schneeflocken-Pfade innerhalb von Dimensionstabellen verwendet. Das Verbinden der Faktentabelle einer Measure-Group mit ihren Dimensionstabellen erfordert ein explizites Dimension-Link-Element innerhalb von <DimensionLinks> – siehe Dimension-Links in Measure-Groups unten.
Dimension-Links in Measure-Groups
Jede <MeasureGroup> deklariert, wie sie sich auf jede Dimension im Cube über einen <DimensionLinks>-Block bezieht. Mondrian 4 unterstützt fünf Standard-Link-Typen plus einen Saiku-spezifischen <BridgeLink> für Many-to-Many-Beziehungen – insgesamt sechs, jeder mit einem spezifischen Zweck:
ForeignKeyLink
Der Standard-Join: Die Faktentabelle hat eine Fremdschlüsselspalte, die auf das Schlüsselattribut der Dimension zeigt. Dies deckt die überwältigende Mehrheit der Sternschema-Cube-Designs ab.
- type: "foreign_key" dimension: "Store" foreign_key_column: "store_id"<ForeignKeyLink dimension="Store" foreignKeyColumn="store_id"/>Attribute:
| Attribut | Erforderlich | Beschreibung |
|---|---|---|
dimension | ja | Name der zu verknüpfenden Dimension |
foreignKeyColumn | eines davon | Einzel-FK-Spalte in der Faktentabelle |
<ForeignKey><Column/></ForeignKey> | oder das | Kind-Element für zusammengesetzte Fremdschlüssel |
attribute | nein | Ziel-Attributname, wenn der FK nicht auf den Dimensionsschlüssel zeigt |
Beispiel mit zusammengesetztem FK und einem expliziten Ziel-Attribut:
- type: "foreign_key" dimension: "Time" foreign_key: - "time_id" attribute: "Date"<ForeignKeyLink dimension="Time" attribute="Date"> <ForeignKey> <Column name="time_id"/> </ForeignKey></ForeignKeyLink>CopyLink
Nur in aggregierten Measure-Groups verwendet (type="aggregate"). Die Aggregat-Tabelle enthält bereits vor-aggregierte Dimensionsschlüssel-Spalten – es wird kein Join benötigt, da die Dimensionsdaten in die Aggregat-Tabelle kopiert wurden. Sie deklarieren, welche Dimensionsspalten welchen Aggregat-Tabellenspalten entsprechen:
<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>Jedes <Column>-Kind mappt eine Dimensionsspalte (table + name) auf ihre Gegenstückspalte in der Aggregat-Tabelle (aggColumn). Das attribute-Attribut auf <CopyLink> ist ein No-op in Mondrian und beeinflusst das Abfrageverhalten nicht.
(Nur in XML gezeigt. Die Spalten-Mappings eines Copy-Links durchlaufen
YAML als column_refs:-Liste im Round-Trip, aber das No-op
attribute wird vom YAML-Konverter verworfen – um nicht einen
YAML-Zwilling zu zeigen, der stillschweigend ein Attribut verloren hat,
das die Quell-XML noch trägt, wird dieser Block als XML belassen.)
NoLink
Deklariert explizit, dass diese Measure-Group nicht in Beziehung zur genannten Dimension steht. Mondrian gibt NULL für Measures in dieser Gruppe zurück, wenn eine Abfrage nach der nicht verknüpften Dimension filtert. Die Verwendung von <NoLink> ist empfohlen (statt den Eintrag wegzulassen), wenn das Schema-Attribut missingLink auf warning gesetzt ist – was der Standard ist.
- type: "no_link" dimension: "Warehouse"<NoLink dimension="Warehouse"/>| Attribut | Erforderlich | Beschreibung |
|---|---|---|
dimension | ja | Name der Dimension ohne Link |
FactLink
Deklariert, dass Dimensionstabelle und Faktentabelle dieselbe physische Tabelle sind – was manchmal als degenerierte Dimension bezeichnet wird. Es wird kein Join erzeugt; die Dimensionsspalten werden direkt aus den Fact-Zeilen gelesen.
- type: "fact" dimension: "Store Type"<FactLink dimension="Store Type"/>| Attribut | Erforderlich | Beschreibung |
|---|---|---|
dimension | ja | Name der degenerierten Dimension |
Das ist üblich für Dimensionen wie “Has coffee bar” oder “Payment method”, die als Spalten auf der Faktentabelle gespeichert werden statt in einem separaten Lookup.
ReferenceLink
Deklariert, dass die Dimension indirekt über das Attribut einer anderen Dimension erreicht wird – ein Bridge- oder Schneeflockenpfad, der die Faktentabelle nicht direkt berührt. Der FK joint auf den Schlüssel eines angegebenen Attributs einer angegebenen Zwischendimension, nicht auf die Faktentabelle.
- type: "reference" dimension: "Store" via_dimension: "Employee" via_attribute: "Store Id"<ReferenceLink dimension="Store" viaDimension="Employee" viaAttribute="Store Id"/>| Attribut | Erforderlich | Beschreibung |
|---|---|---|
dimension | ja | Die indirekt verknüpfte Dimension |
viaDimension | nein | Die Zwischendimension, deren Attribut als Bridge fungiert |
viaAttribute | nein | Das Attribut auf viaDimension, das den Join-Schlüssel enthält |
Das erscheint im FoodMart-HR-Cube, wo die Store-Dimension über das Store Id-Attribut der Employee-Dimension erreicht wird statt über einen direkten FK auf der Salary-Faktentabelle.
BridgeLink
Eine Saiku-Erweiterung zu Mondrian 4. Verknüpft eine Many-to-Many-Dimension über eine separate Bridge-Tabelle, sodass eine einzelne Fact-Zeile gleichzeitig zu mehreren Dimension-Members gehören kann (ein gemeinsames Konto im Besitz von zwei Kunden, ein Ticket mit mehreren Tags). Saiku löst den Fan-Out-Join sicher auf – fullCount dedupliziert die Gesamtsumme über ein symmetrisches Aggregat, weighted teilt jeden Wert nach einer Allokationsspalte.
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"/>| Attribut | Erforderlich | Beschreibung |
|---|---|---|
dimension | ja | Die zu verknüpfende Many-to-Many-Dimension |
bridgeTable | ja | Physische Tabelle, die Fact-Zeilen auf Dimension-Members mappt |
factForeignKeyColumn | ja | Fact-Granularitäts-Schlüsselspalte auf der Faktentabelle |
bridgeFactKeyColumn | ja | Bridge-Spalte, die factForeignKeyColumn entspricht |
bridgeDimensionKeyColumn | ja | Bridge-Spalte, die dem Dimensionsschlüssel entspricht |
aggregation | nein | fullCount (Standard) oder weighted |
weightColumn | nein | Allokations-Gewichtsspalte; erforderlich für weighted |
Die Faktentabelle muss ihre Granularität als <Key> deklarieren, und Bridge-Abfragen erfordern das Calcite-Backend. Siehe Bridge-Dimensionen (Many-to-Many) für die vollständige Behandlung, Allokationssemantik und ein durchgearbeitetes Beispiel.
Tabellen-Hints
Mondrian unterstützt eine kleine Menge datenbankspezifischer Optimierer-Hints auf <Table>-Elementen. Diese werden in generiertes SQL durchgereicht:
| Datenbank | Hint-Typ | Erlaubte Werte | Wirkung |
|---|---|---|---|
| MySQL | force_index | Name eines Index auf der Tabelle | Erzwingt den benannten Index bei der Auswahl von Level-Werten |
<Table name="automotive_dim"> <Hint type="force_index">my_index</Hint></Table>(Nur in XML gezeigt – <Hint> ist nicht Teil des YAML-Schemaformats.
Drücken Sie Optimierer-Hints in XML aus, wenn Sie sie benötigen.)
Hints sind optional und nicht portierbar. Verwenden Sie sie nur, wenn Profiling ein spezifisches Plan-Problem zeigt, das der Hint behebt.
Stern- und Schneeflockenschemas
Das einfachste Cube-Layout – eine Faktentabelle, die mit mehreren Dimensionstabellen verbunden ist – heißt Sternschema. Jede Dimensionstabelle verbindet sich direkt mit der Faktentabelle, und Sie verbinden sie mit <ForeignKeyLink>-Einträgen in der Measure-Group.
Ein Schneeflockenschema erweitert dies, indem es einer Dimension erlaubt, sich über mehrere Tabellen zu erstrecken. Statt einer einzelnen Dimensionstabelle haben Sie eine Kette: Die Faktentabelle verbindet sich mit der ersten Dimensionstabelle, die sich mit einer zweiten verbindet und so weiter. In Mondrian 4 modellieren Sie dies, indem Sie physische <Link>-Elemente zwischen den Dimensionstabellen in <PhysicalSchema> deklarieren, und Mondrian löst den Join-Pfad automatisch auf. Schneeflocken-Dimensionstabellen werden von <Attribute>-Elementen über ihr table-Attribut referenziert.
Zum Beispiel erfordert eine Produkt-Dimension, die sich über die Tabellen product und product_class erstreckt:
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 durchläuft den physischen <Link>-Graphen, um die korrekte SQL-JOIN-Kette zu bauen. Sie brauchen kein <Join>-Element innerhalb der Dimensionsdefinition – das war ein Mondrian-3-Muster. Siehe Dimensionen für das vollständige Dimensionsmodell.
Geteilte Dimensionen
Eine geteilte Dimension wird auf Schema-Ebene (außerhalb eines Cubes) deklariert und von mehreren Cubes wiederverwendet. Da sie keinen festen Fremdschlüssel hat, wird der Link pro Cube innerhalb der <DimensionLinks> jeder Measure-Group hergestellt.
In Mondrian 4 erscheint eine geteilte Dimension im <Dimensions>-Block eines Cubes mit einem source-Attribut:
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>Die Referenz source="Store" zieht die vollständigen Attribut- und Hierarchiedefinitionen der geteilten Dimension herein. Die FK-Spalte, die sie mit der Faktentabelle verbindet, wird auf dem <ForeignKeyLink> deklariert, nicht auf der Dimension selbst.
Mondrian-3-Hinweis: M3 verwendete
<DimensionUsage source="..." foreignKey="..."/>, um geteilte Dimensionen zu referenzieren. In Mondrian 4 wird dies durch<Dimension source="..."/>im<Dimensions>-Block des Cubes und einen<ForeignKeyLink>in der Measure-Group ersetzt. Siehe Dimensionen für das vollständige attributbasierte Dimensionsmodell.
Adaptiert aus dem Mondrian-Projekt-Schema-Guide (EPL v1.0).