1211chef: ORDER BY vor GROUP by

hi mädels und junx, sortieren vor gruppieren klappt bekanntlich nicht aber ne lösung brauch ich trotzdem. die lösung select im select bekomm ich irgendwie nicht hin in perl.

my $ML=$dbh->prepare('SELECT * FROM table WHERE bla1=? OR bla2=? ORDER BY time DESC GROUP by bla1 + bla2') || die $DBI::errstr; $ML->execute($nummer,$nummer) || die $DBI::errstr;

in bla1 oder bla2 muss die selbe kundennummer vorkommen, in der col time steht die unixzeit.

ich brauche für die ausgabe jeweils NUR den jüngsten datensatz pro gruppe, weil immer im jüngsten ds eine info steht die ich brauche. ansonsten wär´s wurscht. wie mach ich das am einfachsten?

gruss gustl

  1. Tach,

    die lösung select im select bekomm ich irgendwie nicht hin in perl.

    woran scheitert es, in deinem Code ist kein Subselect zu sehen?

    my $ML=$dbh->prepare('SELECT * FROM table WHERE bla1=? OR bla2=? ORDER BY time DESC GROUP by bla1 + bla2') || die $DBI::errstr; $ML->execute($nummer,$nummer) || die $DBI::errstr;
    

    in bla1 oder bla2 muss die selbe kundennummer vorkommen, in der col time steht die unixzeit.

    ich brauche für die ausgabe jeweils NUR den jüngsten datensatz pro gruppe, weil immer im jüngsten ds eine info steht die ich brauche. ansonsten wär´s wurscht. wie mach ich das am einfachsten?

    In Mysql kannst du das mit MAX() direkt machen, in allgemeinem SQL brauchst du z.B. eine korrellierte Unterabfrage, da es allgemein nicht erlaubt ist, in einer gruppierten Abfrage Dinge zu selektieren, die nicht gruppiert sind.

    mfg
    Woodfighter

  2. Hallo 1211chef,

    ich brauche für die ausgabe jeweils NUR den jüngsten datensatz pro gruppe, weil immer im jüngsten ds eine info steht die ich brauche. ansonsten wär´s wurscht. wie mach ich das am einfachsten?

    Für dieses Problem gibt es keine einfache Lösung. Wenn wir davon ausgehen, dass die Kombination bla1+bla2, time eindeutig ist, gibt es eine Lösung mit einem Subquery und einem Join:

    CREATE TABLE t1 (
      id INTEGER PRIMARY KEY AUTO_INCREMENT,
      v1 INTEGER NOT NULL,
      v2 INTEGER NOT NULL,
      time TIMESTAMP
    );
    
    INSERT INTO t1 (v1, v2, time) VALUES (1234, 1234, NOW());
    INSERT INTO t1 (v1, v2, time) VALUES (1234, 1234, NOW());
    INSERT INTO t1 (v1, v2, time) VALUES (1235, 1235, NOW());
    INSERT INTO t1 (v1, v2, time) VALUES (1235, 1235, NOW());
    
    SELECT t1.* FROM (SELECT v1+v2 combined_t, MAX(time) time FROM t1 GROUP BY v1+v2) grp INNER JOIN t1 ON t1.v1+t1.v2 = combined_t AND t1.TIME = grp.time;
    

    Wenn nicht, dann hast du ein Problem, dann kannst du da nämlich schweres Geschütz auffahren ;)

    LG,
    CK

    1. Für dieses Problem gibt es keine einfache Lösung.

      Warum eigentlich nicht? Dieses Problem ist doch weit verbreitet. Ich wundere mich gelegentlich dass es da noch keine Erweiterungen gibt die sowas können. Oder vielleicht gibt es die ja auch für manche DBMS und ich kenne sie nur noch nicht?

      In ein GROUP BY Statement noch irgendwie reinzuformulieren "... und davon bitte jeweils nur die Zeile in der SpalteX minimal/maximal ist" wäre ja nicht das Problem.

      1. Hi,

        In ein GROUP BY Statement noch irgendwie reinzuformulieren "... und davon bitte jeweils nur die Zeile in der SpalteX minimal/maximal ist" wäre ja nicht das Problem.

        Und wenn der minimale Wert mehrfach vorkommt? Aus welcher der gefundenen Zeilen sollen dann die Zusatz-Spalten kommen?

        Oder bei z.B

        select min(x), max(x), avg(x), y, z from q group by y

        aus welcher Zeile soll jetzt das z genommen werden? Aus einer mit dem minimalen x, aus einer mit dem maximalen x, oder gar aus einer mit dem durchschnittlichen x (die gibt es ggf. gar nicht ...)

        cu,
        Andreas a/k/a MudGuard

        1. select min(x), max(x), avg(x), y, z from q group by y

          aus welcher Zeile soll jetzt das z genommen werden? Aus einer mit dem minimalen x, aus einer mit dem maximalen x, oder gar aus einer mit dem durchschnittlichen x (die gibt es ggf. gar nicht ...)

          Was ich meine ist eine andere Vorgehensweise als das klassische GROUP BY. Etwas das eine Zeile so liefert wie sie auch wirklich in der Tabelle steht.

          Beispiel: SELECT x, y, z FROM Tabelle GROUP BY x NIMM_NUR_DIE_ZEILE_IN_DER_GILT z ist MAX
          Es werden alle Zeilen nach x zusammengefasst. Dann wird x, y, z aus der Zeile ausgegeben in der z maximal ist.

          Das wäre das was der Fragesteller auch will. Unter allen gleichen Kundennummern jeweils die Zeile suchen die das jüngste Datum hat.

          Und wenn der minimale Wert mehrfach vorkommt? Aus welcher der gefundenen Zeilen sollen dann die Zusatz-Spalten kommen?

          Aus irgendeiner. Zu entscheiden ob sowas möglich ist wäre Sache des Programmierers.

    2. moinMoin(*)

      SELECT t1.* FROM (SELECT v1+v2 combined_t, MAX(time) time FROM t1 GROUP BY v1+v2) grp INNER JOIN t1 ON t1.v1+t1.v2 = combined_t AND t1.TIME = grp.time;
      

      herrje, eh schon verwirrend. dazu kommt die DBI-schnittstelle von perl. wie sollte der subselect denn für die DBI geschrieben aussehen?

      $s1=$dbh->prepare('SELECT t1.* FROM ($s2=$dbh->prepare('SELECT v1+v2 comb ... 
      $s1->execute(x) ... $s2->execute(x) ... ???
      

      also da steig ich jetzt gar nicht mehr durch.

      vermutlich werde ich halt GROUP BY nicht verwenden und irgendwie die ergebnisse nur ORDER BY time DESC aber nicht gruppiert in eine @liste schreiben und diese liste so lange manipulieren bis aus JEDER TEILMENGE die datensätze älteren datums raus sind. was sagt denn der RegEXP*** experte dazu?

      dabei ist aber bisschen schwierig dass in der teildatenmenge diejenigen DS zusammengefasst werden müssen die ich ja eigentlich mit GROUP BY zusammenfassen wollte.

      kundennummer nach der ich suche ist angenommen 200 (v1+v2 sind kundennummern, v3 ist der timestamp).

      DS1: 100;200;4999 (200 kommt vor, ist älter) DS2: 100;200;5000 (200 kommt vor, ist der jüngste) DS3: 200;100;4998 (200 kommt vor, ist älter) ... datensatz-kombination aus 200 und 100 - oder 100 und 200, wenn der jüngste (also hier DS2) übernehmen

      DS4: 90;200;12000 (200 kommt vor, ist älter) DS5: 200;90;12521 (200 kommt vor, ist der jüngste) DS6: 200;100;7896 (200 kommt vor, ist älter) ... datensatz-kombination aus 200 und 90 - oder 90 und 200, wenn der jüngste (also hier DS5) übernehmen

      und so weiter ... DS7: 300;200;12345 (kombi im DS10 und DS7, der jüngste) > übernehmnen DS8: 88;200;12346 (kombi im DS11 und DS8, der jüngste) > übernehmen DS9: 200;9;87423 (kommt nur ein mal vor) > übernehmen DS10: 200;300;9000 (kombi im DS10 und DS7, nicht der jüngste) > nicht übernehmnen DS11: 200;88;7896 (kombi im DS11 und DS8, nicht der jüngste) > nicht übernehmen DS12 ... und so weiter

      ich muss also im Grunde: DS1 mit allen anderen vergleichen, DS2 mit allen anderen vergleichen ... etc.

      wir hatten doch mal einen regExp experten hier im forum oder? regExp ist für mich als schachteldenker echt schwer. oft gehts und ich weiss gar nicht recht wieso eigentlich.

      gruss gustl

      1. Hallo gustl,

        SELECT t1.* FROM (SELECT v1+v2 combined_t, MAX(time) time FROM t1 GROUP BY v1+v2) grp INNER JOIN t1 ON t1.v1+t1.v2 = combined_t AND t1.TIME = grp.time;
        

        herrje, eh schon verwirrend. dazu kommt die DBI-schnittstelle von perl. wie sollte der subselect denn für die DBI geschrieben aussehen?

        $s1=$dbh->prepare('SELECT t1.* FROM ($s2=$dbh->prepare('SELECT v1+v2 comb ... 
        $s1->execute(x) ... $s2->execute(x) ... ???
        

        Naja, das ist einfach nur ein SQL-Statement. Also so:

        $s1 = $dbh->prepare('SELECT t1.* FROM (SELECT v1+v2 combined_t, MAX(time) time FROM t1 GROUP BY v1+v2) grp INNER JOIN t1 ON t1.v1+t1.v2 = combined_t AND t1.TIME = grp.time');
        $s1->execute();
        

        wir hatten doch mal einen regExp experten hier im forum oder?

        Die haben wir immer noch, aber deine Frage ist zu unspezifisch bzw zu wenig ausgearbeitet. Ich zumindest steige nicht so recht durch, was du tun willst.

        LG,
        CK

        1. hallo christian, ich weiss ich bin miserabel darin mich zu erklären.

          also die genaue aufgabe lautet:

          zeige nur die jüngsten datensätze

          von denen in kd1 oder kd2 $kdnr vorkommt

          wenn

          kd1 gleich $kdnr und eastate gleich "A"

          oder

          kd2 gleich $kdnr und eastate gleich "E"

          geordnet nach neueste zuerst.

          die db-struktur ist ganz einfach:

          ident | kd1 | kd2 | eastate | time | ... weitere spalten

          ich brauche für die darstellung immer nur den neuesten datensatz, also den mit der höchsten zahl in der spalte time.

          anders erklärt:

          meine kdnr ist angenommen 100. die zweite kdnr kenne ich nicht.

          es gibt also angenommen 50 datensätze mit der kombination 100;200 ODER 200,100. davon brauche ich nur den jüngsten wenn kd1 gleich 100 ist und eastate gleich "A" ist ODER wenn kd2 gleich 100 ist und eastate gleich "E" ist

          dann gibts also angenommen 70 datensätze mit der kombination 100;300 ODER 300,100. davon brauche ich wieder nur den jüngsten wenn kd1 gleich 100 ist und eastate gleich "A" ist ODER wenn kd2 gleich 100 ist und eastate gleich "E" ist

          ... und so weiter ...

          vermutlich ist es generell ganz simpel, aber ich stehe wieder mal im wald und sehe keine bäume.

          gruss gustl

          1. Hallo,

            hallo christian, ich weiss ich bin miserabel darin mich zu erklären.

            damit bist du ganz bestimmt kein Einzelfall: Das Talent, einen Sachverhalt logisch zu erklären oder zu beschreiben, ist bei sehr vielen schlecht bis gar nicht vorhanden.

            zeige nur die jüngsten datensätze

            von denen in kd1 oder kd2 $kdnr vorkommt

            wenn

            kd1 gleich $kdnr und eastate gleich "A"

            oder

            kd2 gleich $kdnr und eastate gleich "E"

            geordnet nach neueste zuerst.

            Klingt doch gar nicht so kompliziert:

            SELECT ... WHERE ($kdnr=kd1 AND eastate='A') OR ($kdnr=kd2 AND eastate='E') ORDER BY time DESC
            

            Und wenn du jetzt nur den jüngsten Datensatz brauchst, auf den die Bedingung zutrifft, häng einfach noch ein LIMIT 1 hinten an. Oder wo liegt das Problem bzw. was versuchst du dabei noch mit GROUP zu erreichen?

            Ich bin jetzt davon ausgegangen, dass $kdnr sowie die Spalten kd1 und kd2 rein numerisch sind; wenn es Strings sind, muss man natürlich noch Anführungszeichen setzen und $kdnr SQL-gerecht escapen.

            So long,
             Martin

            1. hi martin, danke für deine antwort.

              lies nochmal genau. ich brauche immer den jüngsten datensatz einer kundennummerGRUPPE. dafür bräuchte ich GROUP BY, aber dann kann ich nicht mehr nach dem jüngsten datensatz ordern :-)

              darüber diskutieren wir ja schon seit ner woche.

              ps, falsch ausgedrückt: nach kundennummerGRUPPEN (mehrzahl) wie sie vorkommen, am ende in einer liste, neueste zuerst, und je gruppe immer nur den einen neuesten.

              gruss Gustl

        2. hi (*), also ich habs jetzt so gemacht weil ich den subselect von christian nicht in den griff bekomme.

          ich bilde also erst mal eine liste @ZE mit kundennummerpaaren.

            my $ML=$dbh->prepare('SELECT kd1,kd2 FROM T WHERE   kd1=?   OR   kd2=?   GROUP BY kd1 + kd2') || die $DBI::errstr;
            $ML->execute($kdnr,$kdnr) || die $DBI::errstr; 
          my $DL=$ML->fetchall_arrayref; 
          my @ZE = map {join";",@$_} @$DL;
          

          dann lasse ich die kundennummernpaare in einer schleife durchlaufen und selektiere alle datensätze die auf meine bedingungen passen nach time DESC. das heisst der letzte gelieferte listeneintrag pro kundennummerpaar ist dann auch der jüngste datensatz den ich brauche. durch das einfache fetch steht in $L dann auch der letzte drin. den pusche ich dann einfach pro schleifendurchlauf in die liste @PL.

            my @PL; 
            foreach (@ZE) {
              my @E=split(";",$_); my $kd2; if ($E[0] eq $kdnr) { $kd2=$E[1]; } else { $kd2=$E[0]; }
              my $gr=$dbh->prepare('SELECT ident,kd1,kd2,time,bla,ble,bli,blu FROM T
              WHERE kd1=? AND kd2=? AND eastate=?   OR   kd1=? AND kd2=? AND eastate=?  ORDER BY time DESC') ||   die $DBI::errstr;
              $gr->execute($kdnr,$kd2,'A',  $kd2,$kdnr,'E')  || die $DBI::errstr;
              my $L=$gr->fetch;
              push(@PL,"@$L[0];@$L[1];@$L[2];@$L[3];@$L[4];@$L[5];@$L[6];@$L[7]"); }
          

          wenn ich mir das ankuck wird mir übel, aber es funktioniert.

          leider hab ich das ergebnis in @PL nicht nach zeit sortiert, das wollte ich so machen:

          @PL = (sort { $a->[3] <=> $b->[3] } @PL); 
          

          (feld 3 ist der timestamp pro datensatz)

          mit use strict; erhalte ich aber eine fehlermeldung.

          Can't use string ("1097;1155;1156;1459960752;1;E;te"...) as an ARRAY ref while "strict refs" in use at zacklzement.pl line 70.

          ich weiss ich hatte das auch schon mal gelöst, kann mich aber wieder mal nicht erinnern wie ich das schreiben muss. weiss es zufällig jemand? in der liste @PL stehen doch keine ARRAYreferenzen wie zb. in $L. woher kommt das?

          gruss Gustl