Erik: MySQL: mehrere Anfragen in eine

Hallo,
ist es IYO sinnvoll mehrere Anfragen der Art "SELECT a, b, c FROM d" (die nur eine Zeile zurückgeben) in eine a la "SELECT a, b, c, e.* FROM d, e" zu packen, oder bringt das auch nicht mehr?
Viele Grüße
Erik

  1. Meine Frage bezieht sich vor allem auf die Geschwindigkeit bei sehr häufiger Ausführung durch ein Skript.

    1. Meine Frage bezieht sich vor allem auf die Geschwindigkeit bei sehr häufiger Ausführung durch ein Skript.

      Wir hatten vor einiger Zeit (Jahresanfang 2002) hier mal den Fall, daß ein Provider ein Skript des Fragers lahmgelegt hatte, weil es in einer Stunde etwa 500.000 SELECT-Abfragen produziert hat. Argument: Andere wollen auch was von der Datenbank. Und das ist sehr richtig.

      Soll heißen: Wann immer es dir möglich ist, die Datenbank für eine Gruppe von Daten zu befragen, solltest du das machen. Die Datenbank muß dann nur einmal den Datenbestand durchforsten, und hat aber mehr Ergebnisse, die in den Ausgabepuffer kopiert und dann auf Anfrage ausgegeben werden. Wenn du immer nur eine Zeile erfaßt, dann muß bei jedem neuen SELECT wieder der gesamte Datenbestand durchforstet werden... ziemlich aufwendig, sowas. Unfair gegenüber anderen Nutzern der Datenbank, solltest du die DB nicht allein benutzen. Und von der Performance her in jedem Falle schlechter.

      - Sven Rautenberg

      1. Hi Sven,

        Wir hatten vor einiger Zeit (Jahresanfang 2002) hier mal den Fall, daß ein Provider ein Skript des Fragers lahmgelegt hatte, weil es in einer Stunde etwa 500.000 SELECT-Abfragen produziert hat. Argument: Andere wollen auch was von der Datenbank. Und das ist sehr richtig.

        Hast du zufällig die URL / den Titel oder den Autor dieses Threads, den würde ich mir nämlich gerne mal anschauen (konnte mit der Archiv-Suche nichts finden).

        »»Wenn du immer nur eine Zeile erfaßt, dann muß bei jedem neuen SELECT wieder der gesamte Datenbestand durchforstet werden... ziemlich aufwendig, sowas. [...] Und von der Performance her in jedem Falle schlechter.

        In meinem Beispiel wäre für jede Tabelle ein SELECT. Bringt das dann trotzdem einen Vorteil (von dem Ausgabepuffer einmal abgesehen)? Der zu "durchforstende" Datenbestand bleibt ja der gleiche.

        Viele Grüße
        Erik

        1. Moin nochmal, Erik!

          Hast du zufällig die URL / den Titel oder den Autor dieses Threads, den würde ich mir nämlich gerne mal anschauen (konnte mit der Archiv-Suche nichts finden).

          Den finde ich selbst auch nicht mehr. Vielleicht erinnert sich noch jemand anderes daran.

          In meinem Beispiel wäre für jede Tabelle ein SELECT. Bringt das dann trotzdem einen Vorteil (von dem Ausgabepuffer einmal abgesehen)? Der zu "durchforstende" Datenbestand bleibt ja der gleiche.

          Das macht aber nichts. Suchen bleibt suchen. Außerdem ändert sich sicherlich das Select-Statement, denn du willst ja nicht immer die gleiche Zeile haben, oder?

          Natürlich wird ein schlaues DBMS alle möglichen Cache-Strategien verfolgen - das muß man ja aber nicht ausreizen, denn das bietet für den täglichen Anwendungsfall Reserven.

          - Sven Rautenberg

          1. Moin,

            In meinem Beispiel wäre für jede Tabelle ein SELECT. Bringt das dann trotzdem einen Vorteil (von dem Ausgabepuffer einmal abgesehen)? Der zu "durchforstende" Datenbestand bleibt ja der gleiche.

            Das macht aber nichts. Suchen bleibt suchen. Außerdem ändert sich sicherlich das Select-Statement, denn du willst ja nicht immer die gleiche Zeile haben, oder?

            So wie ich Erik verstanden habe macht das sehr wohl was. Er will jeweils eine Zeile aus zwei Tabellen haben und hat gefragt ob er nun (vereinfacht)
            SELECT * FROM a; SELECT * FROM b;
            oder
            SELECT * FROM a,b;
            machen soll. Letzteres ist aber (gelinde gesagt) suboptimal was die Performance angeht. Das DBMS würde dann nämlich ein Kreuzprodukt der beiden Tabellen bilden und dann darin suchen. Wenn jede der Tabellen 10 Zeilen hat (lächerlich wenig) würde die erste Variante zum Durchsuchen von 20 Zeilen führen, die zweite Variante zum Durchsuchen von 100 Zeilen. Da ist doch ein kleiner Unterschied, gell?

            --
            Henryk Plötz
            Grüße von der Ostsee

            1. Moin!

              So wie ich Erik verstanden habe macht das sehr wohl was. Er will jeweils eine Zeile aus zwei Tabellen haben und hat gefragt ob er nun (vereinfacht)
              SELECT * FROM a; SELECT * FROM b;
              oder
              SELECT * FROM a,b;
              machen soll. Letzteres ist aber (gelinde gesagt) suboptimal was die Performance angeht.

              Ich glaub' ich kann nicht mehr so recht gucken, oder ich hab zu schnell gelesen. Du hast natürlich Recht: So ein JOIN wäre eher unpassend, weil ziemlich aufwendig. Es gibt meist bessere JOINs, die zunächst die relevanten Zeilen aus Tabelle 1 finden und dann mit den relevanten Zeilen aus Tabelle 2 verknüpfen. Sowas macht mehr Sinn. :)

              Ok, also Kommando teilweise zurück:
              SELECT a FROM b WHERE c=1
              SELECT a FROM b WHERE c=2
              ...
              macht keinen Sinn, aber

              SELECT a.*,b.* from a,b WHERE c...
              ist unter Umständen genauso böse. Es hängt aber wirklich von der Art der Abfrage ab, ob das Sinn macht oder nicht! Zwei nicht zusammenhängende Tabellen ohne logischen Bezug fragt man nicht so ab. Wenn hingegen die Tabellen verknüpft sind, kriegt man diese Bezüge entweder durch viele einzelne SELECTs und Programmcode oder durch einen einzigen JOIN aus der Datenbank abgefragt - und was da besser ist, kann nur ein Test sagen. Ich tendiere aber dazu, JOINs zu verwenden, weil die Datenbank für die Datenhaltung und Auslieferung zuständig ist, und nicht ich mit meinem Programm.

              - Sven Rautenberg

              1. Tach Sven,

                So wie ich Erik verstanden habe macht das sehr wohl was. Er will jeweils eine Zeile aus zwei Tabellen haben und hat gefragt ob er nun (vereinfacht)
                SELECT * FROM a; SELECT * FROM b;
                oder
                SELECT * FROM a,b;
                machen soll. Letzteres ist aber (gelinde gesagt) suboptimal was die Performance angeht.

                Ich glaub' ich kann nicht mehr so recht gucken, oder ich hab zu schnell gelesen. Du hast natürlich Recht: So ein JOIN wäre eher unpassend, weil ziemlich aufwendig. Es gibt meist bessere JOINs, die zunächst die relevanten Zeilen aus Tabelle 1 finden und dann mit den relevanten Zeilen aus Tabelle 2 verknüpfen. Sowas macht mehr Sinn. :)

                Ok, also Kommando teilweise zurück:
                SELECT a FROM b WHERE c=1
                SELECT a FROM b WHERE c=2
                ...
                macht keinen Sinn, aber

                SELECT a.*,b.* from a,b WHERE c...
                ist unter Umständen genauso böse. Es hängt aber wirklich von der Art der Abfrage ab, ob das Sinn macht oder nicht! Zwei nicht zusammenhängende Tabellen ohne logischen Bezug fragt man nicht so ab.

                Also verknüpft sind sie schon (siehe http://forum.de.selfhtml.org/?m=48310&t=8632) aber es ist auch nicht viel schwieriger sie einzeln abzufragen. Ist die Tatsache, dass ich zwei SELECTs spare deiner Ansicht nach Vorteil genung?

                Viele Grüße
                Erik

                1. Moin!

                  Also verknüpft sind sie schon (siehe http://forum.de.selfhtml.org/?m=48310&t=8632) aber es ist auch nicht viel schwieriger sie einzeln abzufragen. Ist die Tatsache, dass ich zwei SELECTs spare deiner Ansicht nach Vorteil genung?

                  Siehe meine Antwort dort... (</?m=48310&t=8632>)

                  - Sven Rautenberg

            2. N'abend,

              So wie ich Erik verstanden habe macht das sehr wohl was. Er will jeweils eine Zeile aus zwei Tabellen haben und hat gefragt ob er nun (vereinfacht)
              SELECT * FROM a; SELECT * FROM b;
              oder
              SELECT * FROM a,b;
              machen soll. Letzteres ist aber (gelinde gesagt) suboptimal was die Performance angeht. Das DBMS würde dann nämlich ein Kreuzprodukt der beiden Tabellen bilden und dann darin suchen. Wenn jede der Tabellen 10 Zeilen hat (lächerlich wenig) würde die erste Variante zum Durchsuchen von 20 Zeilen führen, die zweite Variante zum Durchsuchen von 100 Zeilen. Da ist doch ein kleiner Unterschied, gell?

              Also konkret sähe mein Anfrage folgendermaßen aus:
              SELECT a.a, a.b, b.*, c.* FROM a, b, c WHERE a.a='x' AND a.b=b.b AND a.b=c.b

              Das gibt dann eine Zeile zurück. Das sollte doch auch kein größerer Aufwand sein, als das ganze mit drei SELECT zu machen, oder? Aber bringt es nun einen Vorteil oder nicht?

              Viele Grüße
              Erik

              1. Hi Erik,

                Also konkret sähe mein Anfrage folgendermaßen aus:
                SELECT a.a, a.b, b.*, c.* FROM a, b, c WHERE a.a='x'
                AND a.b=b.b AND a.b=c.b

                Das gibt dann eine Zeile zurück. Das sollte doch auch
                kein größerer Aufwand sein, als das ganze mit drei
                SELECT zu machen, oder?

                Mir ist völlig unklar, wie Du das mit drei SELECTs semantisch äquivalent hinkriegen willst.
                (Jedes Deiner drei Statements kann beliebig viele Treffer liefern - und die müßtest Du dann in Deinem Anwendungsprogramm ausmultiplizieren. Das allerdings ist nicht der Sinn von SQL.)

                Es sei denn, b ist unique in _jeder_ Deiner Tabellen - dann allerdings wäre Deine relationale Zerlegung untauglich, weil dann alle Daten in eine einzige Tabelle gehören (so daß sich Deine Frage gar nicht mehr stellen würde).

                Viele Grüße
                      Michael

                1. Hallo Michael,

                  Das gibt dann eine Zeile zurück. Das sollte doch auch
                  kein größerer Aufwand sein, als das ganze mit drei
                  SELECT zu machen, oder?

                  Mir ist völlig unklar, wie Du das mit drei SELECTs semantisch äquivalent hinkriegen willst.
                  (Jedes Deiner drei Statements kann beliebig viele Treffer liefern - und die müßtest Du dann in Deinem Anwendungsprogramm ausmultiplizieren. Das allerdings ist nicht der Sinn von SQL.)

                  Es sei denn, b ist unique in _jeder_ Deiner Tabellen - dann allerdings wäre Deine relationale Zerlegung untauglich, weil dann alle Daten in eine einzige Tabelle gehören (so daß sich Deine Frage gar nicht mehr stellen würde).

                  'b' ist unique. Aber die Zerlegung in die drei Tabellen hat durchaus ihren Sinn. Denn nicht nicht für jedes 'b' sind in allen drei Tabellen Werte. In einer Tabelle sind zudem (sofern sie vorhanden sind) sehr große Werte und eine Tabelle hat (sehr kleine) Werte die sehr oft geändert werden müssen. Und dann ist es doch ein Vorteil, wenn ich eine 'kleine' Zeile habe, die geändert werden muss, die vielleicht 1 kb hat, als wenn ich eine Zeile ändere die ca. 50 kb hat, oder sehe ich das falsch?

                  Viele Grüße
                  Erik

                  1. Hi Erik,

                    'b' ist unique. Aber die Zerlegung in die drei Tabellen hat
                    durchaus ihren Sinn.
                    Denn nicht nicht für jedes 'b' sind in allen drei Tabellen
                    Werte.

                    um das zu modellieren, gibt es den Wert NULL und die Eigenschaft einer Spalte, NULL-Werte aufzunehmen.
                    (Das wird üblicherweise über ein zusätzliches FLAG pro Spalte realisiert, kostet also ein bißchen Platz.)

                    In einer Tabelle sind zudem (sofern sie vorhanden sind) sehr
                    große Werte und eine Tabelle hat (sehr kleine) Werte die sehr
                    oft geändert werden müssen.
                    Und dann ist es doch ein Vorteil, wenn ich eine 'kleine' Zeile
                    habe, die geändert werden muss, die vielleicht 1 kb hat, als
                    wenn ich eine Zeile ändere die ca. 50 kb hat, oder sehe ich
                    das falsch?

                    Ich würde sagen, das ist Aufgabe des RDBMS, dies effizient umzusetzen.
                    Natürlich solltest Du dann nicht mit SELECT * alle Felder holen, wenn Du nur einzelne Felder änderst.
                    Aber die anderen Felder sollten von einem UPDATE einer einzelnen Spalte eigentlich nicht betroffen sein (idealerweise).

                    Mag sein, daß ein konkretes Datenbanksystem (oder ein konkreter Tabellentyp!) davon profitieren würde, wenn Du die am häufigsten geänderte Spalte als letzte deklarierst - falls die Werte aller Spalten einer Zeile gepackt hintereinander in einem Speicherbereich abgelegt werden und dies in der Reihenfolge der Spalten-Deklarationen, dann müßten in diesem Falle zumindest nur sehr selten unveränderte Feldinhalte verschoben werden.

                    Also: Solange Du keine irre hohen Performance-Ansprüche hast, würde ich auf eine Zerlegung in mehrere Tabellen erst mal verzichten. (Es sei denn, der Performance Tuning Guide Deines RDBMS rät Dir explizit dazu, es so zu machen ...)

                    Viele Grüße
                          Michael

                    1. Hi Michael,

                      um das zu modellieren, gibt es den Wert NULL und die Eigenschaft einer Spalte, NULL-Werte aufzunehmen.
                      (Das wird üblicherweise über ein zusätzliches FLAG pro Spalte realisiert, kostet also ein bißchen Platz.)

                      Ist mir schon klar, dass ich nicht zwangsläufig jede Spalte immer ausgefüllt haben muss. Es erschien mir irgendwie besser, die Spalten die nicht immer ausgefüllt sind in eine eigene Tabelle auszulagern, aber vielleicht ändere ich das auch wieder.

                      In einer Tabelle sind zudem (sofern sie vorhanden sind) sehr
                      große Werte und eine Tabelle hat (sehr kleine) Werte die sehr
                      oft geändert werden müssen.
                      Und dann ist es doch ein Vorteil, wenn ich eine 'kleine' Zeile
                      habe, die geändert werden muss, die vielleicht 1 kb hat, als
                      wenn ich eine Zeile ändere die ca. 50 kb hat, oder sehe ich
                      das falsch?

                      Ich würde sagen, das ist Aufgabe des RDBMS, dies effizient umzusetzen.
                      Natürlich solltest Du dann nicht mit SELECT * alle Felder holen, wenn Du nur einzelne Felder änderst.
                      Aber die anderen Felder sollten von einem UPDATE einer einzelnen Spalte eigentlich nicht betroffen sein (idealerweise).

                      MySQL gibt immer 'Rows changed' oder sowas zurück, daraus habe ich geschlossen, dass immer die ganze Zeile neu geschrieben wird.

                      Viele Grüße und danke für deine Mühe,
                      Erik

                      1. Hi Erik,

                        MySQL gibt immer 'Rows changed' oder sowas zurück, daraus habe
                        ich geschlossen, dass immer die ganze Zeile neu geschrieben
                        wird.

                        das würde ich nicht daraus schließen.

                        Ich schließe daraus nur, daß mySQL vor dem Ändern einer Zeile definitiv prüft (und das Ergebnis dieser Prüfung am Ende summarisch anzeigt), ob der zu setzende Wert in der jeweiligen Spalte überhaupt eine Änderung bewirkt - falls nicht, kann es sich das Zurückschreiben nämlich komplett sparen.

                        Wie das Zurückschreiben dann im Einzelnen funktioniert (feldweise, zeilenweise, bedingt etc.), dazu müßte man die entsprechende Dokumentation des Tabellentreibers lesen (oder gar den Quelltext desselben).

                        Viele Grüße
                              Michael

              2. Moin!

                Also konkret sähe mein Anfrage folgendermaßen aus:
                SELECT a.a, a.b, b.*, c.* FROM a, b, c WHERE a.a='x' AND a.b=b.b AND a.b=c.b

                Das gibt dann eine Zeile zurück. Das sollte doch auch kein größerer Aufwand sein, als das ganze mit drei SELECT zu machen, oder? Aber bringt es nun einen Vorteil oder nicht?

                Bei dieser Anfrage passiert folgendes: Es wird die gesamte Tabelle A genommen, jede Zeile dieser Tabelle wird verknüpft mit _allen_ Zeilen der Tabelle B (die entstehende Tabelle hat also soviele Zeilen, wie Tabelle A multipliziert mit Tabelle B Zeilen hat). Jede Zeile dieser Tabelle wird dann noch mit jeder Zeile der Tabelle C verknüpft, so daß am Ende eine Tabelle entstanden ist, die A x B x C Zeilen hat. Wenn jede deiner eizelnen Tabellen zehn Zeilen hat, dann entsteht eine Tabelle mit 10x10x10=1000 Zeilen. Hat jede Tabelle 100 Zeilen, entsteht eine Tabelle mit 100x100x100 = 1.000.000 Zeilen.

                Und erst dann wird diese Tabelle durchsucht nach der Bedingung, die bei WHERE angegeben ist.

                Ein Riesenaufwand, um die Tabelle aufzubauen - vor allem, wenn hinterher nur eine Zeile zu finden ist. Eine Million Zeilen durchsuchen, um eine Zeile zu finden, obwohl nur ein Kriterium (nämlich a.a='x') geprüft wird, ist Wahnsinn.

                Aber man kann es besser machen:

                SELECT a.a, a.b, b.*, c.* FROM a JOIN b ON a.b=b.b JOIN c ON a.b=c.b WHERE a.a='x'

                Hier wird zunächst die komplette Tabelle A genommen, und jede Zeile wird nur mit den Zeilen aus Tabelle B verknüpft, für die die erste Bedingung hinter dem ersten ON zutrifft (wenn Spalte .b eine ID ist, werden als Ergebnis kaum mehr Zeilen rauskommen, als Tabelle A Zeilen hat). Die resultierende Tabelle wird auf die gleiche Weise mit Tabelle C verknüpft, was ebenfalls nicht mehr Zeilen ergeben dürfte, als Tabelle A ursprünglich Zeilen hatte.

                Dann wird geschaut, wo die WHERE-Bedingung zutrifft. Statt einer Million Zeilen werden aber nur hundert Zeilen durchsucht (wenn die Annahmen von vorhin noch gelten). Bei großen Tabellen (stell' dir nur vor, du hast nicht 100 Zeilen in jeder Tabelle, sondern 100.000 oder eine Million) macht das einen entscheidenden Unterschied. ;)

                Ansonsten ist eventuell zu prüfen, ob nicht drei SELECT-Statements schneller sind, als ein dreifacher JOIN. Richtig relevant wird das aber erst, wenn es um Performance geht (sollte deine Abfrage wirklich häufig vorkommen und große Tabellen durchsuchen).

                - Sven Rautenberg

                1. Hallo Sven,

                  Ein Riesenaufwand, um die Tabelle aufzubauen - vor allem, wenn hinterher nur eine Zeile zu finden ist. Eine Million Zeilen durchsuchen, um eine Zeile zu finden, obwohl nur ein Kriterium (nämlich a.a='x') geprüft wird, ist Wahnsinn.

                  Da hast du mich wohl vor einem großen Fehler bewahrt! Mir war der Unteschied zwischen einer solchen Anfrage und einem JOIN nicht bewusst (entweder kam das in meinem MySQL-Buch nicht vor oder ich habe es überlesen).

                  Ansonsten ist eventuell zu prüfen, ob nicht drei SELECT-Statements schneller sind, als ein dreifacher JOIN. Richtig relevant wird das aber erst, wenn es um Performance geht (sollte deine Abfrage wirklich häufig vorkommen und große Tabellen durchsuchen).

                  Das werde ich dann mal machen.

                  Viele Grüße und danke (auch Hendrik),
                  Erik