Christoph: MySQL - WHERE mit AS verwenden

Hallo Forum,

ich suche schon seit 2 Tagen nach einer Lösung für ein Sortierproblem. Ich denke, es ist direkt mit mysql möglich, ansonsten könnte ich das auch über eine PHP-Lösung hinbekommen.

Folgendes Szenario:

  • eine Tabelle mit Aufträgen
  • eine Sorte, die erldigt sind (Spalte auftrErledigt = 1)
  • eine Sorte, die nicht erledigt sind (Spalte auftrErledigt = 0)

Das Problem:

  • die Sorte "unerledigt" (also 0) soll terminlich absteigend sortiert werden
  • die Sorte "erledigt" (also 1) soll terminlich aufsteigend sortiert werden
  • beide Sortierungen sollen in EINER Tabellenübersicht untereinander angezeigt werden, die unerledigten ob und die erledigten darunter.

Mein Idee, wie der Sql-String lauten würde, wenn ich ihn bestimmen könnte und nicht die Datenbank ;):
/**
* @column int(12) termin
* @column int(1) auftragErledigt
* @column alias erledigt
* @column alias unerledigt
*/
SELECT termin, auftragErledigt
FROM tabelle
WHERE auftrErledigt = 0 AS erledigt, auftrErledigt = 1 AS unerledigt
ORDER BY unerledigt ASC, unerledigt DESC

Dass das so nicht funktioniert, ist klar.

Frage: Funktioniert mein Anliegen grundsätzlich, oder mühe ich mich um Sachen, die gar nicht gehen und ich müsste das über Arrays lösen?

Danke im Voraus.
Grüße Christoph

  1. Hi,

    SELECT termin, auftragErledigt
    FROM tabelle
    WHERE auftrErledigt = 0 AS erledigt, auftrErledigt = 1 AS unerledigt
    ORDER BY unerledigt ASC, unerledigt DESC

    Dass das so nicht funktioniert, ist klar.

    Ja. Das liegt aber weniger am „WHERE mit AS“, wie du es im Titel nennst als viel mehr daran, dass du hier zwei mal nach dem selben Wert zu sortieren versuchst, in „entgegengesetzten“ Richtungen, und noch dazu dieser Wert für die Sortierung _innerhalb_ eines der beiden Bereiche gar keine Aussagekraft hat.

    Das Problem:

    • die Sorte "unerledigt" (also 0) soll terminlich absteigend sortiert werden
    • die Sorte "erledigt" (also 1) soll terminlich aufsteigend sortiert werden
    • beide Sortierungen sollen in EINER Tabellenübersicht untereinander angezeigt werden, die unerledigten ob und die erledigten darunter.

    SELECT foo FROM bar ORDER BY UNIX_TIMESTAMP(datum) * IF(auftrErledigt = 0, 1, -1)

    UNIX_TIMESTAMP sorgt dafür, dass das Datum ein Zahlwert ist, und die Multiplikation mit 1 oder -1 dafür, dass die Sortierung der Datensätze für die einen aufsteigend und für die anderen absteigend passiert.
    (In der Theorie, nicht ausprobiert – und gerade zu faul darüber nachzudenken, in welcher Reihenfolge 1 und -1 angegeben werden müssen bzw. ob noch ein explizites ASC/DESC dahinter muss -> selber rausfinden!)

    MfG ChrisB

    --
    RGB is totally confusing - I mean, at least #C0FFEE should be brown, right?
    1. Hallo Chris,

      SELECT foo FROM bar ORDER BY UNIX_TIMESTAMP(datum) * IF(auftrErledigt = 0, 1, -1)

      UNIX_TIMESTAMP sorgt dafür, dass das Datum ein Zahlwert ist, und die Multiplikation mit 1 oder -1 dafür, dass die Sortierung der Datensätze für die einen aufsteigend und für die anderen absteigend passiert.
      (In der Theorie, nicht ausprobiert – und gerade zu faul darüber nachzudenken, in welcher Reihenfolge 1 und -1 angegeben werden müssen bzw. ob noch ein explizites ASC/DESC dahinter muss -> selber rausfinden!)

      Ja, das kommt der Sache schon sehr nah, aber final ist das leider noch nicht. Was hieran die Krux ist, dass die "globale" Filterung über:
      ORDER BY UNIX_TIMESTAMP(datum) * IF(auftrErledigt = 0, 1, -1) läuft, denn nach der Klammer muss noch die Sortierrichtung angegeben werden, nämlich entweder ASC oder DESC -> und DAS ist das Entscheidene!

      MySQL sortiert wunderbar den Timestamp durch Multiplikation mit 1 oder -1, bildet dadurch die gewünschte Reihenfolge korrekt ab, nämlich dass die dringensten Termine ganz oben stehen. ABER durch das "globale" ASC stehen diese Termine ganz hinten in der tabellarischen Übersicht. Drehe ich ASC zu DESC stehen sie wieder zu Anfang aber die Reihenfolge der dringenden Termine kehrt auch wieder um.

      Bevor ich mir jetzt einen Wolf teste, werde ich das ganze über Arrays lösen.

      Vielen Dank für Deinen Denkanstoß.

      Gruß
      Christoph

      1. Hallo,

        SELECT foo FROM bar ORDER BY IF(auftrErledigt = 1, UNIX_TIMESTAMP(datum), (UNIX_TIMESTAMP(maxDatum+1Tag) - UNIX_TIMESTAMP(datum)) * -1)

        maxDatum + 1 Tag musst du noch ersetzen

        vg ichbinich

        --
        Kleiner Tipp:
        Tofu schmeckt am besten, wenn man es kurz vor dem Servieren durch ein saftiges Steak ersetzt...
  2. Tach!

    Das Problem:

    • die Sorte "unerledigt" (also 0) soll terminlich absteigend sortiert werden
    • die Sorte "erledigt" (also 1) soll terminlich aufsteigend sortiert werden
    • beide Sortierungen sollen in EINER Tabellenübersicht untereinander angezeigt werden, die unerledigten ob und die erledigten darunter.

    Du hast also zwei Teilabfragen mit unterschiedlicher Sortierung. Formuliere sie einzeln und verknüpfe sie mit UNION. (Beachte die Hinweise zu den Klammernsetzungen.)

    dedlfix.

    1. Tach!

      Selber Tach! ;)

      Du hast also zwei Teilabfragen mit unterschiedlicher Sortierung. Formuliere sie einzeln und verknüpfe sie mit UNION. (Beachte die Hinweise zu den Klammernsetzungen.)

      (  
      SELECT a . * , w.bez AS werkstoff  
      FROM `fert_auftraege_daten` a  
      INNER JOIN `werkstoff` w ON w.idWerkstoff = a.werkstoff  
      WHERE a.auftrErledigt =0  
      ORDER BY a.termin DESC  
      )  
      UNION  
      (  
      SELECT a . * , w.bez AS werkstoff  
      FROM `fert_auftraege_daten` a  
      INNER JOIN `werkstoff` w ON w.idWerkstoff = a.werkstoff  
      WHERE a.auftrErledigt =1  
      ORDER BY a.termin ASC  
      )
      

      PERFEKT!! Danke SEHR! :)

      Herzliche Grüße
      Christoph

  3. Hallo, ich nochmal.

    die Lösung von dedlfix war leider nur auf den ersten Blick ok. UNION verbietet die ORDER BY in den SELECT's, die vereinigt werden sollen. Ein:

    (  
    SELECT termin, auftrErledigt  
    FROM `table`  
    WHERE auftrErledigt = 0  
    ORDER BY termin ASC  
    )  
    UNION  
    (  
    SELECT termin, auftrErledigt  
    FROM `table`  
    WHERE auftrErledigt = 1  
    ORDER BY termin DESC  
    )
    

    ist witzlos, da MySQL das ORDER BY automatisch herausoperiert (es sei denn, es wird in Verbindung mit einem LIMIT innerhalb der SELECT's verwendet).

    Gültig wäre:

    (  
    SELECT termin, auftrErledigt  
    FROM `table`  
    WHERE auftrErledigt = 0  
    )  
    UNION  
    (  
    SELECT termin, auftrErledigt  
    FROM `table`  
    WHERE auftrErledigt = 1  
    )  
    ORDER BY termin ASC
    

    Damit sind wir aber wieder beim Ausgangsproblem, dass termin "global" sortiert wird.

    Mit PHP ist die Lösung völlig einfach und problemlos:

      
    public function fetchAssocMulti ( $sqlString = '' ) {  
    	$data = array();  
    	$row = $db->query($sqlString);  
    	while( $anfrage = $row->fetch_assoc() ) {  
    		$data[] = $anfrage;  
    	}  
    	if ( !$row ) {  
    		exit($row->error);  
    	} else {  
    		return $data;  
    	}  
    } //end function  
      
    $sqlString_1 = SELECT * FROM `table` WHERE auftrErledigt = 0 ORDER BY termin ASC;  
    $sqlString_2 = SELECT * FROM `table` WHERE auftrErledigt = 1 ORDER BY termin DESC;  
      
    $data_1 = $this->fetchAssocMulti($sql_string_1);  
    $data_2 = $this->fetchAssocMulti($sql_string_2);  
      
    §data = array_merge($data1, $data2)  
    
    

    Fertig! Klappt wunderbar. Wobei ich nicht ausschließen will, dass das auch direkt per sql funktioniert, nur habe ich keine Zeit lange danach zu suchen. Werde ich sicher nochmal aufnehmen, wenn ich nicht so viel um die Ohren habe.

    Danke nochmal an Alle, die mir Tipps gegeben haben.

    Herzliche Grüße
    Christoph

    1. Tach!

      die Lösung von dedlfix war leider nur auf den ersten Blick ok. UNION verbietet die ORDER BY in den SELECT's, die vereinigt werden sollen.

      Schade, dann solltest du doch nochmal den ChrisB-Weg zu gehen versuchen. Nach meinem Dafürhalten war er auch fast richtig, nur muss in der IF()-Funktion zuerst die -1 und dann die 1 stehen. Nicht erledigte willst du absteigend sortieren, also den größten Zeitwert zuerst. Die anderen willst du aufsteigend sortieren, also den kleinsten zuerst. Wenn wir nun von den unerledigten den größten mit einem Vorzeichenwechsel zum kleinsten machen, dann musst du auch hier aufsteigend sortieren, um die gewünschte Reihenfolge zu bekommen. Somit hast du zwei aufsteigende Teilmengen, dich such auch noch dadurch in die richtige Reihenfolge bringen, weil die einen stets negativ und damit kleiner als die anderen sind.

      dedlfix.

    2. Moin Christoph,

      die Lösung von dedlfix war leider nur auf den ersten Blick ok. UNION verbietet die ORDER BY in den SELECT's, die vereinigt werden sollen. Ein:

      (

      SELECT termin, auftrErledigt
      FROM table
      WHERE auftrErledigt = 0
      ORDER BY termin ASC
      )
      UNION
      (
      SELECT termin, auftrErledigt
      FROM table
      WHERE auftrErledigt = 1
      ORDER BY termin DESC
      )

      
      > ist witzlos, da MySQL das ORDER BY automatisch herausoperiert (es sei denn, es wird in Verbindung mit einem LIMIT innerhalb der SELECT's verwendet).  
        
      Ich hab keine Ahnung, ob MySQL das unterstützt, ich hab gerad keine funktionierende MySQL-Installation hier, aber SQL erlaubt Prinzipiell sowas hier:  
        
      ~~~sql
        
      SELECT * FROM (  
        (SELECT termin, auftrErledigt  
        FROM `table`  
        WHERE auftrErledigt = 0  
        ORDER BY termin DESC)  
        UNION  
        (SELECT termin, auftrErledigt  
        FROM `table`  
        WHERE auftrErledigt = 1  
        ORDER BY termin ASC)  
      ) AS auftraege;  
      
      

      Einen Versuch ist es wert. Prinzipiell würde ich aber auch eher den Ansatz von ChrisB wählen, allerdings in leicht anderer Form:

        
      SELECT termin, auftrErledigt  
        FROM `table`  
        ORDER BY auftrErledigt,  
          CASE  
            WHEN auftrErledigt = 0 THEN termin  
            ELSE UNIX_TIMESTAMP(termin) * -1  
          END;  
      
      

      Das selektiert dir zuerst die Reihen mit auftrErledigt = 0 (oder 1, je nachdem ob DESC oder ASC) und danach sortiert es dir innerhalb der auftrErledigt-Sortierung nach termin aufsteigend oder termin absteigend. Halte ich für einfacher als die temporäre Tabelle.

      LG,
       CK