Home > Artikel > Ausgabe 3/2016 > SELECT-Abfragen in Union

SELECT-Abfragen in Union

Achtung: Sie sind nicht angemeldet. Wenn Sie Abonnent sind und sich anmelden, lesen Sie den kompletten Artikel, laden das PDF herunter oder probieren die Beispieldatenbank aus (sofern vorhanden).

In der vorigen Ausgabe von Access [basics] schnitten wir das Thema UNION-Abfragen bereits an. Dort wurde ein Kombinationsfeld zur Formularnavigation mit einem UNION-SELECT-Ausdruck als Datenherkunft belegt. Deshalb nehmen wir diese SQL-Spezialität hier einmal etwas allgemeiner unter die Lupe und betrachten zusammengesetzte Abfragen im Detail.

Beispieldatenbank

Die Beispiele dieses Artikels finden Sie in der Datenbank 1603_Union­Abfragen.accdb

Daten zusammenfassen

In der Beispieldatenbank stellt sich eine Aufgabe, die so gar nicht selten zu erledigen ist: Daten verschiedener Herkunft sollen in einer Tabelle zusammengeführt werden. Es handelt sich um Adressen, die etwa aus importierten CSV-Dateien stammen und bereits in den Tabellen tblAdressen1 bis tblAdressen4 vorliegen.

Die erste enthält nur Adressen aus dem Postleitzahlengebiet 1* und 2*, die nächste aus 3*, 4* und 5*, eine weitere aus 6*, 7*, 8* und 9*, sowie schließlich eine für jene aus 0*. Die Datensätze dieser vier Tabellen transferieren wir nun in ihrer Gesamtheit in die Tabelle tblAdressen.

Hierfür könnten Sie eine einfache Anfügeabfrage erstellen und nacheinander die jeweiligen Quelltabellen in sie einsetzen, um den Export zu bewerkstelligen. Das allerdings ist wenig elegant und zieht so einiges Mausgeklicke nach sich, von der Fehleranfälligkeit einmal abgesehen. Besser, Sie greifen hier zu einer UNION-Abfrage, die die Sache in einem Rutsch erledigt.

UNION-Abfragen

Eine UNION-Abfrage fasst zwei oder mehr einzelne Abfragen in einem Ergebnis zusammen. Während Sie fast alle Abfragetypen mit dem GUI-Query-Editor anlegen und bearbeiten können, sieht das bei UNION-Abfragen anders aus. Hier sind Sie im Entwurf auf den SQL-Editor beschränkt. Ohne grundlegende SQL-Kenntnisse sind sie damit also nicht im Boot.

Bild 1 zeigt, wie Sie gespeicherte UNION-Abfragen im Navigationsbereich von Access sofort erkennen können. Das Schnittmengensymbol macht sie deutlich.

Die zwei Ringe stellen das Symbol für UNION-Abfragen im Navigationsbereich dar

Bild 1: Die zwei Ringe stellen das Symbol für UNION-Abfragen im Navigationsbereich dar

Legen Sie Ihre erste UNION-Abfrage so an: Klicken Sie im Ribbon Erstellen auf Abfrageentwurf. Schließen Sie sofort den Dialog zur Tabellenauswahl. Der Ribbon-Tab Entwurf ist nun automatisch aktiv, und links wird auch gleich der Button SQL für die Ansicht eingeblendet. Klicken Sie darauf, um den SQL-Editor zu öffnen.

Nur als Hinweis hat Access bereits den Ausdruck Select; als SQL-Text im Editor eingefügt. Um nun alle Datensätze der Tabelle tblAdressen1 im Abfrageergebnis anzuzeigen, erweitern Sie den Ausdruck in

SELECT * FROM tblAdressen1

Da die Aufgabe darin besteht, zusätzlich zu dieser Tabelle auch noch eine weitere ins Ergebnis zu holen, benötigen wir ein weiteres SELECT-Statement, das diesmal aber über den Ausdruck UNION mit dem anderen SELECT-Statement verknüpft wird:

SELECT * FROM tblAdressen1

UNION

SELECT * FROM tblAdressen2

Klicken Sie auf den Ausführen-Button im Ribbon, um sich davon zu überzeugen, dass diese Abfrage einwandfrei funktioniert. Speichern Sie sie unter dem Namen qry_Union_Simple ab. Im Navigationsbereich wird sie sogleich angezeigt und weist das entsprechende Symbol auf. Ein Rechtklick auf den Eintrag listet im Kontextmenü übrigens nun ebenfalls nur die SQL-Ansicht ein. Die normale Entwurfsansicht ist nicht erreichbar.

Eine UNION-Abfrage wird als solche allerdings nur identifiziert, wenn Sie den Aufbau von oben hat und Sie das Ergebnis nicht in Klammern einfassen. Denn auch diese Syntax ließe sich herstellen:

SELECT * FROM (

SELECT * FROM tblAdressen1

UNION

SELECT * FROM tblAdressen2

)

Die eigentliche UNION-Abfrage ist hier innerhalb der Klammern zu finden und wird wie eine einzelne Tabelle behandelt, indem ein weiterer Select-Ausdruck auf diese Unterabfrage angewandt wird. Das Anzeigeergebnis ist zwar dasselbe, die Abfrage wird dabei jedoch als normale Auswahlabfrage gespeichert. Damit besteht automatisch auch die Möglichkeit, sie im Entwurfsmodus zu öffnen. Das sieht dann etwa aus, wie in Bild 2. In dieser Abfrage wurde das UNION-Ergebnis über das äußere Select-Statement noch einer Sortierung nach Namen unterzogen, sowie einer Filterung nach dem Postleitzahlenbereich 1*.

Dass es sich hier um eine UNION-Abfrage handelt, ist nicht gleich erkennbar

Bild 2: Dass es sich hier um eine UNION-Abfrage handelt, ist nicht gleich erkennbar

Das geht aus dem Entwurf zwar vor, irritierend ist aber die seltsame Tabellenbezeichnung [%$##@_Alias]. Access setzt diesen Ausdruck immer dann ein, wenn eine Unterabfrage nicht namentlich definiert ist. Sie können das etwa so ändern:

SELECT * FROM (

SELECT * FROM tblAdressen1

UNION

SELECT * FROM tblAdressen2

) As UTable

Hier wird das Klammerergebnis, die UNION-Abfrage, als virtuelle Tabelle UTable definiert, was anschließend auch im Entwurfsmodus zu sehen ist. Das [%$##@_Alias] macht dem UTable Platz. Indessen geht im Entwurf auch jetzt noch nicht hervor, dass die angezeigte Tabelle tatsächlich eine UNION-Abfrage darstellt. Erst das Schalten in den SQL-Modus lüftet das Geheimnis.

Sie können direkt auch mehrere Tabellen oder Abfragen in einer UNION-Abfrage zusammenführen.

Alle für unsere Zielabfrage benötigten Adresstabellen fügen sich so aneinander:

SELECT * FROM tblAdressen1

UNION

SELECT * FROM tblAdressen2

UNION

SELECT * FROM tblAdressen3

UNION

SELECT * FROM tblAdressen4

Im Ergebnis stehen damit alle Adressen aller Postleitzahlenbereiche bereit.

Wie viele Tabellen in einer UNION-Abfrage eingebaut sein dürfen, steht nicht eindeutig fest. An sich gibt es die Aussage von Microsoft, dass eine Abfrage nicht mehr als 32 Tabellen enthalten darf. Entweder ist diese Information veraltet und spiegelt nicht mehr die Eigenschaften der Access Database Engine wider, oder für UNION gelten andere Regeln. Jedenfalls gelang es uns problemlos, auch 49 Adresstabellen aneinanderzufügen. Erst bei 50 und mehr kam es zur Fehlermeldung

Ausdruck oder Abfrage zu komplex

Es dürfte auch von der Anzahl der Felder der Quelltabellen abhängen, wann diese Meldung auf den Plan tritt.

Übrigens können sie die gleiche Tabelle auch mehrmals mit sich selbst kombinieren:

SELECT * FROM tblAdressen1

UNION

SELECT * FROM tblAdressen1

Man sollte hier erwarten, dass sich die Zahl der Datensätze gegenüber tblAdressen1 verdoppelt. Tatsächlich ist das nicht der Fall!

Der Grund dafür ist, dass Access im Ergebnis keine doppelten identischen Datensätze ausgibt, also zusätzlich ein DISTINCT ausführt. Der Ausdruck UNION bekommt damit ja auch die korrekte Bedeutung: Das Ergebnis ist die Mengensumme beider Tabellen! Allerdings können Sie dies aufheben, indem Sie nach UNION noch das Prädikat ALL setzen:

SELECT * FROM tblAdressen1

UNION ALL

SELECT * FROM tblAdressen1

In diesem Fall werden tatsächlich alle Ausgangsdatensätze aneinander gereiht. Vorteil dieser Variante ist die bessere Performance. Access muss hier lediglich die Datensätze summieren, während ohne ALL noch die Überprüfung auf Doppelungen stattfindet.

Sortierung und Filterung

Die kombinierten Datensätze können gleich innerhalb der UNION-Abfrage sortiert und gefiltert werden, indem die normalen ORDER BY-Statements oder WHERE-Bedingungen ans Ende des SQL-Ausdrucks gestellt werden:

SELECT * FROM tblAdressen1

UNION

SELECT * FROM tblAdressen2

UNION

SELECT * FROM tblAdressen3

WHERE PLZ LIKE "1*"

ORDER BY Nachname;

Die drei Quelltabellen werden kombiniert und das Ergebnis schließlich der Filterung nach Postleitzahlengebiet 1 unterzogen, sowie aufsteigend nach Nachname sortiert.

Welche Anforderungen bestehen an die Ausgangstabellen?

Die wichtigste Anforderung ist, dass die Zahl der Felder jeder Tabelle oder Abfrage übereinstimmen. Das gilt indessen nicht für die Namen der Felder. Auch der Datentyp spielt keine Rolle. Versuchen Sie etwa dies:

SELECT Nachname FROM tblAdressen1

UNION

SELECT PLZ FROM tblAdressen2

Selbst dann, wenn PLZ vom Datentyp Zahl (Long) wäre, käme ein gültiges, wenn auch wenig sinnvolles, Ergebnis heraus. Die eine Spalte der Abfrage wird in der Anzeige als Nachname ausgegeben. Im Zweifel erzeugt Access hier den Datentyp String. Sie erkennen nebenbei, dass es unter Umständen durch die Konvertierungen und eindeutige Kombinierung der Datensätze ganz schön zu arbeiten hat.

Völlig belanglos ist die Benennung der Felder allerdings nicht. Nehmen Sie etwa die Ausgangstabellen tblAdressen1 und tblAdressen4. In letzterer gibt es das Feld Nachname nicht. Hier lautet es stattdessen NName. Dennoch funktioniert

SELECT * FROM tblAdressen1

UNION

SELECT * FROM tblAdressen4

ORDER BY Nachname;

Versuchen Sie es aber anders herum, so findet die SQL-Engine das im Sortierausdruck verlangte Nachname jedoch nicht mehr und gibt deshalb eine Fehlermeldung aus:

SELECT * FROM tblAdressen4

UNION

SELECT * FROM tblAdressen1

ORDER BY Nachname;

Access verwendet also das erste Select-Statement, beziehungsweise die erste Tabelle, für die Spaltenbenennungen und geht stillschweigend davon aus, dass die Felder der weiteren Tabellen identisch benannt sind.

Eine Abfrage kommt übrigens generell auch ganz ohne Ausgangstabellen aus. Ein einfacher SQL-Ausdruck, wie dieser, ist etwa erlaubt:

SELECT 3 AS Zahl;

In UNION-Abfrage ist dies hingegen limitiert. Dieser Ausdruck läuft nicht:

SELECT 3 AS Zahl

UNION

SELECT 4 AS Zahl

Access meckert hier über die Abstinenz einer Ausgangstabelle. Um die Abfrage zum Laufen zu bringen, fügen Sie einfach eine beliebige Tabelle hinzu:

SELECT 3 AS Zahl FROM MSysObjects

UNION

SELECT 4 AS Zahl FROM MSysObjects

Die eingesetzte Systemtabelle MSysObjects, die in jeder Access-Datenbank vorhanden ist, spielt offenbar hier keine Rolle. Schließlich wird ja auf keines ihrer Felder Bezug genommen.

Dass die Sache nicht so einfach ist, wird ersichtlich, wenn Sie das UNION gegen UNION ALL austauschen. Im Ergebnis erscheinen nun genau so viele Dreien und Vieren, wie die Tabelle MSysObjects Datensätze aufweist! Das ist auch logisch, da der Ausdruck 3 oder 4 quasi für jeden Datensatz der Tabelle berechnet wird.

Für solche Nummerierungszwecke eignet sich deshalb eine spezielle Tabelle, die eigens für UNION-Abfragen angelegt wird. In der Beispieldatenbank nennt sie sich tblX und hat, wie aus Bild 3 ersichtlich, nur einen einzigen Datensatz des Typs Long.

Inhalt der Hilfstabelle tblX

Bild 3: Inhalt der Hilfstabelle tblX

Interessant ist übrigens auch die auf das Feld ID angewandte Gültigkeitsregel, welche den Ausdruck >0 Und <2 trägt. Damit ist sichergestellt, dass niemals ein weiterer Datensatz in die Tabelle gelangt, denn kein anderer Wert, als 1, entspricht dieser Regel.

Die sicherste und performanteste UNION-Abfrage zur Generierung einer Nummernfolge, sieht damit so aus:

SELECT 1 AS Zahl FROM tblX

UNION ALL

SELECT 2 AS Zahl FROM tblX

UNION ALL

Sie haben das Ende des frei verfügbaren Teil dieses Artikels erreicht!

Wenn Sie mehr lesen und auf viele weitere Artikel zugreifen möchten, melden Sie sich als Abonnent unter Login an. Falls nicht, bestellen Sie doch einfach ein Jahresabonnement!