mixmastertobsi: langsame MySQL Abfrage

Hallo,

ich habe eine SQL-Abfrage, welche etwas langsam ist, weil hier bei jedem Aufruf ein UPDATE gemacht wird.

UPDATE auftrag 
SET gesamtpreis = (SELECT SUM(auftrag_artikel.menge*auftrag_artikel.preis) FROM auftrag_artikel WHERE auftrag_artikel.auftragnr=auftrag.auftragnr) + auftrag.gutschein + auftrag.rabatt + auftrag.versandkosten)
WHERE auftrag.auftragnr='$id'

Grundsätzlich ändert sich hier ja nicht permanent etwas, weshalb ich nun das Script umgeschrieben habe.

Als erstes wird per SELECT die Summe errechnet und wenn diese zum Gesamtpreis unterschiedlich ist, soll ein UPDATE gemacht werden.

Meine Frage hierzu. Den Abgleich, ob die Summe unterschiedlich ist, mache ich in PHP mit IF(). Gibt es eine Möglichkeit, dies ohne PHP elegant zu lösen und das NUR das Update gemacht wird, wenn errechnete Summe und Gesamtpreis unterschiedlich ist?!?

  1. Tach!

    Gibt es eine Möglichkeit, dies ohne PHP elegant zu lösen und das NUR das Update gemacht wird, wenn errechnete Summe und Gesamtpreis unterschiedlich ist?!?

    Es gibt Stored Procedures.

    dedlfix.

  2. Moin,

    In mySQL gibt es auch CASE-Statements und IF-Konstrukte. Das sollte helfen. Ansonsten bitte nachschauen ob alles ordentlich indiziert ist. Speziell auftrag_artikel.auftragnr, auftrag.auftragnr, auftrag.gutschein, auftrag.rabatt und auftrag.versandkosten.

    Noch was anderes:

    WHERE auftrag.auftragnr='$id'

    Das hast du wirklich so in deinem Code? Wo kommt die ID her? hier gibt es unter Umständen Angriffsfläche für SQL-Injection!

    Gruß Bobby

    --
    -> Für jedes Problem gibt es eine Lösung, die einfach, sauber und falsch ist! <- ### Henry L. Mencken ### -> Nicht das Problem macht die Schwierigkeiten, sondern unsere Sichtweise! <- ### Viktor Frankl ### ie:{ br:> fl:{ va:} ls:< fo:) rl:( n4:( de:> ss:) ch:? js:( mo:} sh:) zu:)
    1. Hallo Bobby,

      die ID kommt von intern. Kann also nicht durch eine Usereingabe manipuliert werden.

    2. Also ich habe es jetzt mal probiert, doch leider gelingt es mir nicht.

      Kannst Du mir mal ein simples Beispiel machen, wie ein UPDATE aussehen kann. Es soll nur der UPDATE-Befehl ausgeführt werden, wenn der WErt nicht gleich ist.

      1. Tach!

        Also ich habe es jetzt mal probiert, doch leider gelingt es mir nicht.

        Was auch immer du gemacht hast ...

        Kannst Du mir mal ein simples Beispiel machen, wie ein UPDATE aussehen kann.

        Nicht nur ein Update, du brauchst eine Stored Procedure. In der steht dann unter anderem auch das Update.

        Es soll nur der UPDATE-Befehl ausgeführt werden, wenn der WErt nicht gleich ist.

        Das geht so ähnlich wie in PHP, nur eben als Stored Procedure im DBMS.

        dedlfix.

        1. Aber wie lege ich solch etwas an? Das ist ja im DB System implementiert und wird nicht wie bei einer php im Texteditior bearbeitet.

          1. Tach!

            Aber wie lege ich solch etwas an? Das ist ja im DB System implementiert und wird nicht wie bei einer php im Texteditior bearbeitet.

            Das Anlegen geschieht über ein Statement. Nach demselben Prinzip, wie eine andere Dinge, wie beispielsweise Tabellen, angelegt werden. Die Syntax dazu steht in der MySQL-Dokumentation.

            dedlfix.

          2. Hello,

            kennst Du Heidi-SQL? Das Tool benutze ich gerne, auch übers Internet, da selbstverständlich nur mit TLS-Verschlüsselung und Anmeldung per SSH-Key.

            Und dann kannst Du auch nachher nachkucken, wie das Create-Statement lautet, oder der Triggertext, oder die Stored Routine.

            Du musst aber trotz Stored Routines (Triggers & Procedures) immer an die Nebenläufigkeit denken. Das ganze Datenmodell muss möglichst so schlau aufgebaut werden (Stammdaten, Bewegungsdatein, Konfigurationsdaten), dass man keine Tabellen speren muss. Bei InnoBD kannst Du auch mit Satzsperren arbeiten.

            Unterscheiden solltest Du zwischen Roundturn-abhängigen "Select - view/edit - Update"-Vorgängen und solchen, die in einem Zug (ohne Roundturn zum Client) oder der API (PHP) im DMBS durchführbar sind. Letztere sind zu bevorzugen.

            Bei Roundturn-abhängingen Vorgängen arbeitet man dann eher nicht mit Satz- oder Tabellensperren, sondern mit Conflict-Countern o.ä.

            Liebe Grüße
            Tom S.

            --
            Es gibt nichts Gutes, außer man tut es
            Andersdenkende waren noch nie beliebt, aber meistens diejenigen, die die Freiheit vorangebracht haben.
            1. Danke Dir, aber bei deinen Ausführungen verstehe ich erstmals viel "Bahnhof" 😉 HeidiSQL kann ich dann wie PHPMyAdmin verstehen - richtig?

              Mein MySQL beschränkt sich halt auf einfachen Abfragen mit ein paar JOINS und ein paar Funktionen.

              Von Procedures, Stammdaten, Bewegungsdatein habe ich noch wenig gehört 😟

              1. Tach!

                Von Procedures, Stammdaten, Bewegungsdatein habe ich noch wenig gehört 😟

                Es kann übrigens auch gut sein, dass dein Problem sich nicht durch die Klärung deiner Eingangsfrage lösen lässt. Du suchst da eine Lösung, von der du annimmst, dass sie dein Problem löst. Aber hast du es denn überhaupt richtig analysiert? Welche Stelle konkret ist denn der Flaschenhals in deinem Szenario? Mit dem IF bekommst du nur das UPDATE ausgeklammert. Wenn der Zeitfresser im SELECT steckt, kommst du damit nicht wirklich weiter.

                dedlfix.

                1. Also wenn ich die Abfragen getrennt mache (also erst die SELECT, dann mit PHP vergleichen und dann, wenn unterschiedlich, das Update) ist es in jedem Fall wesentlich schneller.

                  1. Tach!

                    Also wenn ich die Abfragen getrennt mache (also erst die SELECT, dann mit PHP vergleichen und dann, wenn unterschiedlich, das Update) ist es in jedem Fall wesentlich schneller.

                    Fehlt da vielleicht ein Index auf der Auftragsnummer? Lass dir mal das EXPLAIN anzeigen.

                    dedlfix.

                    1. Nein, index bei der AUftragsnummer passt. Wie gesagt - das Problem besteht nur, wenn ich ein UPDATE machen möchte.

                      1. Tach!

                        Nein, index bei der AUftragsnummer passt. Wie gesagt - das Problem besteht nur, wenn ich ein UPDATE machen möchte.

                        Dann frag mal das EXPLAIN, ob es dir problematische Stellen offenbaren kann.

                        dedlfix.

  3. Gewinnst Du denn dadurch tatsächlich etwas?

    In beiden Fällen muss die Auftrag- und Auftrag_Artikel Tabelle gelesen und die neue Summe berechnet werden. Darauf folgt dann ein Update. Nach Alt in jedem Fall, nach Neu nur bei Änderung.

    Der Schreibvorgang sollte den kleinsten Teil des Aufwandes ausmachen.

    Lasse einen EXPLAIN laufen und prüfe, ob Dir Indexe fehlen. Die Query sollte bei korrekter Indexierung nur dann langsam sein, wenn Du massenhaft Artikel am Auftrag hast.

    Welche DB-Engine verwendest Du? MyISAM macht Table-Locks, keine Row-Locks, d.h. eine Anwendung, die viele User hat, die viele Updates machen, kann sich an den Sperren festfressen. DA kann deine Aufteilung sinnvoll sein. Trotzdem läufst Du dann Gefahr, inkonsistente Daten zu erhalten.

    1. Query (von Dir): neuen Gesamtpreis ermitteln
    2. Query (von anderem Benutzer): Rabatt ändern
    3. Query (von Dir): Gesamtpreis schreiben

    Ein einzelner Update ist atomar, da kann die 2. Query sich nicht dazwischen schieben. Trennung in SELECT und UPDATE wird nur dann atomar behandelt, wenn Du eine Transaktion mit passendem Isolation Level verwendest. Dafür brauchst Du allerdings InnoDB, das wird von MyISAM nicht unterstützt.

    Rolf