Zum Inhalt springen

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"

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"

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. int bewirkt ResultSet.getInt()-Aufrufe und int-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.)

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"

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.

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:

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"

Attribute:

AttributErforderlichBeschreibung
dimensionjaName der zu verknüpfenden Dimension
foreignKeyColumneines davonEinzel-FK-Spalte in der Faktentabelle
<ForeignKey><Column/></ForeignKey>oder dasKind-Element für zusammengesetzte Fremdschlüssel
attributeneinZiel-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"

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.)

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"
AttributErforderlichBeschreibung
dimensionjaName der Dimension ohne Link

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"
AttributErforderlichBeschreibung
dimensionjaName 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.

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"
AttributErforderlichBeschreibung
dimensionjaDie indirekt verknüpfte Dimension
viaDimensionneinDie Zwischendimension, deren Attribut als Bridge fungiert
viaAttributeneinDas 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.

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"
AttributErforderlichBeschreibung
dimensionjaDie zu verknüpfende Many-to-Many-Dimension
bridgeTablejaPhysische Tabelle, die Fact-Zeilen auf Dimension-Members mappt
factForeignKeyColumnjaFact-Granularitäts-Schlüsselspalte auf der Faktentabelle
bridgeFactKeyColumnjaBridge-Spalte, die factForeignKeyColumn entspricht
bridgeDimensionKeyColumnjaBridge-Spalte, die dem Dimensionsschlüssel entspricht
aggregationneinfullCount (Standard) oder weighted
weightColumnneinAllokations-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:

DatenbankHint-TypErlaubte WerteWirkung
MySQLforce_indexName eines Index auf der TabelleErzwingt 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"

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"

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).