Joins
Für die explizite Konfiguration von Joins stehen folgende Typen zur Verfügung:
►HINWEISE◄
Typischerweise werden Joins in der Konfiguration einer Suche (bzw. einer Sub-Suche oder Subselect Projektion) auf übergeordneter Ebene angelegt, um den dabei festgelegten Alias-Namen in der weiteren Konfiguration der Suche zu nutzen.
Bei Bedarf können Joins auch genutzt werden, um eine Verkettete Projektion (Beispiele siehe dort) aufzubauen. Dabei gelten die verwendeten Alias-Namen nicht außerhalb der Projektion.
Auch im Kontext von Datengrid-Einstellungen können Joins eingesetzt werden, um zusätzliche Daten in die Tupel-Suche für eine Übersicht einzubinden. Dafür steht im Kontext der Spaltenkonfiguration ein Tab-Reiter "Joins" zur Verfügung. Die auf diesem Weg konfigurierten Joins stehen zwar formal in Beziehung zu einer bestimmten Spalte des Datengrids, sie können über den zugewiesenen Alias-Namen allerdings im Kontext der gesamten Datengrid-Definition adressiert werden. Umgekehrt ist darauf zu achten, dass nicht für denselben Alias-Namen unterschiedliche Definitionen in unterschiedlichen Spalten vorliegen.
Hintergrund
Alle Sucharten in Lobster Data Platform / Orchestration erfordern die Auswahl eines Entitätstyps (im Feld "Entität"), der die primäre Datenquelle für die "Suche" festlegt.
Die Auswahl der Entität bestimmt, auf welche Datenbanktabelle sich die für die Suche erzeugte Datenbankabfrage im FROM-Abschnitt der SELECT-Anweisung unmittelbar bezieht.
Der FROM-Abschnitt einer SELECT-Anweisung kann außerdem mehrere JOIN-Abschnitte enthalten, um die Ergebnisse von weiteren Tabellenzugriffen in die Suche einzubinden.
Innerhalb jedes JOIN-Abschnitts regelt dabei die mehr oder weniger komplexe ON-Bedingung, welche Kriterien erfüllt sein müssen, damit Datensätze aus der JOIN-Tabelle als mit einem bestimmten Datensatz aus der FROM-Tabelle "in Beziehung stehend" betrachtet werden.
Schematisches Beispiel
Eine Tabelle CANDIDATES bewertet in der Spalte LEVEL die Kenntnisse von in der Spalte NAME identifizierten Kandidaten in Bezug auf die in der Spalte LANGUAGE benannte Sprache:
|
Eine weitere Tabelle
TASKS
definiert für per Spalte
TASK
benannte Aufgaben die jeweils erforderlichen "Englischkenntnisse" (EN_LEVEL):
|
Die folgende leicht vereinfachte SELECT-Anweisung zielt darauf ab, eine Schnittmenge zwischen TASKS und geeigneten CANDIDATES zu bilden:
SELECT TASK, NAME FROM TASKS INNER JOIN CANDIDATES ON ((LANGUAGE = 'en') AND (LEVEL >= EN_LEVEL))
Ergebnis:
|
Die SELECT-Anweisung erstellt sinngemäß ein "Kreuzprodukt" TASKS x CANDIDATES und liefert als "Treffer" (=Ergebniszeilen) nur diejenigen Kombinationen von TASK (aus TASKS) und NAME (aus CANDIDATES), für die ausreichende Englischkenntnisse vorliegen (ON-Bedingung). Hier wird ausdrücklich ein INNER JOIN verwendet, weil als Ergebnis exakt die durch die ON-Bedingung definierte Schnittmenge aus CANDIDATES und TASKS erwünscht ist.
|
Implizit generierte Joins
Beim Ausführen einer "Suche", wird ausgehend von der Konfiguration der Suche mindestens eine SELECT-Anweisung erzeugt und - sofern dies fehlerfrei gelingt - an die Datenbank übergeben.
Häufig erstreckt sich die Datenhaltung für einen Entitätstyp über mehrere Datenbanktabellen, deren Beziehung abhängig vom Datenmodell für die Entität dann auch datenbankseitig mehr oder weniger streng "geregelt" ist.
Wenn die Beziehung zwischen einer Entität und den in einer "Detailtabelle" verwalteten Daten hinreichend verbindlich ist, dann werden die betreffenden Relationen beim Zugriff auf Detailfelder in Projektionen automatisch "aufgelöst", ohne dass dafür explizit Joins konfiguriert werden müssen.
►HINWEIS◄ Implizieren unterschiedliche Projektionen exakt denselben Join, dann wir dieser datenbankseitig in der Regel nicht redundant ausgeführt, sondern innerhalb der erzeugten SELECT-Anweisung gemeinsam genutzt.
|
In einer Tupel-Suche für Benutzer greift eine Feldprojektion auf das Adressfeld "Name" (address.name1) zu. Diese Suche liefert ohne weitere Vorkehrungen das gewünschte Ergebnis, obwohl nur der "Benutzername" (username) direkt in der Datenbanktabelle für Benutzer ( base_user ) zu finden ist. Der Zugriff auf die "Adresse" erfordert einen LEFT JOIN auf die Datenbanktabelle base_address , die alle im System bekannten Adressen enthält. Dieser LEFT JOIN inklusive der für den Kontext zielführenden ON-Bedingung automatisch generiert, wenn die Datenbankabfrage ausgeführt wird. |
Sinngemäß wird für diese einfache Tupel-Suche folgende SELECT-Anweisung erzeugt: SELECT username, name1 FROM base_user LEFT JOIN base_address ON (id = address_id)
|
|
In einer Tupel-Suche für Benutzer greift eine Feldprojektion auf das Listenfeld "Rollen" (roles) zu, das mehrere ID-Werte von Rollen als Wert enthalten kann. Diese Suche liefert ohne weitere Vorkehrungen das gewünschte Ergebnis, obwohl nur der "Benutzername" (username) direkt in der Datenbanktabelle für Benutzer ( base_user ) zu finden ist. Damit demselben Benutzer mehrere Rollen-IDs zugeordnet werden können, wird eine spezifische Detailtabelle ( base_user_roles ) benötigt, in der unterschiedliche Rollen-IDs ( role_id ) mit demselben Fremdschlüssel ( user_id ) auf die Benutzer-Tabelle kombiniert werden können. Der Zugriff auf die "Rollen" eines Benutzers erfordert wieder einen LEFT JOIN. |
Sinngemäß wird für diese einfache Tupel-Suche folgende SELECT-Anweisung erzeugt: SELECT username, role_id FROM base_user LEFT JOIN base_user_roles ON (id = user_id) ►HINWEIS◄ Jede Rollen-ID könnte ihrerseits als Fremdschlüssel für einen Datensatz in der Tabelle für Rollen (base_role) interpretiert werden, sodass eigentlich eine (m : n)-Relation vorliegt (Benutzer ←(m : n) → Rollen). Allerdings kann diese hier nicht etwa durch eine Feldprojektion wie roles.roleName erzwungen werden, weil Rollen im Datenmodell für Benutzer nur referenziert sind und nicht eingebettet oder enthalten. |
Beispiel: Benutzer ←1 : 1→ Adressen ~ Typisiertes-Attribut-Projektion |
|
|
In einer Tupel-Suche für Benutzer greift eine Typisiertes-Attribut-Projektion auf das Kennzeichenattribut für die Adresse mit dem (Sub-)Typ VIP zu, um dessen "Kennzeichenwert" (flagValue) zurückzugeben. Diese Suche liefert ohne weitere Vorkehrungen das gewünschte Ergebnis, obwohl nur der "Benutzername" (username) direkt in der Datenbanktabelle für Benutzer ( base_user ) zu finden ist. Wie im ersten Beispiel oben ausgeführt, impliziert bereits der Zugriff auf ein Feld der Adresse einen LEFT JOIN auf die Tabelle base_address. Wie im Screenshot links zu sehen, bezieht sich unsere Typisiertes-Attribut-Projektion ausdrücklich auf den Attributbesitzerpfad address. Per Standard werden Kennzeichenattribute für Benutzer auch überhaupt nicht unterstützt. Datenbankseitig erfordert die Implementierung eines Attributtyps für einen Besitzer eine eigene Datenbanktabelle, in der die Attributfelder für diese Besitzer-Attributtyp-Kombination gespeichert werden. Der Zugriff auf Attributwerte erfordert daher auch immer mindestens einen impliziten Join. Die Projektionen für Attribute vereinfachen den Zugriff nur an der Oberfläche. Die Notwendigkeit in den spezifischen Detailtabellen nachzuschlagen entfällt datenbankseitig dadurch aber nicht. ►HINWEIS◄ In speziellen Fällen kann es notwendig sein, auf den Komfort der Projektionen für Attribute zu verzichten und stattdessen explizite Joins für den Zugriff auf Attributwerte einzurichten. |
Sinngemäß wird für diese einfache Tupel-Suche folgende SELECT-Anweisung erzeugt: SELECT username, flag_value FROM
base_user
|
|
►ANMERKUNG◄ In ON-Bedingung für den zweiten LEFT JOIN wird als Vergleichswert für die address_id -Spalte in der rot gekennzeichneten Attribut-Tabelle ( base_address_flag_attribute ) die id aus der grün gekennzeichneten Adress-Tabelle (base_address) herangezogen. Wie in der ON-Bedingung für den ersten LEFT JOIN ersichtlich ist, muss diese allerdings mit dem Fremdschlüssel address_id in der blau gekennzeichneten Benutzer-Tabelle (base_user) übereinstimmten. Man könnte also auf den ersten LEFT JOIN zur Adresse komplett verzichten, wenn man den Fremdschlüssel address_id direkt mit der address_id -Spalte des Attributs vergleichen würde. Diese Abkürzung kann man nur in einem expliziten Join erzwingen. Der implizite Join wird immer dem intern vordefinierten Schema (Entität → Attributbesitzerpfad → Attribut) folgen, wenn ein von der Entität im FROM-Abschnitt abweichender Attributbesitzerpfad angegeben ist. |
Explizit definierte Joins
Durch die explizite Definition von Joins können zusätzliche Datenquellen in eine Suche einbezogen werden, damit diese für die Definition von Projektionen zur Verfügung stehen.
Alle Sucharten können Projektionen auf Joins zur Definition von Einschränkungen verwenden.
►HINWEIS◄ Ein INNER JOIN kann die Suche ggf. auch einschränken, ohne dass Einschränkungen auf der Hauptebene der Suche vorliegen.In einer Tupel-Suche oder CSV Suche können über Joins zugeordnete Daten als Projektionen für Ausgabespalten und Gruppierungen dienen.
In Datengrid-Einstellungen können Daten aus Joins außerdem Restriktionsprojektionen (zum Filtern) oder Sortierprojektionen (zum Sortieren) genutzt werden.
Unter Umständen sind explizite Joins auch nur als Zwischenschritt erforderlich, um Projektionen für Schlüsselwerte in der ON-Bedingung weiterführender Joins zu ermöglichen.
Alle Joins verfügen über die rechts abgebildeten Parameter:
|
|
Die Konfiguration einer Join Bedingung ist grundsätzlich optional. Für alle Joins außer dem Datenobjekt Join impliziert der Kontext bereits eine Bedingung für den Join:
Eine explizit konfigurierte Join Bedingung wird mit einer ggf. anwendbaren impliziten Bedingung grundsätzlich in einer UND-Verknüpfung zusammengefasst. Der Join steuert in diesem Fall nur Datensätze bei, für die sowohl die implizite als auch die explizite Bedingung als bestanden gelten. |
ACHTUNG
Eine wichtige Restriktion für die Definition einer expliziten Join Bedingung ist, dass sich diese keine Projektion mit eine Pfad zu einem Feld beziehen darf, der einen weiteren Join implizieren würde.
Was recht kompliziert klingt, ist am folgenden Beispiel anschaulich nachvollziehbar:
Eine Suche für Benutzer verwendet einen Datenobjekt Join, um die Daten aller Firmen in die Suche einzubeziehen, deren ID im Listenfeld "Firmen" (companies) eines Benutzerkontos genannt wird.
Als Nebenbedingung soll dabei gelten, dass dabei nur Firmenkonten berücksichtigt werden sollen, deren Adressfeld "Land" (address.countryCode) auf das Land "Schweiz" (CH) verweist.
►HINWEIS◄ Die rechts abgebildete Konfiguration ist nicht komplett unsinnig, aber "knapp daneben". Sie soll einen verbreiteten Irrweg bei der Konfiguration einer Join Bedingung demonstrieren, die eine Nebenbedingung enthält.
Diese Konfiguration liefert beim Ausführen der Suche eine Fehlmeldung: A join within a on condition is not possible Der "Join", auf den sich die Fehlermeldung bezieht ist nicht der explizit angelegte Datenobjekt Join zum Firmenkonto. Problematisch ist dagegen, dass das Feld "Land" in der Adresse des Firmenkontos adressiert wird. Der im Parameter Feld ausgewählte Pfad impliziert einen Join auf die Adresse, damit deren "Land"-Feld ausgewertet werden kann. Was etwa in der Projektion für eine Ausgabespalte keine Schwierigkeiten macht, ist im Kontext einer Join Bedingung allerdings nicht möglich. Damit die Einschränkung für das Land des Firmenkontos wirksam umgesetzt werden kann, muss die Firmenadresse durch einen zusätzliche Join explizit in die Suche einbezogen werden. |
|
Die Nebendingung für die Adresse einer durch einen Join einbezogenen Firma kann hier effektiv nur durch einen zusätzlichen Join für die Adresse umgesetzt werden: |
|
Der oben beschriebene Datenobjekt Join kann wie folgt angepasst werden:
In der rechts abgebildeten Form bindet der Datenobjekt Join zunächst alle Firmen zu, die im companies-Feld eines Benutzers zugewiesen sind. Unterhalb wurde ein Mapped Join hinzugefügt, der sich auf das "Adresse"-Feld einer durch den ersten Join bereitgestellten Firma (cmp.address) als Join Property bezieht:
|
|