Jörg: Unterschiedliche Ergebnissmenge im Script vs. phpMyAdmin

Hallo,

ich habe eine Query, die im Scriptverlauf eine Ergebnissmenge generiert, die zu viele Rows hat.

Dieselbe Query in phpMyAdmin hat die erwarttete Anzahl Rows.

Woran kann das liegen?

SELECT
    m.Bla
FROM
    maschinen m
LEFT JOIN dgdgdg dg ON
    m.MaschinenID = dg.MaschinenID AND(
        (
            (
                dg.Erstelldatum =(
                SELECT
                    MAX(Erstelldatum)
                FROM
                    dguv
                WHERE
                    MaschinenID = m.MaschinenID AND unterschrieben = 1
            ) AND unterschrieben = 1
            ) OR(unterschrieben = 0)
        ) AND dg.del = 0
    )
WHERE
    m.MaschinenID IN(61, 62, 64, 51) AND m.MaschinenDel != 1
ORDER BY
    dg.ID ASC

Im Script eingesetzt und direkt aufgelöst:

$result_masch = mysqli_query($db,$query_masch);
echo "<pre>";
print_r($result_masch);
exit;

ergibt das:

	mysqli_result Object
(
    [current_field] => 0
    [field_count] => 18
    [lengths] => 
    [num_rows] => 6
    [type] => 0
)

Die Query ergibt aber im phpMyAdmin richtigerweise nur 4 Rows.

Ich weiß nicht, wo ich mit dem Debugging ansetzen soll.

Kann mir das einer sagen?

Jörg

  1. Lieber Jörg,

    manchmal stelle ich fest, dass ich vergessen habe, die DB frisch vom Server zu ziehen und somit mit zweierlei Datenbeständen teste.

    Liebe Grüße

    Felix Riesterer

    1. Hallo Felix,

      manchmal stelle ich fest, dass ich vergessen habe, die DB frisch vom Server zu ziehen und somit mit zweierlei Datenbeständen teste.

      War auch mien erster Gedanke. Aber die db passt 😕

  2. Hallo Jörg,

    ja, das ist ein typischer Fall von Kannjagarnichtsein. Du zeigst nicht, wie der Inhalt von $query_masch zu Stande kommt. Ist das ein konstanter String, in dem die gleiche Query steht wie in phpMyAdmin?

    Das ist vermutlich irgendwas dusseliges, und etwas, das außerhalb dessen liegt, was Du uns zeigst. Aber wenn Du es uns zeigen könntest, hättest Du das Problem ja schon selbst gesehen…

    Meine ersten Ansätze wären:

    • Überzeuge Dich, dass phpMyAdmin und PHP Script tatsächlich auf die gleiche DB zugreifen.

    • Gib im Script nicht nur den var_dump von $result_masch aus, sondern auch einen echo von $query_masch. Überzeuge Dich mehrfach, gerne auch unter Einbeziehung anderer Augen, dass die Queries identisch sind.

    • Schau Dir die Rows der beiden Resultsets an, nicht nur die Anzahlen.

    Irgendwo muss ein Hinweis stecken. Aber nicht darin, was im Forum steht.

    Rolf

    --
    sumpsi - posui - obstruxi
    1. Hallo Rolf,

      ja, das ist ein typischer Fall von Kannjagarnichtsein. Du zeigst nicht, wie der Inhalt von $query_masch zu Stande kommt. Ist das ein konstanter String, in dem die gleiche Query steht wie in phpMyAdmin?

          echo("$query_masch");
          $result_masch = mysqli_query($db,$query_masch);
      

      Das ist vermutlich irgendwas dusseliges, und etwas, das außerhalb dessen liegt, was Du uns zeigst. Aber wenn Du es uns zeigen könntest, hättest Du das Problem ja schon selbst gesehen…

      Meine ersten Ansätze wären:

      • Überzeuge Dich, dass phpMyAdmin und PHP Script tatsächlich auf die gleiche DB zugreifen.

      • Gib im Script nicht nur den var_dump von $result_masch aus, sondern auch einen echo von $query_masch. Überzeuge Dich mehrfach, gerne auch unter Einbeziehung anderer Augen, dass die Queries identisch sind.

      • Schau Dir die Rows der beiden Resultsets an, nicht nur die Anzahlen.

      Rows habe ich mir auch schon angesehen.

      Hast völlig recht... typischer Fall von... 🙂

  3. Kleine Korrektur:

    Die Query heißt natürlich:

    SELECT
        m.Bla
    FROM
        maschinen m
    LEFT JOIN dgdgdg dg ON
        m.MaschinenID = dg.MaschinenID AND(
            (
                (
                    dg.Erstelldatum =(
                    SELECT
                        MAX(Erstelldatum)
                    FROM
                        dgdgdg 
                    WHERE
                        MaschinenID = m.MaschinenID AND unterschrieben = 1
                ) AND unterschrieben = 1
                ) OR(unterschrieben = 0)
            ) AND dg.del = 0
        )
    WHERE
        m.MaschinenID IN(61, 62, 64, 51) AND m.MaschinenDel != 1
    ORDER BY
        dg.ID ASC
    
    1. @RolfB und @Felix,

      Verteufelt, Ihr hattest doch recht. Ich hatte im Script die DB gewechselt und es gar nicht selber wahrgenommen. Sorry für den fehlalarm!

      Ok, jetzt stimmen die Ergebnisse überein, aber die Query passt dann doch nicht.

      Mir werden nun alle Einträge der gejointen Tabelle dgdgdg angezeigt, ich wollte hier aber nur den für diese Maschine letzten Eintrag haben.

      Außerdem verstehe ich meine Query nicht mehr ganz, ich weiß nicht recht, warum ich dort nicht nur "AND unterschrieben = 1 AND del=0 drin steht.

      Jörg

      1. Hallo Jörg,

        die Query sucht pro Maschine Sätze aus der dgdgdg Tabelle heraus. Und zwar zwei Kategorien von Sätzen:

        • diejenigen mit unterschrieben=1. Davon werden die gefunden, die vom jüngsten Erstelldatum sind.
        • alle mit unterschrieben=0

        Das Ganze wird gefiltert auf Sätze mit del=0 (also wohl die nicht gelöschten), und das Filtrat wird nach einer Spalte sortiert, die sich wohl symbolisch in SELECT m.Bla verstecken wird.

        Inwiefern das fachlichen Sinn ergibt, weißt nur Du 😉

        Rolf

        --
        sumpsi - posui - obstruxi
        1. Hallo Rolf,

          die Query sucht pro Maschine Sätze aus der dgdgdg Tabelle heraus. Und zwar zwei Kategorien von Sätzen:

          • diejenigen mit unterschrieben=1. Davon werden die gefunden, die vom jüngsten Erstelldatum sind.
          • alle mit unterschrieben=0

          Aha, danke Dir! 👍 Genau diese Aufdröselung habe ich nochmal gebraucht.

          Das Ganze wird gefiltert auf Sätze mit del=0 (also wohl die nicht gelöschten), und das Filtrat wird nach einer Spalte sortiert, die sich wohl symbolisch in SELECT m.Bla verstecken wird.

          Stimmt genau. Das ist auch alles ok.

          Inwiefern das fachlichen Sinn ergibt, weißt nur Du 😉

          Ergibt fast genau den Sinn, den ich benötige. Aber eben nur fast. Aber ohne Deine Aufdröselung wäre ich nicht auf den Code gekommen, daher nochmals bedankt! Ich benötige immer nur die jüngsten, egal ob unterschrieben oder nicht, insofern habe ich wie folgt geändert:

          AND (((dg.Erstelldatum = (SELECT MAX(Erstelldatum)
                                            FROM dgdgdg
                                            WHERE MaschinenID = m.MaschinenID))) AND dg.del = 0)
          WHERE 
          

          Scheint auch jetzzt die korrekte Ergebnissmenge anzuzeigen (ich habe nur 2 Beispiele zur Verfügung).

          Siehst Du noch einen Unterschied/Fehler zwischen Query und meinem fachlichen Sinn oder würdest Du zustimmen?

          Danke+Gruß, Jörg

          1. Hallo Jörg,

            Beispiel - keine Ahnung ob das bei Dir fachlich vorkommen kann. Ohne Kenntnis der Anwendung ist sein Vorkommen aber durchaus denkbar.

            Erstelldatum   del
            05.01.2022     0
            05.01.2022     1
            10.01.2022     1
            

            Dein SELECT MAX(Erstelldatum) würde Dir den 10.01. liefern. Da zu diesem Datum aber alle Rows del=1 haben, führt das dazu, dass Du nichts joinst.

            D.h. Du solltest die Abfrage del = 0 auch in der Maximumbestimmung vorsehen. Sie nur dort zu machen wäre wieder inkorrekt, denn dann würdest Du zwar korrekt den 05.01. als MAX(Erstelldatum) bestimmen, dann aber auch die gelöschten Sätze hinzujoinen.

            Rolf

            --
            sumpsi - posui - obstruxi
            1. Hallo Rolf,

              Beispiel - keine Ahnung ob das bei Dir fachlich vorkommen kann. Ohne Kenntnis der Anwendung ist sein Vorkommen aber durchaus denkbar.

              Berechtigter Einwand. Prinzipiell wäre das zwar unproblematisch, aber Du hast recht, sauberer wäre die Berücksichtigung eines solchen Falles.

              Erstelldatum   del
              05.01.2022     0
              05.01.2022     1
              10.01.2022     1
              

              Dein SELECT MAX(Erstelldatum) würde Dir den 10.01. liefern. Da zu diesem Datum aber alle Rows del=1 haben, führt das dazu, dass Du nichts joinst.

              D.h. Du solltest die Abfrage del = 0 auch in der Maximumbestimmung vorsehen. Sie nur dort zu machen wäre wieder inkorrekt, denn dann würdest Du zwar korrekt den 05.01. als MAX(Erstelldatum) bestimmen, dann aber auch die gelöschten Sätze hinzujoinen.

              Hm, habe ich versucht, bekomme aber "den 05.01" (bei mir ist das Beispiel etwas anders) nicht angezeigt.

              AND (((dg.Erstelldatum = (SELECT MAX(Erstelldatum)
                                                FROM dgdgdg
                                                WHERE MaschinenID = m.MaschinenID 
                                                AND dg.del = 0))) AND dg.del = 0)
              WHERE ...
              

              Wo habe ich den Fehler gemacht?

              Jörg

              1. Hallo Jörg,

                um - keine Ahnung. Du hast LISP-SQL geschrieben (LISP: Die Programmiersprache mit mehr Klammern als Code), aber wenn ich das in einen Editor kopiere und durch Einrückungen gliedere, sieht es nicht falsch aus.

                Was vermutlich kein Problem ist, ist der Umstand, dass Du im MAX-Select mit m.MaschinenID vergleichst, obwohl sich der Subselect auf die dg Tabelle bezieht. Während der Auswertung eines konkreten Subselect sollten diese beiden Werte identisch sein, deswegen meine ich, dass es unproblematisch ist.

                Vorschlag zur Fehlereingrenzung:

                Such Dir eine Maschinen-ID, für die Du Ergebnisse erwartest. Sagen wir mal, das wäre die 4711.

                Schau Dir an, was

                SELECT MAX(Erstelldatum)
                                                FROM dgdgdg
                                                WHERE MaschinenID = 4711 
                                                      AND dg.del = 0
                

                liefert. Muss ja irgendein Datum sein, sowas wie 'xx.yy.20zz'

                Setze dieses Datum händisch in deine Query, an Stelle des Subselect

                SELECT
                      m.Bla
                FROM
                      maschinen m
                LEFT JOIN
                      dgdgdg dg ON m.MaschinenID = dg.MaschinenID
                               AND dg.Erstelldatum = 'xx.yy.20zz'
                               AND dg.del = 0
                WHERE
                      m.MaschinenID IN (4711) 
                  AND m.MaschinenDel != 1
                ORDER BY
                      dg.ID ASC
                

                Und dann mal weiter 'rantasten. Ohne deine DB kann ich das jetzt auch nicht näher erklären. Es kann auch an dem Teil der Query liegen, den Du aus Gründen von Betriebsgeheimnissen nicht zeigst.

                Wenn Du nachher den Subselect wieder einsetzt: Ersetze nur 'xx.yy.20zz' durch (SELECT MAX(...) WHERE ...) - mehr Klammern braucht es nicht.

                Rolf

                --
                sumpsi - posui - obstruxi
      2. Hello,

        Darum sollte man alle Bezugsgrößen immer explizit angeben, wenn dies möglich ist, und nicht die bequeme Variante wählen, und die API raten lassen, was gemeint sein könnte.

        Verteufelt, Ihr hattest doch recht. Ich hatte im Script die DB gewechselt und es gar nicht selber wahrgenommen. Sorry für den fehlalarm!

        Ok, jetzt stimmen die Ergebnisse überein, aber die Query passt dann doch nicht.

        Mir werden nun alle Einträge der gejointen Tabelle dgdgdg angezeigt, ich wollte hier aber nur den für diese Maschine letzten Eintrag haben.

        Außerdem verstehe ich meine Query nicht mehr ganz, ich weiß nicht recht, warum ich dort nicht nur "AND unterschrieben = 1 AND del=0 drin steht.

        Jörg

        Glück Auf
        Tom vom Berg

        --
        Es gibt nichts Gutes, außer man tut es!
        Das Leben selbst ist der Sinn.
    2. LEFT JOIN dgdgdg dg ON ... FROM dgdgdg

      Einerseits vergibst du den Alias dg für eine Tabelle, dann rufst du dieselbe Tabelle ohne Alias auf. Habe ich noch nie gemacht, sieht für mich ungewohnt aus.

      1. Hallo Linuchs,

        SQL ist keine Programmiersprache, ein Werkzeug für DB Kunden aller Art, vom Programmierer bis hin zum bekannten C-Promi Dymsta Anzunim Uhse (kurz: DAU). Deswegen versucht es, mitzudenken.

        Tables in Subqueries sind lokal, d.h. wenn die Subquery nur eine Table verwendet, braucht sie keinen Alias.

        Nur falls schon innerhalb der Subquery mehrere Tabellen auftreten und Spaltennamen mehrdeutig sind, ist in der Subquery die Vergabe von Aliasen erforderlich.

        Und wenn aus der Subquery ein Bezug auf eine Row außerhalb der Subquery hergestellt werden muss, dann ist für diesen Bezug ebenfalls ein Alias nötig.

        Rolf

        --
        sumpsi - posui - obstruxi