psar04
Goto Top

SQL,Access: Mehrere Tabellen mit gleicher Struktur bzw. Spalten zusammenführen

Hallo,

ich habe eine zugegebener Weise nicht so tolle Access Datenbank bekommen, aus der ich Daten rausziehen muss. In der DB gibt es Tabellen, die DatenXX heißen. XX steht hierbei für die Kurzform von Jahreszahlen (das sollte wohl der Übersichtlichkeit dienen). Die Tabellen haben ansonsten alle die gleiche Struktur bzw. Spaltenbezeichnungen. Eine Spalte heißt jahrgang und ich benötige nun alle Datensätze, bei denen das Feld "jahrgang" leer ist. Für eine einzelne Tabelle ist das ja recht schnell gemacht, ich benötige die Datensätze nun aber aus allen Tabellen bei denen jahrgang leer. Die entsprechenden Datensätze aus allen Tabellen sollen in der Ausgabe dann einfach untereinander geschrieben werden.
Bei zwei Tabellen würde ich das einfach so machen:
SELECT * FROM (SELECT * FROM Daten00 UNION ALL SELECT * FROM Daten01) WHERE jahrgang is null;

Doch was ist die eleganteste Möglichkeit die Datensätze aus allen Tabellen (Daten00 - Daten99) zusammenzuführen? 98 UNIONs? Da meckert Access garantiert, dass die Abfrage zu komplex ist. Gibt es eine Möglichkeit mit einem Range-Operator oder so zu arbeiten, der mir nicht bekannt ist? Also z. B. SELECT * FROM RANGE Daten$XX (00,99,1) WHERE....

Ich freue mich auf eure Tipps.

Grüße
PSaR04

Content-Key: 242753

Url: https://administrator.de/contentid/242753

Ausgedruckt am: 29.03.2024 um 14:03 Uhr

Mitglied: colinardo
Lösung colinardo 05.07.2014 aktualisiert um 18:00:30 Uhr
Goto Top
Hallo PSaR04,
mit VBA hätte ich dafür eine Lösung. Es fasst alle deine gewünschten Daten in einer neu erstellten Tabelle zusammen, und öffnet diese zum Schluss.
Sub MergeTablesIntoOne()
    Dim rsAll As Recordset, rsCurrent As Recordset, tblName as String, c as integer, i as integer   
    DoCmd.SetWarnings False
    ' passende Datensätze aus Daten00 in neue Tabelle schreiben  
    DoCmd.RunSQL "Select * into DataResult from Daten00 where jahrgang is null"  
    DoCmd.SetWarnings True
    ' neue Tabelle öffnen  
    Set rsAll = CurrentDb.OpenRecordset("Select * From DataResult", dbOpenDynaset)  
    Set db = CurrentDb
    ' Für alle passenden Tabellen  
    For i = 1 To 99
        tblName = "Daten" & Right("0" & i, 2)  
        Set rsCurrent = db.OpenRecordset("Select * From " & tblName & " where jahrgang is null", dbOpenSnapshot)  
        rsCurrent.MoveFirst
        While Not rsCurrent.EOF
            rsAll.AddNew
            For c = 0 To rsAll.Fields.Count - 1
                rsAll.Fields(c).Value = rsCurrent.Fields(c).Value
            Next
            rsAll.Update
            rsCurrent.MoveNext
        Wend
    Next
    ' neu erstellte Tabelle anzeigen  
    DoCmd.OpenTable "DataResult"  
End Sub
Grüße Uwe
Mitglied: PSaR04
PSaR04 05.07.2014 um 17:55:18 Uhr
Goto Top
Hallo colinardo!

Vielen Dank für den Programmcode, ich hatte gar nicht mit einer so ausführlichen Lösung gerechnet. Das hat mir bestimmt 2 Stunden Arbeit gespart, da ich mit VBA bisher noch nicht so viel gemacht habe.
Anhand der doch relativ aufwendigen Lösung gehe ich dann davon aus, dass sich die gewünschte Abfrage nicht einfacher realisieren lässt (etwa über einen mir unbekannten Operater oder ähnlichem, wie im ersten Post von mir gehofft)?!?
Ich musste deinen Quellcode nur noch an 2 Stellen etwas anpassen, da es zu Fehlern gekommen ist.
1. Bei der Deklaration der Recordsets rsAll und rsCurrent musste ich den Datentyp von "Recordset" auf "DAO.Recordset" ändern. Da es sonst zur Fehlermeldung "Typen unverträglich" kam.
2. Einige der Tabellen sind leer, daher musste ich noch ein "If Not rsCurrent.RecordCount = 0 Then..." ergänzen.

Hier der komplette Quellcode:
Sub MergeTablesIntoOne()
    Dim rsAll As DAO.Recordset, rsCurrent As DAO.Recordset, tblName as String, c as integer, i as integer   
    DoCmd.SetWarnings False
    ' passende Datensätze aus Daten00 in neue Tabelle schreiben  
    DoCmd.RunSQL "Select * into DataResult from Daten00 where jahrgang is null"  
    DoCmd.SetWarnings True
    ' neue Tabelle öffnen  
    Set rsAll = CurrentDb.OpenRecordset("Select * From DataResult", dbOpenDynaset)  
    Set db = CurrentDb
    ' Für alle passenden Tabellen  
    For i = 1 To 99
        tblName = "Daten" & Right("0" & i, 2)  
        Set rsCurrent = db.OpenRecordset("Select * From " & tblName & " where jahrgang is null", dbOpenSnapshot)  
        If Not rsCurrent.RecordCount = 0 Then
            rsCurrent.MoveFirst
            While Not rsCurrent.EOF
                rsAll.AddNew
                For c = 0 To rsAll.Fields.Count - 1
                    rsAll.Fields(c).Value = rsCurrent.Fields(c).Value
                Next
                rsAll.Update
                rsCurrent.MoveNext
            Wend
        End If
    Next
    ' neu erstellte Tabelle anzeigen  
    DoCmd.OpenTable "DataResult"  
End Sub

Vielen Dank nochmal.
PSaR04
Mitglied: colinardo
colinardo 05.07.2014 aktualisiert um 18:15:39 Uhr
Goto Top
1. Bei der Deklaration der Recordsets rsAll und rsCurrent musste ich den Datentyp von "Recordset" auf "DAO.Recordset" ändern. Da es sonst zur Fehlermeldung "Typen unverträglich" kam.
OK, dann handelt es sich vermultich um eine ältere Access Datenbank. In neueren ist ADO Standard.

Anhand der doch relativ aufwendigen Lösung gehe ich dann davon aus, dass sich die gewünschte Abfrage nicht einfacher realisieren lässt (etwa über einen mir unbekannten Operater oder ähnlichem, wie im ersten Post von
Wart mal ab vielleicht hat der @Biber da eventuell noch eine Lösung für eine reine SQL Abfrage.

Grüße Uwe
Mitglied: Biber
Lösung Biber 05.07.2014, aktualisiert am 06.07.2014 um 17:00:41 Uhr
Goto Top
Moin PSaR04 und colinardo,

um die Zweifel auszuräumen:
- es existiert weder in Access-SQL noch einem anderem SQL-Dialekt etwas wie "Mach mir einen UNION über Tabelle 1...n"
- wie PSaR04 schon richtig vermutete - Access würde bei 99 Tabellen in einer Query auch bedauernd abwinken, da bei 32 offenen Tabellen in einer Query Schicht ist (Okay, ist an dieser Stelle mal kein Vorwurf an die PraktikantInnen. Wenn mehr als 32 Tabellen in einer Query verbaut werden müssen, dann ist das Datenbankdesign marode, so wie ja auch in dieser "Ich mach ma' 100 Tabellen, weil ich 100 Jahre verwalten will"-Musterlösung).


Einziger Verbesserungsvorschlag, den ich zum Skript anbringen würde:

Ich würde sicherheitshalber ein Feld mehr ins Resultset übernehmen, damit ich weiss, aus welcher Tabelle der Satz stammt.
Also statt
Set rsCurrent = db.OpenRecordset("Select * From " & tblName & " where jahrgang is null",

etwa so
Set rsCurrent = db.OpenRecordset("Select ' " & tblname & " ' as Tbname, t.* From " & tblName &" as t where jahrgang is null",


Falls die Abfragen auf die 99 Tabellen noch auf andere Fragestellungen modifiziert werden sollen:

Dann würde ich die Daten aus den Tabellen 1..99 nicht nur jetzt einmalig in einen Resultset schaufeln, sondern gleich- wenn ich eh' alle Tabellen durchwackeln muss- den ganzen Quatsch in eine neue Tabelle schreiben. Mit allen Sätzen, ohne einschränkendes WHERE.

Und eben mit einem Zusatzfeld, in dem der Quelltabellenname steht.

Grüße
Biber
Mitglied: PSaR04
PSaR04 06.07.2014 um 16:59:38 Uhr
Goto Top
Zitate von @colinardo:

OK, dann handelt es sich vermultich um eine ältere Access Datenbank. In neueren ist ADO Standard.
Ja, es handelt sich um eine Datenbank, die glaube ich unter Access 2000 entwickelt wurde.


Zitate von @Biber:
- es existiert weder in Access-SQL noch einem anderem SQL-Dialekt etwas wie "Mach mir einen UNION über Tabelle 1...n"
Ok, danke für die Bestätigung. Ich konnte mir das auch fast nicht vorstellen, da ich trotz intensiver Suche nichts dazu gefunden habe, aber gut jetzt Gewissheit zu haben.

Wenn mehr als 32 Tabellen in einer Query verbaut werden müssen, dann ist das Datenbankdesign marode, so wie ja auch in dieser "Ich mach ma' 100 Tabellen, weil ich 100 Jahre verwalten will"-Musterlösung).
Da stimme ich dir voll und ganz zu. Die DB soll jetzt auch schnellstmöglich abgelöst werden, hat schon genug Nerven gekostet. Es handelt sich bei der benötigten Abfrage schon um eine vorbereitende Maßnahme dafür.

Ich würde sicherheitshalber ein Feld mehr ins Resultset übernehmen, damit ich weiss, aus welcher Tabelle der Satz stammt.
Ja, daran hatte ich auch schon gedacht, bisher ist das aber nicht wichtig. Aber gut, dass du die Lösung dafür schon gleich gepostet hast, kann ich bestimmt noch gebrauchen.
Dann würde ich die Daten aus den Tabellen 1..99 nicht nur jetzt einmalig in einen Resultset schaufeln, sondern gleich- wenn ich eh' alle Tabellen durchwackeln muss- den ganzen Quatsch in eine neue Tabelle schreiben. Mit allen Sätzen, ohne einschränkendes WHERE.
Wenn noch mehr Abfragen notwendig werden, mache ich das. Bisher ist das aber noch nicht abzusehen.


Vielen Dank, ihr habt mir sehr weitergeholfen!
PSaR04