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:

Tabelle "CANDIDATES"

NAME

LANGUAGE

LEVEL

Agneta

en

4

Agneta

sv

5

Bruno

en

1

Bruno

it

5

Charles

en

5

Charles

fr

2

Diego

en

3

Diego

es

5

Emilia

de

3

Emilia

en

2

Emilia

it

5

Florence

fr

5

Eine weitere Tabelle TASKS definiert für per Spalte TASK benannte Aufgaben die jeweils erforderlichen "Englischkenntnisse" (EN_LEVEL):

Tabelle "TASKS"

TASK

EN_LEVEL

Service Desk

4

Pool supervisor

3

Housekeeping

2

Security

2

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:

TASK

NAME

Service Desk

Agneta

Service Desk

Charles

Pool supervisor

Agneta

Pool supervisor

Charles

Pool supervisor

Diego

Housekeeping

Agneta

Housekeeping

Charles

Housekeeping

Diego

Housekeeping

Emilia

Security

Agneta

...

...

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.

  • In Unterscheid dazu würde ein LEFT JOIN im Ergebnis auch TASKS auflisten, für die keiner der CANDIDATES ausreichend qualifiziert ist. Die Spalte NAME bliebe dann leer.

  • Für alle TASKS ungeeignete CANDIDATES (hier: "Bruno" und "Florence") sind weder im INNER JOIN noch im LEFT JOIN zu finden. Diese würden nur per RIGHT JOIN mit leerer Spalte TASK aufgelistet.

    WICHTIG◄ Im Kontext einer Suche in Lobster Data Platform / Orchestration kann ein RIGHT JOIN in vielen Fällen nicht so "arbeiten", wie das unmittelbar auf einer Datenbank der Fall wäre. Per Standard werden explizit konfigurierte Joins (ebenso wie ggf. implizit generierte; Details s. unten) als LEFT JOIN ausgeführt.

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.



Beispiel: Benutzer ←(1 : 1)→ Adressen

images/download/attachments/167858318/image-2024-1-17_16-35-56-version-1-modificationdate-1705505757058-api-v2.png

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)


  • Die Tabelle base_user für Benutzer verweist über das Fremdschlüssel-Feld address_id auf das als Primärschlüssel definierte Feld id der Tabelle base_address für Adressen.

  • Die ON-Bedingung verlangt die Übereinstimmung von Fremdschlüssel und Primärschlüssel, sodass jedem Benutzer "seine" Adresse zugeordnet wird.

  • Der LEFT JOIN stellt sicher, dass auch Benutzer ohne zugeordnete Adresse im Ergebnis erscheinen können.

Beispiel: Benutzer ←(1 : n) → IDs von zugewiesenen Rollen

images/download/attachments/167858318/image-2024-1-17_17-27-46-version-1-modificationdate-1705508866380-api-v2.png

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

images/download/attachments/167858318/image-2024-1-18_10-24-5-version-1-modificationdate-1705569845585-api-v2.png

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
LEFT JOIN base_address ON ( id = address_id)
LEFT JOIN base_address_flag_attribute ON ((id = address_id) AND (flag_type = VIP))

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 AttributbesitzerpfadAttribut) 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:

  • Der Join Alias (mit dem Standardwert a) definiert den Namen über den die Struktur aus dem Join in der zugehörigen Suche adressiert werden kann. Die betrifft meistens auch Projektionen für Einschränkungen in der Join Bedingung.

  • Im Auswahlfeld/Combobox muss ein Join Typ ("Inner", "Left", "Right") ausgewählt werden. Per Standard ist "Left" vorbelegt, da dieser Join Typ den typischen Anwendungsfall - das Hinzuziehen von ggf. vorhandenen Datensätzen in den Kontext der Suche - abdeckt. Die anderen Typen ("Inner" und "Right") sind im Kontext einer Suche oft nur eingeschränkt hilfreich bzw. nur in besonderen Fällen überhaupt einsetzbar.

  • Die Option Optional ist per Standard ausgewählt. Dies bewirkt, dass der konfigurierte Join zur Laufzeit nur dann effektiv in Datenbankabfragen übernommen wird, wenn in der Suchkonfiguration auch wirklich Projektionen angelegt sind, die auf den Alias-Namen zugreifen.

images/download/attachments/167858318/image-2024-1-18_14-3-41-version-1-modificationdate-1705583021831-api-v2.png

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.

images/s/-95e2zf/9012/8yg2g7/_/images/icons/emoticons/warning.svg ACHTUNG images/s/-95e2zf/9012/8yg2g7/_/images/icons/emoticons/warning.svg 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.


  • Der Datenobjekt Join verweist auf den Entitätstyp "Firmenkonto" als Datenobjekt.

  • Als Join Alias wurde willkürlich das Textkürzel cmp ausgewählt.

  • Als Join Typ ist "Inner" ausgewählt". In Verbindung mit der Abwahl der Option Optional soll dies erzwingen, dass die übergeordnete Suche nur Benutzer auflistet, für mindestens ein Firmenkonto existiert, das die folgende Join Bedingung erfüllt:

    • Die erste Feld Einschränkung verlangt eine Übereinstimmung der Firmen-ID (cmp.id) mit einem Eintrag im Listenfeld "Firmen" (companies) des Benutzerkontos.

    • Die zweite Feld Einschränkung soll sicherstellen, dass sich der Standort einer zugeordneten Firma in der Schweiz befindet:

      • Als Prüfwert (links) wird dazu das Adressfeld "Land" der Firma (cmp.address.countryCode) adressiert.

      • Der statische Vergleichswert (rechts) wird über einen Dynamische Aufzählung-Wertauflöser bereitgestellt.


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.

images/download/attachments/167858318/image-2024-1-19_18-13-7-version-1-modificationdate-1705684387852-api-v2.png

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:

  • Der Join Typ wurde auf "Left" umgestellt.

  • Die Optional-Option wurde abgewählt.

  • Die zweite Feld Einschränkung wurde aus der Join Bedingung entfernt.

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:

  • Als Join Alias wurde hier willkürlich der Name cmpAddress zugeordnet.

  • Der Join Typ "Inner" stellt in Verbindung mit der abgewählten Option Optional sicher, dass die übergeordnete Suche genau die Benutzer als Treffer liefert, für die die Join Bedingung erfüllt ist, die prüft, ob das "Land"-Feld der Adresse der Firma (cmpAddress.countryCode) auf das Land "Schweiz" (CH) verweist.


images/download/attachments/167858318/image-2024-1-19_18-51-6-version-1-modificationdate-1705686666395-api-v2.png