Announcement

Collapse
No announcement yet.

Suche Tipps: Lange Queries modularisieren und dabei trotzdem schnell abfragen?

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Suche Tipps: Lange Queries modularisieren und dabei trotzdem schnell abfragen?

    Hallo zusammen,

    0. Handarbeit

    ich habe immer wieder recht aufwendige Einzelqueries für Auswertungen. D.h. eine einzelne große/komplexe Abfrage, die an den Server geschickt wird. Zurück kommt ein einzelnes Resultset, in dem dann die fertige Auswertung steckt. Die Performance ist gut und ich bin "eigentlich" zufrieden...

    Was dabei nicht so gut ist, ist die Übersichtlichkeit solcher Abfragen und die Wiederverwendbarkeit für andere/ähnliche Auswertungen. Bislang kopiere ich eine bestehende, ähnliche Auswertung und passe sie dann an. Das geht - ist aber nicht so toll. Ich hätte die Sache gerne modularer. D.h. wiederkehrende Teile werden einmal geschrieben und gut getestet. Später kann man diese dann einbinden/verwenden und alles wird übersichtlicher und sauberer. Soweit die Theorie. Was kann man alles machen? Mal schauen:

    1. STORED FUNCTIONs

    Bestimmte Teile habe ich probeweise in Funktionen ausgelagert. Als einfaches Beispiel: IstSaSoFeiertag(datum). Zurück kommt TRUE/FALSE (1/0). Wie der Name vermuten lässt, prüft diese Funktion, ob das übergebene Datum den Wochentag Samstag oder Sonntag hat und schaut in einer Feiertagstabelle nach, ob in "unserem" Bundesland das Datum auf einen Feiertag fällt. Bislang frage ich das über eine Subquery und WEEKDAY() direkt ab. Das geht genauso, ist aber länger/umständlicher und man kopiert dieses Codefragment umher. Noch schlimmer wird es, wenn Teile X-fach benötigt werden. Z.B. sollen 10 Spalten auf bestimmte Randbedingungen geprüft werden (z.B. für eine nachfolgende Gruppierung/Filterung). Dann wiederholt sich der fast gleiche Code 10-Mal. Und wenn man dann noch Spalte1 * Spalte2 AS Gesamt braucht, dann multipliziert man "zwei Würste" miteinander. Super unübersichtlich. In der Praxis mache ich dann extra Subqueries (bzw. CTEs), nur um mit den Alias-Namen weiter arbeiten zu können. Daher: eine Funktion als Rettung.

    Man nimmt dann einfach eine Funktion mit dem Spaltennamen und ein paar notwendigen Parametern und gut ist. Das sieht kompakt und sauber aus, bekommt einen sprechenden Funktionsnamen und man muss nur einmal programmieren (DRY - don't repeat yourself). Alles ist gut. Sollte man meinen...
    ...bis man auf die jämmerliche Performance stößt. Solange die Funktion nur "wenige" Male aufgerufen wird, ist das super. Wenn man aber sehr viele Datensätze damit bearbeitet, dann bremst das extrem. Zudem kann MySQL dann die verknüpften Indizes nicht mehr benutzen, solange eine Funktion drauf liegt. Resultat bei mir: Faktor 100 bis 1000 langsamer. D.h. das kann ich nicht machen (leider, leider, leider!!!).

    2. VIEWs

    Wiederkehrende Teile werden in VIEWs gepackt und in der Auswertung muss nur noch mit den VIEWs gearbeitet werden, die alle Daten sinnvoll vorbereiten. Funktioniert, ist übersichtlich und wiederverwendbar. Problem gelöst... sollte man meinen. Problem again: Performance. Es gibt z.B. (noch) keine Indices auf Views bei MySQL. Bei einfacheren Queries geht sogar noch alles glatt (schnell), weil die darunterliegenden Indices (von den verknüpften Tabellen) genutzt werden können. Wenn es komplizierter wird, dann fängt MySQL mit nested subselects an, produziert dafür temporäre Tabelle(n) und die sind dann ohne Indizes => langsam. Auch haben VIEWs gewisse Einschänkungen, die es ohne VIEWs nicht gibt. Bis vor kurzem (vor MySQL 5.7.irgendwas) gingen z.B. nicht mal Subqueries im "FROM". Aktuell gibt's wohl auch noch Einschränkungen bei CTEs. Wie auch immer - es ist kein 1:1 Ersatz.

    3. Materialized Views

    Wäre eine Alternative, da man dort (je nach DB) Indizes benutzen kann und alles schon vorgecached bereit liegt. MySQL kann das leider nicht. Ich habe das mal manuell gemacht (CREATE TABLE test_materialized_view AS SELECT * FROM test_view / CREATE INDEX ... ON ... etc. pp.) und die Performance ist super. Man hat aber wieder ein komplexeres Szenario und muss sich Gedanken machen, wann und wie oft die Materialized View aktualisiert wird. Und wenn das Aktualisieren mal nicht funktioniert, hat man schnell total veraltete Daten). Es gib noch so Bastelzusätze wie "Flexviews", um Materialized Views in MySQL nachzurüsten. Aber will/muss man sich das wirklich antun? Das ist alles nicht so attraktiv.

    4. STORED PROCEDUREs die SQL-Query-Strings erstellen die mit PREPARE/EXECUTE ausgeführt werden

    Weitere Idee: Man arbeitet zwar mit Prozeduren und Funktionen, aber diese erstellen nur einen SQL-Query-String, der anschließend an PREPARE und EXECUTE verfüttert wird. Vorteil: Man kann wie oben beschrieben sauber mit Funktionen arbeiten, diese erstellen aber den "ausführlichen" SQL Quelltext, der sich dann auch X-Mal in ähnlicher Form wiederholen darf (wie oben beschrieben). Das Ergebnis ist dann identisch schnell wie bei der handgeschriebenen SQL-Query. Problem gelöst. Oder? Na ja... das macht die Sache wieder extra kompliziert, da man bei Fehlern nicht die entsprechende Stelle findet, sondern erst mal rückwärts suchen muss, von welchem Code das erstellt wurde und dann wiederum dort nachschauen muss, warum das so erstellt wurde. Etc. pp. Klingt nicht nach Spaß! Abgesehen davon hatte ich bei meinen Tests festgestellt, dass mit PREPARE nicht alles umsetzbar ist, was nativ geht (wegen Bug und Feature-Einschränkungen).

    5. Anderes DBMS

    Ein anderes DBMS verbinden mit mehr Features und dann damit auswerten. Z.B. kann man in Oracle mit "CREATE DATABASE LINK ..." eine andere Datenbank (also MySQL in meinem Fall) als lokales Schema bereitstellen und dann ganz normal damit arbeiten, als lägen die Daten auf der Oracle-Datenbank. Ob das aber von der Geschwindigkeit her überhaupt sinnvoll ist, weiß ich nicht. Aber es wäre möglich... ginge sicherlich... aber... hm... ich weiß nicht so recht...

    6. Include von SQL-Dateien

    Man bindet andere SQL-Quellen an den entsprechenden Stellen ein als "Includes".

    Code:
    In der main.sql:
    
    source include/sub1.sql
    source include/sub2.sql
    source include/sub3.sql
    Das geht dann aber nur mit dem mysql-CLI-Tool und ... ist etwas... na ja... grobschlächtig.

    7. Skripte (Shell/Batch) verbinden SQL-Files

    Wie bei 6. nur dass als Output eine einzelne SQL-Datei übrig bleibt, die ganz normal verwendet werden kann (außerhalb vom mysql-CLI-Tool). Oder Holzhammer: cat include/sub*.sql > main.sql

    8. Anderweitige Programmiersprache erstellt SQL-Queries via Templates

    Sowas wie PHP, Python, Java... erstellt die Queries und führt sie dann aus. Gibt sich nicht viel zu Punkt 4. Man könnte höchstens über Templates nachdenken, die via DSL (domain specific language) die SQL-Query zusammen bauen. (klingt auch total easy...gib mir 5 Minuten, dann läuft alles für IMMER)

    9. Snippets via IDE

    Bestimmte Teile werden in einer IDE als "Snippets" (Codefragmente als Vorlage) abgelegt und dann entsprechend an den gewünschten Stellen eingefügt. Besser als Copy+Paste aber an sich auch nicht wirklich sauber/schön.

    10. Ich besinne mich zurück und kehre reumütig zu Punkt 0 zurück - denn früher ging es ja auch und war sowieso alles besser! ;-D

    Wie macht ihr das? Gibt es noch geschicktere Methoden oder habt ihr noch andere Ideen für mich? Es soll keine Doktorarbeit daraus werden, aber die jetzige Methode (Punkt 0) ist doch sehr rudimentär und ich möchte sehr gerne zukünftig etwas einfacher/übersichtlicher arbeiten, OHNE deutlich länger als jetzt als auf das Auswertungsergebnis warten zu müssen. Ich brauche lediglich die Quadratur des Kreises. Mehr verlange ich ja gar nicht! Mensch jetzt sagt doch auch mal was!

  • #2
    Erstmal als ganz allgemeine Antwort:

    1. Mein Weg wären Views, ausnahmslos, als erster Schritt. Fraglich wäre ggF. wie dynamisch sich Feldnamen oder so ändern, und es damit eben nur Namensänderungen wären, oder ob sich auch Strukturen stark unterscheiden und wie gut man das dann in verschiedene Grundmuster(Basisviews) zusammenfassen kann,
    2.Wenn man das geregelt hat, kann man / will man vielleicht irgendwo Performance optimieren und würde mit Indizierung und Materialized Views arbeiten.
    1b. Ach so, zu den Views. Views, auch wenn sie viel Komplexität verstecken, müssen nicht langsamer sein als ein riesen SQL Statement
    3 Stored Procedures können gezielt helfen, kritische Abfragen zu beschleunigen. Das so vielseitig hinzubekommen, wie Du es vermutlich haben willst, ist sicher nicht trivial. > Einzelfallbetrachtung
    4. andere Systeme: Scripte und Templates machen nichts schneller. Templates oder Hilfstools zum Erstellen komplexer Abfragen machen es hoffentlich bequemer. Dann schon eher andere DB. Du sprichst von Oracle, meinst wahrscheinlich Oracle Database, nutzt aber mySQL. Das sind natürlich Welten. Performance erreicht man auch durch bessere Sprachfeatures. Da bist Du natürlich mit Oracle database und Oracle mySQL bei unterschiedlichsten Systemen, was die Fähigkeiten angeht. Alternativvorschlag: postgres, kostenlos, aber nicht umsonst. Schlägt mySQL Funktional um Längen, einfachere Lizenzbestimmungen, mit den entsprechenden Extensions auch sehr Oracle database kompatibel. Extrem vielseitige Index Möglichkeiten, Stored Procs, große Typenvielfalt und starke Typisierungsmöglichkeiten in SQL.

    Was mir jetzt spontan einfällt:
    Postgres ist im Dictionarybereich und der Sprachfülle so stark, dass sich vielleicht ein kleines Set von SP anbietet, zusammen mit ein paar Hilfstabellen. Damit könnte man Views generieren, Feldnamen mappings hinterlegen usw. usf.
    Man könnte sich ein aufwendiges Frontend sparen und die Entwicklungsarbeit hauptsächlich in die SQL Pflege, Generierung, Management stecken.
    Insgesamt muss man dabei im Auge behalten, wieviel Sinn (Kosten/Nutzen) das für einen einzelnen "Nutzer" macht.
    Gruß, defo

    Comment


    • #3
      Hallo defo! Vielen Dank für Deine Infos und Tipps!

      Zu Views: Das wäre auch mein bevorzugter Weg, denn ein prinzipielles Problem ist oft, dass die Daten nicht wirklich "sauber" vorliegen. Das fängt bei kryptischen Tabellen- und Spaltennamen an, geht bei den fehlenden Datentypen weiter (einfach alles in VARCHAR(255) gepackt) und hört bei Sachen wie fehlenden Fremdschlüsseln oder teilweise etwas merkwürdiger Denormalisierungen nicht auf (z.B. teilweise CSVs in Feldern, die dann erst mal zerpflückt werden müssen und dann entsprechend langsam sind). Und das betrifft leider einige Hersteller von Produkten, die ihre Daten in die Datenbanken speichern, die man dann auswerten soll. Ich hatte mir zwischenzeitlich schon überlegt, ob ich via ETL die Daten sauber umwandeln und speichern soll, so wie Datawarehouses das machen. Das ist aber (abgesehen vom Aufwand) nicht gut möglich, da die Daten, wenn man Probleme feststellt, direkt in den herstellereigenen Programmen korrigiert werden und ich das Ergebnis dann direkt kontrollieren will. Und man kann auch das direkte Feedback nutzen, wenn im Herstellerprogramm in einem Testdatensatz XYZ geändert wird, wertet das anschließend die Auswertung korrekt aus? Das ist als Kontrolle und Plausibilitätsprüfung ganz praktisch.

      Mein Plan sind mehrere "Viewlayer":

      Viewlayer 1. Anständige Namen: Diese View stellt menschenlesbare Tabellen- und Spaltennamen zur Verfügung und auch nur die Spalten, die verwendet werden sollen

      Viewlayer 2. Typumwandlung: Diese View wandelt die Spalten in die richtigen Spaltentypen um (ggf. mit Fehlerbehandlung?). Z.B. DATE oder TIME statt VARCHAR, damit man damit vernünftig arbeiten kann (z.B. Zeit- und Datumsabstände bestimmen).

      Viewlayer 3 Menschenlesbare Inhalte Aus den ganzen Nachschlagetabellen werden anhand der ID die Werte bestimmt und diese direkt eingetragen. Z.B. statt ID "583" dann "Geschäftsleitung".

      Viewlayer 4 Berechnete Zusatzspalten: Z.B. wird aus "Datum" (mit dann richtigem Typ) noch eine Spalte "SaSoFeiertag", "Wochentagname", "Jahr", "Monat", "Kalenderwoche" usw. neben dran gestellt. Das hätte den Sinn, Anwendern die Daten "vorgekaut" zur Verfügung zu stellen. Wenn man sie in Excel oder Statistikprogrammen öffnet, dann sieht derjenige direkt, was er braucht und sucht. Würde man diese Spalten indexieren, dann könnte man u.U. die Abfragen extrem beschleunigen.

      Viewlayer 5 Aggregation/Auswertung Und dann vielleicht noch sehr häufig gebrauchte Gruppierungen etc., die man direkt aufrufen kann. Oder: Ich stelle ALLE Auswertungen als View zur Verfügung. Dann bräuchte man bei erneuten Anfragen nur ein SELECT * FROM auswertung4711 WHERE jahr=2017

      Was wäre davon zu halten? Dann könnte man selbst entscheiden, auf welchem "Layer" man die Daten abgreifen möchte. Letztendlich machen ich genau so etwas in meinen Auswertungen auch - nur eben immer von vorn d.h. per Copy + Paste und u.U. wird dann das Rad mehrfach erfunden.
      Für die Einzelschritte hätte ich gerne diverse FUNCTIONS benutzt, damit man auch das nicht X-fach wiederholen muss, sondern in den Views mit einem Dutzend Funktionen alles übersichtlich hin bekommt. Das ist eben nur von der Performance her ein (großes) Problem.


      Zur Performance von Views: Bei MySQL ist das wohl etwas schwierig. In bestimmten Fällen wird es leider extrem langsam. Ich könnte mir vorstellen, dass so etwas eine Oracle Database (die meinte ich mit Oracle) deutlich besser optimiert.


      Mit Postgres hatte ich auch schon geliebäugelt aus diversen Gründen, hatte aber bis jetzt noch nichts damit gemacht. Das wäre tatsächlich eine gute Idee! Die haben ziemliche viele Features eingebaut, die interessant wären. Ich habe gesehen, dass man die MySQL-DB auch verbinden könnte (mysql_fdw) und dann darüber die Daten beziehen. Die können auch Materialized Views (wenn auch nicht wie bei Oracle Database automatisch aktualisiert) und sogar in gewissen Grenzen über Spalten mit Funktionen Indices erstellen. Sehr interessant! Der Execution-Plan (EXPLAIN) sieht auch gut aus mit vielen Infos und übersichtlich. U.U. könnte man darüber besser optimieren.

      Comment


      • #4
        Ich würde den Ansatz mit den 5 Schichten vielleicht etwas "abspecken". ZB 1 und 2 zusammenfassen und 3 und 4. Es soll ja eine Erleichterung sein. Wenn Du am Ende an jeden Select 5x rumschnippelst ist es vielleicht keine Hilfe, leiert aus, wird unscharf und es entstehen Fehler, keiner blickt mehr durch.

        Typumwandlung ist nicht ohne, jenachdem welcher Schrott drinsteht. Ein Substr bspw für gekürzte Felder würde ich mit einem Indikator versehen, wenn etwas abgeschnitten wurde, bspw, "substr(originalfeld, 21)||">>" oder so. Datums oder Zahlumwandlungen im ersten Anlauf mit einer Prüffunktion, nur was umwandelbar ist, wird auch umgewandelt.
        Fehlermeldungen aus Funktionen in Views will man nicht, man produziert notfalls mit einem einzigen Select Statement 100tausende Fehler.
        Daraus ergibt sich, dass man das Plausibilisierungsviews zwischen den verschiedenen Ebenen macht, wenn man fehlerhafte Sätze tatsächlich ausfiltert oder nach einem definierten Schema Umwandlungsfehler flagt.

        Menschlesbare Daten, Typprüfung /-Konvertierung usw. sind natürlich nicht umsonst. Aber weils so schön bequem ist, wird dann immer volle Breitseite ausgewertet und der Server ist schnell am Ende. Für eine solche Ebene würde ich evtl. ein prozedurales Verfahren einsetzen, dass vor Aufruf dieser Views einen Scope setzen muss, andernfalls gibt man leere Menge aus.

        Originally posted by Wursel View Post

        Viewlayer 5 Aggregation/Auswertung Und dann vielleicht noch sehr häufig gebrauchte Gruppierungen etc., die man direkt aufrufen kann. Oder: Ich stelle ALLE Auswertungen als View zur Verfügung. Dann bräuchte man bei erneuten Anfragen nur ein SELECT * FROM auswertung4711 WHERE jahr=2017
        Das habe ich nicht verstanden. "..Häufig .. oder .. alle..und dann bei erneut.."?

        Tja und es ist nicht alles schnell. Man muss vielleicht für verschiedene Fälle optimieren. Bei mysql kann ich mir vorstellen, dass es da nicht glänzt. Oracle DB schon, aber es ist auch fallabhängig (und teuer).

        Postgres
        Es bietet eine Menge Zeug, sehr vielseitig, XML, JSON per Query abfragen, ausgefeilte Indexierung, die weit über Function Based Indizes raus geht. Und es gibt einen Haufen Extensions, u.a. Oracle Extensions für Kompatibilität. Dann kann man ggF. immer noch umsteigen, wenn man die genutzt hat.
        Gruß, defo

        Comment

        Working...
        X