Jörg Peschke: LIMIT oder Begrenzung der ResulSets schneller?

Hallo zusammen,
Ich hätte da mal interessehalber eine Frage, die jüngst bei einem Projekt aufgetaucht ist:

Nehmen wir an, ich will eine sortierte Liste einer DB abrufen:

SELECT * FROM tabelle
ORDER BY irgendwas;

So. Jetzt möchte ich aber nur die ersten X einträge haben.
Kann ich natürlich so machen:

SELECT * FROM tabelle
ORDER BY irgendwas LIMIT 0,10;

(oder so ähnlich, genauen Syntax hab ich grad nich im Kopf).

Ich könnte aber auch...
...das Statement von oben nehmen, aber nicht alle ResultSets holen, z.b.:

ResultSet rs = statement.execute();
int cnt = 0;
while ((rs.next()) && (cnt < 10)) {
   // Tu irgendwas
   cnt++;
}

Welche Lösung ist schneller? Ist eine schneller? Oder hängt es vom DBMS ab, wie genau LIMIT implementiert wurde?

Für mein Projekt hat sich die Frage  dann in sofern als hinfällig dargestellt, da das verwendete DBMS kein LIMIT unterstützt - aber wissen würd ichs doch ganz gerne :)

Grüße,
Jörg

  1. Hallo Jörg,

    Nehmen wir an, ich will eine sortierte Liste einer DB abrufen:

    SELECT * FROM tabelle
    ORDER BY irgendwas;

    wieviel Daten muss das DBMS zurückliefern?

    So. Jetzt möchte ich aber nur die ersten X einträge haben.
    Kann ich natürlich so machen:

    SELECT * FROM tabelle
    ORDER BY irgendwas LIMIT 0,10;

    wieviel Daten muss das DBMS hier zurückliefern?

    (oder so ähnlich, genauen Syntax hab ich grad nich im Kopf).

    Ich könnte aber auch...
    ...das Statement von oben nehmen, aber nicht alle ResultSets holen, z.b.:

    wann werden die Daten zum Client transferiert?

    ResultSet rs = statement.execute();
    int cnt = 0;
    while ((rs.next()) && (cnt < 10)) {
       // Tu irgendwas
       cnt++;
    }

    Welche Lösung ist schneller? Ist eine schneller? Oder hängt es vom DBMS ab, wie genau LIMIT implementiert wurde?

    Übrigens: Wo begrenzt Du hier das Resultset?

    Im Spezialfall: Teste doch. Dazu bieten vernünftige DBMS übrigens Werkzeuge.
    Teste mit einer Tabelle mit 10 Millionen Datensätzen. Lass Dir einmal nur die ersten 10 zurückgeben, danach alle und betrachte nur die ersten 10.

    Grundsätzlich gilt: Fordere keine Daten an, die Du nicht benötigst. Deswegen ist in sehr vielen Fällen

    SELECT * FROM

    keine gute Idee.

    Freundliche Grüße

    Vinzenz

    1. Hallo,

      ResultSet rs = statement.execute();
      int cnt = 0;
      while ((rs.next()) && (cnt < 10)) {
         // Tu irgendwas
         cnt++;
      }

      Welche Lösung ist schneller? Ist eine schneller? Oder hängt es vom DBMS ab, wie genau LIMIT implementiert wurde?
      Übrigens: Wo begrenzt Du hier das Resultset?

      Das ist jetzt eben die Frage. Wenn das Resultset bei einem "statement.execute()" komplett geholt wird, ist natürlich die Lösung mit LIMIT vorzuziehen.

      Aber: Dessen bin ich mir eben nicht sicher. Ich denke nämlich, dass
      "statement.execute()" das Resultat in der Datenbank komplett vorbereitet, aber nicht komplett zurückliefert.
      Die Daten einer Zeile geholt werden (denke ich) erst bei rs.next();

      Wenn ich "ORDER" weglassen würde, wär wohl auf jeden Fall die "LIMIT"-Variante schneller - denn dann würde die Datenbank im LIMIT-Fall nur die ersten X Zeilen einer Tabelle lesen, im anderen Fall alles.
      Fürs Sortieren muss die datenbank ja aber ohnehin die ganze Tabelle lesen. Es sei denn, es liegt ein Index für das Sortierkriterium vor (war aber in diesem Fall auch nicht so, da dass Sortierkriterium eine Funktion war)

      Im Spezialfall: Teste doch. Dazu bieten vernünftige DBMS übrigens Werkzeuge.
      Teste mit einer Tabelle mit 10 Millionen Datensätzen. Lass Dir einmal nur die ersten 10 zurückgeben, danach alle und betrachte nur die ersten 10.

      Jo, das werd ich mal tun, wenn wieder Luft ist.

      Grundsätzlich gilt: Fordere keine Daten an, die Du nicht benötigst. Deswegen ist in sehr vielen Fällen

      SELECT * FROM

      Jepp, da hast Du recht, zumal "*" ja nicht unbedingt festlegt, in welcher Reihenfolge die Spalten ankommen, was zum verarbeiten der Ergebnisse keine Freude ist :).
      War jetzt auch nur ein Beispiel, im realen Szenario hatte ich schon Spaltennamen angegeben :)

      Danke für die Einfälle.

      Grüße,
      Jörg

      1. Hallo,

        Das ist jetzt eben die Frage. Wenn das Resultset bei einem "statement.execute()" komplett geholt wird, ist natürlich die Lösung mit LIMIT vorzuziehen.
        Aber: Dessen bin ich mir eben nicht sicher. Ich denke nämlich, dass
        "statement.execute()" das Resultat in der Datenbank komplett vorbereitet, aber nicht komplett zurückliefert.
        Die Daten einer Zeile geholt werden (denke ich) erst bei rs.next();

        selbst wenn wir mal für einen Moment annehmen, dass das tatsächlich so ist und die Datenbank die Ergebnisse in keiner Weise nach außen trägt. Der SQL-Standard schreibt verschiedene Stability Levels vor um zu garantieren, dass sich dein Cursor auch wie erwartet verhält (hab leider gerade keinen Link da). In den allermeisten Fällen muss die Datenbank also das Ergebnis deiner Abfrage getrennt von den eigentlichen Daten halten um sicherzustellen, dass eine andere parallele Abfrage nicht die Integrität der Daten gefährdet. Um auf Vinzenz Beispiel zurückzukommen, müssten sie also 10Mio Daten NOCHMAL irgendwie ablegen (oder geschickt gegen den Datenbestand abgleichen). Unabhängig von der Prozessorlast dürfte die "alles abfragen"-Variante also eine signifikant höhere Speicherlast zur Folge haben.

        MfG
        Rouven

        --
        -------------------
        Computer programming is tremendous fun. Like music, it is a skill that derives from an unknown blend of innate talent and constant practice. Like drawing, it can be shaped to a variety of ends: commercial, artistic, and pure entertainment. Programmers have a well-deserved reputation for working long hours but are rarely credited with being driven by creative fevers. Programmers talk about software development on weekends, vacations, and over meals not because they lack imagination, but because their imagination reveals worlds that others cannot see. -- Larry OBrien and Bruce Eckel in Thinking in C#
        1. Moin,

          Um auf Vinzenz Beispiel zurückzukommen, müssten sie also 10Mio Daten NOCHMAL irgendwie ablegen (oder geschickt gegen den Datenbestand abgleichen). Unabhängig von der Prozessorlast dürfte die "alles abfragen"-Variante also eine signifikant höhere Speicherlast zur Folge haben.

          Ah, guter Punkt!
          So hab ichs noch nicht gesehen, aber das ist sehr einleuchtend.

          Thx.

          Jörg

      2. echo $begrüßung;

        Aber: Dessen bin ich mir eben nicht sicher. Ich denke nämlich, dass
        "statement.execute()" das Resultat in der Datenbank komplett vorbereitet, aber nicht komplett zurückliefert.
        Die Daten einer Zeile geholt werden (denke ich) erst bei rs.next();

        Aufklärung bietet wie so oft das Handbuch. Hier zwei Beispiele:

        Für MySQL ist es nicht ganz offensichtlich zu finden. Die Erklärung der Arbeitsweise mit Resultsets ist bei der Funktion mysql_unbuffered_query() "versteckt". Die neuere mysqli-Schnittstelle bietet ein wenig mehr Steuerungsmöglichkeiten an.

        Für Oracle (OCI-Extension) gibt es den Konfigurationsparameter oci8.default_prefetch sowie die Funktion oci_set_prefetch(), um die Anzahl der beim Client zu puffernden Datensätze einzustellen.

        echo "$verabschiedung $name";

        1. Hallo dedlfix,

          Aufklärung bietet wie so oft das Handbuch. Hier zwei Beispiele:

          Für MySQL ist es nicht ganz offensichtlich zu finden. Die Erklärung der Arbeitsweise mit Resultsets ist bei der Funktion mysql_unbuffered_query() "versteckt".

          Aufschlußreich scheint mir dort der Satz

          <zitat>
              You also have to fetch all result rows from an unbuffered SQL query,
              before you can send a new SQL query to MySQL.
          </zitat>

          zu sein.

          Die neuere mysqli-Schnittstelle bietet ein wenig mehr Steuerungsmöglichkeiten an.

          Aber auch dort lese ich Hinweise auf die Problematik heraus, die vor allem Rouven auf dem Beispiel meiner 10-Millionen-Datensatz-Query herausgearbeitet hat. Grundsätzlich bleibe ich meiner Aussage aus meinem ersten Posting:

          Frage von der Datenbank keine unnötigen Daten ab.

          Für Oracle (OCI-Extension) gibt es den Konfigurationsparameter oci8.default_prefetch

          <zitat>
              OCI_EXACT_FETCH (integer)

          Statement fetch mode. Used when the application knows in advance
              exactly how many rows it will be fetching. This mode turns
              prefetching off for Oracle release 8 or later mode. Cursor is
              cancelled after the desired rows are fetched and may result in
              reduced server-side resource usage.
          </zitat>

          die zu dieser Konstanten gehörende Beschreibung weist meiner Meinung nach ebenfalls auf die Ressourcenproblematik hin, die bei ungepufferten Abfragen entstehen kann, siehe dazu auch meine hinweisenden Fragen:

          wann werden die Daten zum Client transferiert?
          Übrigens: Wo begrenzt Du hier das Resultset?

          Freundliche Grüße

          Vinzenz

  2. Hi,

    Welche Lösung ist schneller?

    mach doch mal ein (Gedanken)experiment mit einer sehr grossen zurückgegebenen Datensatzmenge. Was denkst Du was dann passiert mit dem lieben kleinen rs?

    MFG
    Heinz