Tabellenname während Abfrage erstellen
Trezimann
- datenbank
Hallo Insider, habe mal ein kleines Problem, mit dem ich mich schon ein paar Stunden rumschlage. Es geht um folgendes: Ich habe eine Tabellenabfrage in einer Datenbank erstellt, die auch grundsätzlich funktioniert. Der code (in Kurzform):
"select a.wert1,a.wert2,a.wert3 from tabelle1 AS a JOIN tabelle_[a.wert2] AS b where a.wert1 = 0 AND wert3 >= x AND b.werta = y ORDER BY datum DESC"
Wie gesagt, das klappt (Original ist bedeutend länger). Das Problem ist die 2.Tabelle, deren Name sich normalerweise aus "tabelle" + a.wert2 (im beispiel in eckigen Klammern), also aus einem Wert der ersten Tabelle zusammensetzt. Ich habe etliche Schreibweisen probiert: ...tabelle."a.wert2."..., ohne Anführungszeichen, etc. Setze ich statt [a.wert2] sofort die richtige Zahl ein, klappt alles. Meine Frage ist nun, ob es überhaupt möglich ist, den Tabellennamen während einer Abfrage zu generieren? Wenn ja wäre ich für einen Tip dankbar, wo ich solche Beispiele oder Hilfe finden kann. Treziman
Hi,
habe mal ein kleines Problem, mit dem ich mich schon ein paar Stunden rumschlage. Es geht um folgendes: Ich habe eine Tabellenabfrage in einer Datenbank erstellt, die auch grundsätzlich funktioniert. Der code (in Kurzform):
"select a.wert1,a.wert2,a.wert3 from tabelle1 AS a JOIN tabelle_[a.wert2] AS b where a.wert1 = 0 AND wert3 >= x AND b.werta = y ORDER BY datum DESC"
Wie gesagt, das klappt (Original ist bedeutend länger). Das Problem ist die 2.Tabelle, deren Name sich normalerweise aus "tabelle" + a.wert2 (im beispiel in eckigen Klammern), also aus einem Wert der ersten Tabelle zusammensetzt.
Das kann m.W. nicht funktionieren - die Tabellennamen können nicht dynamisch aus den selektierten Werten erstellt werden.
Mir erscheint das Datenmodell fragwürdig - wenn es mehrere gleichstrukturierte und durchnumerierte Tabellen gibt.
Warum ist das nicht eine große Tabelle, die in einer zusätzlichen Spalte die Nummer enthält?
Dann würde es funktionieren, per
JOIN grosse_tabelle b ON a.wert2 = b.nummer AND bisherigeJoinBedingung
den Wert aus Tabelle a zu berücksichtigen.
cu,
Andreas a/k/a MudGuard
Hello,
habe mal ein kleines Problem, mit dem ich mich schon ein paar Stunden rumschlage. Es geht um folgendes: Ich habe eine Tabellenabfrage in einer Datenbank erstellt, die auch grundsätzlich funktioniert. Der code (in Kurzform):
"select a.wert1,a.wert2,a.wert3 from tabelle1 AS a JOIN tabelle_[a.wert2] AS b where a.wert1 = 0 AND wert3 >= x AND b.werta = y ORDER BY datum DESC"
Wie gesagt, das klappt (Original ist bedeutend länger). Das Problem ist die 2.Tabelle, deren Name sich normalerweise aus "tabelle" + a.wert2 (im beispiel in eckigen Klammern), also aus einem Wert der ersten Tabelle zusammensetzt.
Ich habe etliche Schreibweisen probiert: ...tabelle."a.wert2."..., ohne Anführungszeichen, etc. Setze ich statt [a.wert2] sofort die richtige Zahl ein, klappt alles. Meine Frage ist nun, ob es überhaupt möglich ist, den Tabellennamen während einer Abfrage zu generieren? Wenn ja wäre ich für einen Tip dankbar, wo ich solche Beispiele oder Hilfe finden kann.
Bei MySQL geht das mit EXECUTE, Benutzerfunktionen und prepared Statements. Ist also leider etwas komplexer. Ich habe das auch erst einmal gemacht, finde es jetzt aber auf die Schnelle nicht.
Musst Du dich bitte mal durchwühlen. Wenn es geklappt hat, bitte hier posten, damit das Ergebnis dokumentiert ist.
EXECUTE
aufrufenSELECT
abfragenDas prepared Statement geht nur durch die Hintertür, siehe 3.
Liebe Grüße
Tom S.
Hello,
ich habe das eben mal durchgespielt wie beschrieben.
Das funktioniert (bei mir) solange, wie das Endergebnis nur aus einem einzelnen Wert oder einem einzigen Resultset besteht. Mit mehreren Resultsets habe ich es noch nicht hinbekommen.
Unter Stackoverflow habe ich noch etwas dazu gefunden. Da muss ich jetzt selber mal fragen:
Wie lange stehen die Resultsets zur Verfügung und wie kommt man da heran? Kann man die einzeln wieder freigeben?
Liebe Grüße
Tom S.
Äh wie jetzt?
Du hast eine Master-Tabelle
Wert1 Wert2
===== =====
Rolf 1
TS 2
MudGuard 1
Trezimann 2
Und dann zwei Zusatztabellen tab_1 und tab_2, und du bekommt einen Join hin, der für mich und MudGuard Zusatzwerte aus tab_1 liest, und für dich und Trezimann die Werte aus tab_2 ? Und zwar ohne UNION?
Kannst Du das man zeigen?
An Treziman: Mir wäre nicht bekannt, dass der SQL Standard einen Zugriff auf variable Tabellennamen vorsieht. Mich würde interessieren, welche Query bei Dir grundsätzlich funktioniert hat, bevor Du auf dynamische Tabellen erweitern wolltest.
Eine Lösung kann es geben, wenn man einen SQL Server hat, der Stored Procedures als Funktionen vorsieht, die man dann als Ergebnisspalten einbauen kann. Innerhalb dieser Funktionen kann man dann entweder eine Fallunterscheidung nach Wert2 machen und auf eine konkrete Tabelle zugreifen, oder man erzeugt darin ein dynamisches SQL Statement als String und führt das aus. Letzteres ist allerdings ein Performancekiller, weil dieses Statement dann jedesmal neu übersetzt und gebunden werden muss. Die Art der Realisierung hängt dann auch am konkreten SQL Server - hast Du MS SQL? IBM UDB? PostGreSQL? MySQL?
Für eine SINNVOLLE Lösung müsste man auch noch wissen, wie die Verknüpfungsregel zwischen tabelle1 und den Zusatztabellen sein soll. Du hast keine hingeschrieben, d.h. es gibt ein kartesisches Produkt. Das ist vermutlich nicht, was Du willst. Und du hast keine Spalten aus der Zusatztabelle im SELECT, d.h. der einzige Effekt wäre eigentlich, dass jede Zeile aus tabelle1 N-fach in die Ausgabe kommt (mit N=Anzahl Zeilen in der jeweiligen Zusatztabelle).
Für einen Vorschlag nehme ich daher mal an, dass
In dem Fall könnte man es so lösen. Beachte, dass die ON Bedingung nur dafür sorgt, dass die relevanten Zeilen aus tabelle1 in Bezug gesetzt werden, es fehlt definitiv eine richtige Join-Bedingung um das kartesische Produkt zu vermeiden. Es sei denn, du willst das unbedingt.
SELECT a.wert1, a.wert2, a.wert3, z.zusatz1, z.zusatz2
FROM tabelle1 AS a JOIN tabelle_a AS z ON a.wert2 = 'a'
WHERE a.wert1 = 0 AND a.wert3 >= x AND z.werta = y
UNION ALL
SELECT a.wert1, a.wert2, a.wert3, z.zusatz1, z.zusatz2
FROM tabelle1 a JOIN tabelle_b AS z ON a.wert2 = 'b'
WHERE a.wert1 = 0 AND a.wert3 >= x AND z.werta = y
UNION ALL
SELECT a.wert1, a.wert2, a.wert3, z.zusatz1, z.zusatz2
FROM tabelle1 AS a JOIN tabelle_c AS z ON a.wert2 = 'c'
WHERE a.wert1 = 0 AND a.wert3 >= x AND z.werta = y
UNION ALL
SELECT a.wert1, a.wert2, a.wert3, z.zusatz1, z.zusatz2
FROM tabelle1 AS a JOIN tabelle_d AS z ON a.wert2 = 'd'
WHERE a.wert1 = 0 AND a.wert3 >= x AND z.werta = y
ORDER BY datum DESC
Aber wenn es dir irgendwie möglich ist, halte Dich an MudGuards Vorschlag: Fasse die Subtabellen zu einer zusammen, füge eine Spalte "wert2" hinzu und joine über Wert2.
Rolf
Tach!
Und dann zwei Zusatztabellen tab_1 und tab_2, und du bekommt einen Join hin, der für mich und MudGuard Zusatzwerte aus tab_1 liest, und für dich und Trezimann die Werte aus tab_2 ? Und zwar ohne UNION?
Ja, so in der Art.
Kannst Du das man zeigen?
An Treziman: Mir wäre nicht bekannt, dass der SQL Standard einen Zugriff auf variable Tabellennamen vorsieht. Mich würde interessieren, welche Query bei Dir grundsätzlich funktioniert hat, bevor Du auf dynamische Tabellen erweitern wolltest.
Der SQL-Standard ist dabei auch nicht hilfreich. Aber jedes DBMS hat ja so seine eigenen Erweiterungen. Mit MySQL ist das Ziel erreichbar, indem man sich das Statement als String zusammenbaut und aus diesem über PREPARE ein EXECUTEables Statement generiert. Empfehlenswert ist das allerdings nicht als Lösung für das Problem. Stattdessen sollte man eher Mudgards Vorschlag in Betracht ziehen und statt zwei Tabellen nur eine mit einem weiteren Feld als Unterscheidungsmerkmal nehmen.
dedlfix.
Hello,
Du hast eine Master-Tabelle
Wert1 Wert2 ===== ===== Rolf 1 TS 2 MudGuard 1 Trezimann 2
Und dann zwei Zusatztabellen tab_1 und tab_2, und du bekommt einen Join hin, der für mich und MudGuard Zusatzwerte aus tab_1 liest, und für dich und Trezimann die Werte aus tab_2 ? Und zwar ohne UNION?
Kannst Du das man zeigen?
Das hatte ich in dem anderen Posting bereits alles verlinkt.
Man muss sich eine Stored Routine erstellen, die intern mit prapared Statements arbeitet.
Die kann aber dann als Funktion aber immer nur einen Wert als Ergebnis liefern.
Und wie Dedlfix schon beigetragen hat, wird der Server dabei einschlafen, da in jeder Zeile der Hauptabfrage das Statement für die Unterabfrage (in der Funktion) neu erstellt, gebunden, ausgeführt und zerstört werden muss.
Wenn es sich um Tabellen mit gleichem Aufbau handelt, kann man sie wirklich besser zusammenführen und für die Abfragen dann stored Routines verwenden. In diesen kann man dann auch vertikale Zugriffsrechte verwiklichen und dafür den direkten Zugriff auf die Tabelle verbieten.
Vertikale Rechte wären für mein Empfinden nahezu der einzige Grund, die Daten in mehreren gleichartigen Tabellen zu speichern. Beispiel dafür wären Programme für Außendienstmitarbeiter mit Kundenschutz usw., wenn man dann mit unterschiedlichen Apps (Filialen) auf eine gemeinsame Datenbank zugreifen will. Da will man die Geschäftsregeln ja gerne im DBMS zusammenhalten und nicht in den APIs umsetzen.
Liebe Grüße
Tom S.
Hallo, erstmal vielen Dank für Eure Antworten. Mit nur einer einzigen Tabelle kann ich das nicht machen, da Tabelle1 immer existiert, Tabelle2 für jeden User erst angelegt und im Abmeldungsfall gelöscht wird. Die Tabellen sind nicht gleich, haben unterschiedliche Spalten. Schade, ich bin während meiner Arbeiten mit PHP und MySqli bisher praktisch auf keine Grenzen gestossen. Muss ich halt über zwei Abfragen laufen lassen. Um das nochmal etwas zu verdeutlichen: Aus Tabelle1 wird die ID (unique) ausgelesen. Tabelle2 setzt sich aus "liste_" und dieser ID zusammen, also z.B. "liste_33". Löscht das Mitglied seinen Account, wird Tabelle2 nicht mehr benötigt, in Tabelle1 stehen aber noch andere Mitglieder. Den Namen separat zusammen zu setzen (tabelle2 = "liste_".$row->id."...) geht ja. Ich wollte es nur in einer einzigen Abfrage. Wäre einfacher gewesen.
Nochmals danke. Trezimann
Hallo
Mit nur einer einzigen Tabelle kann ich das nicht machen, da Tabelle1 immer existiert, Tabelle2 für jeden User erst angelegt und im Abmeldungsfall gelöscht wird. Die Tabellen sind nicht gleich, haben unterschiedliche Spalten.
Die Tabellen für die einzelnen Benutzer sind unterschiedlich aufgebaut oder sind es Tabelle1 und die nutzerabhängigen Tabellen (jeweils Tabelle2) unterschiedlich aufgebaut?
Tschö, Auge
Tach!
Mit nur einer einzigen Tabelle kann ich das nicht machen, da Tabelle1 immer existiert, Tabelle2 für jeden User erst angelegt und im Abmeldungsfall gelöscht wird. Die Tabellen sind nicht gleich, haben unterschiedliche Spalten.
Tabelle 1 ist nicht gleich zu Tabelle 2 oder sind die Tabellen 2 je Nutzer unterschiedlich?
Falls das zweite der Fall ist: Es gibt auch Datenmodelle, die können unterschiedlich viele Attribute pro Ding speichern. Zum Beispiel EAV - Entity-Attribute-Value. Dieses Modell ist zwar auch nicht mit simplen Statements abzufragen, dafür ist das Prinzip aber immer gleich.
Falls die Tabellen 2 immer gleich aufgebaut sind, gibt es eigentlich keinen Grund, die Daten nicht in derselben Tabelle abzulegen. Oder was war deine Grundlage für diese Entscheidung?
dedlfix.
Tabelle 2 ist für jeden User gleich und sieht so aus: Tabellenname: "liste_33", bedeutet, vorübergehende Tabelle solange Mitglied, für User mit ID 33
ID | userid_1 | zeichen_1 | zeichen_2
userid_1 enthält z.b. den Wert 75, zeichen_1 enthält z.B. 1 und bedeutet, dass User Nr.75 an User Nr.33 schon gemailt hat, zeichen_2 enthält 1 und heisst, es wurde geantwortet. nächste Spalte: userid_1 enthält wieder den Wert 75, zeichen_1 enthält aber 2 und heisst, dass User Nr.75 z.B. den Beitrag im Forum von User Nr.33 schon gelesen hat usw.
Beide Tabellen sind unterschiedlich aufgebaut. An anderen Stellen habe ich mit Tabelle2 schon gearbeitet. Läuft auch alles.
Löscht Mitglied Nr.33 seinen Account, kann Tabelle "liste_33" auch gelöscht werden, nicht aber Tabelle1, da dort alle Mitglieder mit Stammdaten erfasst sind.
In Tabelle2 weisst keine Spalte auf Tabelle1 hin, kann also nicht über andere Werte joinen. Und selbst wenn, müsste ich den Tabellennamen ermitteln.
Die Abfrage, die mir vorschwebt, sähe so aus:
gib mir die ID aus Tab1 wenn Tab1.wert1 = 0 UND Tab2.wert1 != 1
Lediglich der Name der Tabelle2 (Tab2) ist hierbei variabel und setzt sich eben aus der ID in Tabelle1 und dem Teil "liste_" zusammen.
Tach!
Tabelle 2 ist für jeden User gleich und sieht so aus: Tabellenname: "liste_33", bedeutet, vorübergehende Tabelle solange Mitglied, für User mit ID 33
ID | userid_1 | zeichen_1 | zeichen_2
Dann ist doch da bereits alles drin, um die Datensätze voneinander zu unterscheiden und es ist überhaupt kein Problem, die Daten aller Nutzer in derselben Tabelle 2 zu haben, oder?
Löscht Mitglied Nr.33 seinen Account, kann Tabelle "liste_33" auch gelöscht werden, nicht aber Tabelle1, da dort alle Mitglieder mit Stammdaten erfasst sind.
Ja, dann löscht man in Tabelle 2 alle Daten von User 33. Genauso einfach erledigt, wie die Tabelle des Users 33 zu löschen.
So wie du das jetzt machst, braucht deine Anwendung die Rechte, um Tabellen zu erstellen und zu löschen. Das ist unnötig mehr, als sie eigentlich bräuchte, wenn du nur eine Tabelle 2 für alle Mitglieder angelegt hättest. Dann würden lediglich Schreib-/Lese-rechte für Tabellendaten ausreichen.
Die Abfrage, die mir vorschwebt, sähe so aus:
gib mir die ID aus Tab1 wenn Tab1.wert1 = 0 UND Tab2.wert1 != 1
Lediglich der Name der Tabelle2 (Tab2) ist hierbei variabel und setzt sich eben aus der ID in Tabelle1 und dem Teil "liste_" zusammen.
Das Problem und die mögliche umständliche Lösung mit all ihren Nachteilen würde sich in Luft auflösen, wenn du nicht unnötig viele Tabellen anlegen würdest.
dedlfix.
Hallo dedlfix, Du hast nicht Unrecht! Anfangs habe ich natürlich über die Struktur des Projektes nachgedacht und mich für diese übersichtliche Lösung entschieden. Es gibt auch eine ähnliche Tabelle, in der der gesamte Emailverkehr für jedes Mitglied gespeichert ist. Posteingang, Postausgang, geantwortet ja - nein, usw. Dies in einer einzigen Tabelle zu speichern, bei tausenden von Nachrichten und hunderten gleichzeitigen Zugriffen auf eine Tabelle, halte ich nicht für gut. Nichts desto trotz denke ich über die Änderung wenigstens dieser einen Tabelle nach, um die es hier geht. Übrigens entscheidet nicht der User, ob er eine Tabelle anlegen darf. Dies geschieht automatisch bei Registrierung. Okay, ich werde mal weitermachen.
Nochmals danke für Eure Mühe. Trezimann
Hallo Trezimann,
Übrigens entscheidet nicht der User, ob er eine Tabelle anlegen darf.
Natürlich nicht, aber dein DBMS braucht das Recht eine Tabelle anlegen zu dürfen. Ein Dienst, der mit höheren Rechten fährt als nötig, stellt ein erhöhtes Sicherheitsrisiko dar.
Bis demnächst
Matthias
Hallo
Du hast nicht Unrecht! Anfangs habe ich natürlich über die Struktur des Projektes nachgedacht und mich für diese übersichtliche Lösung entschieden.
Angesichts der Probleme mit deiner Lösung würde ich unterstellen wollen, du hättest dich für die unübersichtliche Lösung entschieden.
Es gibt auch eine ähnliche Tabelle, in der der gesamte Emailverkehr für jedes Mitglied gespeichert ist. Posteingang, Postausgang, geantwortet ja - nein, usw. Dies in einer einzigen Tabelle zu speichern, bei tausenden von Nachrichten und hunderten gleichzeitigen Zugriffen auf eine Tabelle, halte ich nicht für gut.
Mal abgesehen von der allgemeinen Performanz der Maschine, auf der die Datenbank läuft, ist eine Datenbank genau dafür da, mit großen Datenbeständen strukturiert umzugehen. Warum sollte deine Lösung, hunderte gleichzeitige Zugriffe über -zig oder hunderte Tabellen mit dynamischer Festlegung der Namen der beteiligten Tabellen abzuwickeln, besser/schneller laufen, als wenn alle Abfragen über einige wenige Tabellen laufen?
Tschö, Auge
Tach!
Dies in einer einzigen Tabelle zu speichern, bei tausenden von Nachrichten und hunderten gleichzeitigen Zugriffen auf eine Tabelle, halte ich nicht für gut.
Es ist der Job eines DBMS viele Zugriffe zu bewältigen. Schnelles Zugreifen in eine große Datenmenge unterstützt man durch Indexe. Hast du denn mal probiert, ab welchen Datenmengen und Zugriffszahlen die Reaktionszeit in die Knie geht? Wenn die Last zu groß wird, gibt es geeignetere Lastverteilungsmaßnahmen als das Aufteilen der Daten in viele kleine jeweils extra zu verwaltende Einheiten.
Übrigens entscheidet nicht der User, ob er eine Tabelle anlegen darf. Dies geschieht automatisch bei Registrierung.
Schon klar, aber deine Anwendung braucht die Rechte dazu, genauer gesagt, die Kennung, mit der sich deine Anwendung am DBMS anmeldet. Und Anwendungen gibt man gern so wenig wie möglich Rechte, damit da keiner zu viel Unfug anstellen kann, wenn er eine Lücke findet.
dedlfix.
Meine Idee am Anfang war, wenn zwei Mitglieder miteinander über das PNS kommunizieren, werden nur zwei (kleinere) Tabellen benötigt, statt einer riesigen. Letztendlich kann man hunderte von Nachrichten speichern, ganze Verläufe. Noch läuft nichts im Netz, nur auf XAMPP. Mit grossen Zugriffszahlen kann ich so gesehen noch nicht experimentieren. Der Server, auf dem ich alles hochladen werde, sollte es aber packen. Eure Einwände leuchten mir ein. Es ist aber so, dass ich bereits mehr als 3/4 des Projektes fertiggestellt habe, mit weit über 100 Scripten. Jetzt nochmal alles umzuschreiben, wobei es ja funktioniert, würde zusätzliche wochenlange Arbeit bedeuten, wobei wiederum am Ende kein anderes Ergebnis herauskäme. In diesem Sinne, lasst mich erstmal machen. Sollte ich im weiteren Verlauf auf unüberwindbare Probleme stossen, werde ich das ein oder andere ändern MÜSSEN. Danke und bis dann.
Tach!
Meine Idee am Anfang war, wenn zwei Mitglieder miteinander über das PNS kommunizieren, werden nur zwei (kleinere) Tabellen benötigt, statt einer riesigen. Letztendlich kann man hunderte von Nachrichten speichern, ganze Verläufe. Noch läuft nichts im Netz, nur auf XAMPP. Mit grossen Zugriffszahlen kann ich so gesehen noch nicht experimentieren. Der Server, auf dem ich alles hochladen werde, sollte es aber packen.
Mit anderen Worten, du hast da optimiert, ohne dass sich die Stellen als problematisch gezeigt haben und du hast auch nicht probiert, ob diese Lösung besser als das übliche ist. Ich sag mal so: es gibt bessere Vorgehensweisen.
In diesem Sinne, lasst mich erstmal machen. Sollte ich im weiteren Verlauf auf unüberwindbare Probleme stossen, werde ich das ein oder andere ändern MÜSSEN.
Ich kann und will dir da nicht reinreden, ist ja dein Projekt. Und man kann ja auch aus Fehlern lernen, wie es nicht geht. Es ist nur schade, wenn man fertig ist und die konzeptionellen Probleme erst dann zutage treten. Wobei es auch gut sein kann, dass das DBMS mit vielen kleinen Tabellen ebensowenig Probleme hat wie mit einer großen. Da spielen am Ende so viele Faktoren rein, dass man das schlecht von außen prophezeihen kann.
dedlfix.
Hallo Trezimann,
Eure Einwände leuchten mir ein. Es ist aber so, dass ich bereits mehr als 3/4 des Projektes fertiggestellt habe, mit weit über 100 Scripten.
Ohne die Struktur deines Projektes im Detail zu kennen: Vielleicht solltest du langfristig zumindest die Datenbank-spezifischen Teile zentralisieren (Funktionen, ggf. Objektorientierung) und saubere Schnittstellen definieren, dann kannst du die leichter anpassen und warten – und ggf. auch gegen etwas völlig anderes austauschen.
Jetzt nochmal alles umzuschreiben, wobei es ja funktioniert, würde zusätzliche wochenlange Arbeit bedeuten, wobei wiederum am Ende kein anderes Ergebnis herauskäme.
... außer wartbarere Software und damit später eine Arbeitsersparnis bei der Erweiterung.
In diesem Sinne, lasst mich erstmal machen. Sollte ich im weiteren Verlauf auf unüberwindbare Probleme stossen, werde ich das ein oder andere ändern MÜSSEN.
Später, wenn so etwas bereits im Betrieb ist, wird sich nur mit großem Aufwand etwas grundlegend ändern lassen. Bedenke das.
Gruß
Julius