André Laugks: mySQL: LEFT JOIN

Hallo!

Ich wollte zwei Tabellen http://www.kombinat-laugks.de/extern/selfhtml_sql_16_07_20001.txt folgendermaßen ausgeben.

1.Bsp:
tab1.id   tab1.name    tab2.info
1         André        123
1         NULL         145
1         NULL         156
2         Swen         222
2         NULL         333
.
.
usw.

2. Bsp(noch besser ;-)):
tab1.id   tab1.name    tab2.info
1         André        NULL
1         NULL         123
1         NULL         145
1         NULL         156
2         Swen         Null
2         NULL         222
2         NULL         333
.
.
usw.

Mit "SELECT tab1.*, tab2.* FROM tab2 LEFT JOIN tab1 ON tab1.id=tab2.id;" habe ich es (siehe Link) "ungefähr" geschaft. Problem ist, "André" werden die falschen Infonummern(tab2.info) zugewiesen.

Mit ORDER BY konnte ich nichts hinbekommen.

Ist das mit mySQL überhaupt möglich oder muß ich zwei SELECTs absetzen?

Danke und MfG, André Laugks

  1. ReHallo!

    Total vergessen, wenn jemand Lust auf ein mySQL-Dump mit diesen Tabellen hat!
    http://www.kombinat-laugks.de/extern/leftjoin.sql

    MfG, André Laugks

  2. Hallo Andre

    ein typisches Beispiel für eine UNION-Abfrage. Das kann allerdings mysql nicht.

    1. Bsp(noch besser ;-)):
      tab1.id   tab1.name    tab2.info
      1         André        NULL
      1         NULL         123
      1         NULL         145
      1         NULL         156
      2         Swen         Null
      2         NULL         222
      2         NULL         333
      .
      .
      usw.

    Auf der Console klappt folgendes:

    create temporary table test.tmp (id INT not null, name VARCHAR (100), info VARCHAR (100));insert into test.tmp  select test.tab1.id,Name,'' as info from test.tab1;insert into test.tmp select test.tab2.tab1_id,'' as Name, test.tab2.info as info from test.tab1 left join test.tab2 on test.tab1.id=test.tab2.ta
    b1_id;SELECT * from test.tmp order by id;drop table test.tmp;

    über php geht das nicht in einem Arbeitsgang. Da benötigst du mehrere Zugriffe:

    create temporary table test.tmp (id INT not null, name VARCHAR (100), info VARCHAR (100));

    insert into test.tmp  select test.tab1.id,Name,'' as info from test.tab1;

    insert into test.tmp select test.tab2.tab1_id,'' as Name, test.tab2.info as info from test.tab1 left join test.tab2 on test.tab1.id=test.tab2.ta
    b1_id;
    SELECT * from test.tmp order by id;drop table test.tmp;

    An sich kann man schon mit dem Createbefehl ein select auf die auszulesende Tabelle ausführen.
    z.B. so:

    create temporary table test.tmp select test.tab1.id,Name,'NULL' as info from test.tab1;insert into test.tmp select test.tab2.tab1_id,'NULL' as Name, test.tab2.info as info from test.tab1 left join test.tab2 on test.tab1.id=test.tab2.tab1_id;SELECT * from test.tmp order by id;drop table test.tmp;

    Zu beachten ist, im ersten Select müssen alle verwendeten Felder angelegt werden. Nullwerte werden da nicht akzeptiert. Auch ist die Feldlänge der temporären Tabelle immer abhängig vom ersten Select.

    Persönlich würde ich mich in diesem Fall jedoch für folgende Variante entscheiden:

    select tab1.id,Name,info from tab1 left join tab2 on tab1_id=tab1.id order by id

    Hier erhälst du zwar keine Nullzellen, aber gleiche Name erfüllen ja prinzipiell den gleichen Effekt. Werte die einen Eintrag in der Tab1 haben und keinen Eintrag in Tab2 werden ja sowieso zurückgegeben.

    Mit "SELECT tab1.*, tab2.* FROM tab2 LEFT JOIN tab1 ON
    tab1.id=tab2.id;"

    ^^^^^^^^^^^^^^^^
    habe ich es (siehe Link) "ungefähr" geschaft. Problem ist, "André" werden die falschen Infonummern(tab2.info) zugewiesen.

    Du hattest hier nur falsch verknüpft.

    Viele Grüße

    Antje

    1. Hallo!

      ein typisches Beispiel für eine UNION-Abfrage. Das kann allerdings mysql nicht.

      Schade! So einiges vermisse ich in letzter Zeit an mySQL. Aber einem geschenkten Pferd, schaut man nichts ins Maul, oder selbst eine Datenbank programmieren oder sich für eine andere(viel Geld) entscheiden. :-)

      create temporary table test.tmp (id INT not null, name VARCHAR (100), info VARCHAR (100));insert into test.tmp  select test.tab1.id,Name,'' as info from test.tab1;insert into test.tmp select test.tab2.tab1_id,'' as Name, test.tab2.info as info from test.tab1 left join test.tab2 on test.tab1.id=test.tab2.ta

      b1_id;SELECT * from test.tmp order by id;drop table test.tmp;

      Problem ist, mehrere Statements arbeitet mySQL IMHO nach einander ab, also wie nach einander abgesetzt Statments.

      select tab1.id,Name,info from tab1 left join tab2 on tab1_id=tab1.id order by id

      Hier erhälst du zwar keine Nullzellen, aber gleiche Name erfüllen ja prinzipiell den gleichen Effekt. Werte die einen Eintrag in der Tab1 haben und keinen Eintrag in Tab2 werden ja sowieso zurückgegeben.

      Jaein! Ich wollte als erstes den Namen schreiben, dann alle Fotos(tab2.info) dann den nächsten Namen usw.. Mit NULL hätte ich das schön abfragen können.

      if(is_null($name))

      {

      Name

      }

      else

      {

      Foto

      }

      Aber man kann das sicherlich was basteln! Ich werde es auf alle Fälle man austesten.

      Mit zwei SELECTs kann ich auch leben, zuerst
      die Namen dann die Fotos.

      Du hattest hier nur falsch verknüpft.

      Werde ich doch mal gleich probieren.

      Danke und MfG, André Laugks

      1. Hallo Andre

        Schade! So einiges vermisse ich in letzter Zeit an mySQL. Aber einem geschenkten Pferd, schaut man nichts ins Maul, oder selbst eine Datenbank programmieren oder sich für eine andere(viel Geld) entscheiden. :-)

        :-) naja, ich arbeite zur Zeit viel mit Oracle, da vermisse ich auch einiges gegenüber mysql. Es hebt sich eben irgendwo auf.

        Problem ist, mehrere Statements arbeitet mySQL IMHO nach einander ab, also wie nach einander abgesetzt Statments.

        dachte ich mir

        select tab1.id,Name,info from tab1 left join tab2 on tab1_id=tab1.id order by id

        Jaein! Ich wollte als erstes den Namen schreiben, dann alle Fotos(tab2.info) dann den nächsten Namen usw.. Mit NULL hätte ich das schön abfragen können.

        Da ich eh sehr gern diese Bereiche trenne, würde ich das über assoziative Arrays lösen.

        Also unmittelbar nach dem Datenbankzugriff ein Array aufbauen. Etwa so:
        (Ich spare mir jetzt den DB-Syntax. Wenn du z.B. die MySQL-Klasse der PHP-Lib verwendest, ist das eh kein Problem.)

        $foto=array();
        if (!isset($foto[$name])) $foto[$name]=array();
        $foto[$name][]=$info;

        while (list($key,$value)=each($foto))
        {
        echo $key; //der Name

        for ($i=0;$i<count($value);$i++)
         {

        echo $value[$i]; //die Informationen
         }

        }

        Diese Form nehme ich so ziemlich häufig. Vielleicht nutzt sie dir auch was.

        Mit zwei SELECTs kann ich auch leben, zuerst
        die Namen dann die Fotos.

        In kleinen Projekten ist das kein Problem. Bei größeren Projekten oder einer größeren Serverlast kann jeder zusätzliche Select sehr viel Ballast sein.

        Viele Grüße

        Antje

        1. Hallo Antje!

          :-) naja, ich arbeite zur Zeit viel mit Oracle, da vermisse ich auch einiges gegenüber mysql. Es hebt sich eben irgendwo auf.

          Würde ich auch mal gern! Aber viele große Projekte beweisen, daß es auch mit mySQL geht. Man muß eben ein paar mehr Sachen "ausserhalb" der DB machen, was man bei Qracle, DB2 etc. "intern" lösen kann, ... so etwas wie Fremdschlüßel etc.

          Wenn ich eine Person lösche, muß ich das eben gleich in 3 Tabellen machen, in der Daten von der Person liegen.
          Für eine Tabelle muß ich i.d.R. 5 INSERTs auswühren...! Wäre nicht schlecht wenn man das in einem Rutsch machen könnte.

          Da ich eh sehr gern diese Bereiche trenne, würde ich das über assoziative Arrays lösen.

          $foto=array();
          if (!isset($foto[$name])) $foto[$name]=array();
          $foto[$name][]=$info;

          while (list($key,$value)=each($foto))
          {
          echo $key; //der Name

          for ($i=0;$i<count($value);$i++)
          {

          echo $value[$i]; //die Informationen
          }

          }

          Diese Form nehme ich so ziemlich häufig. Vielleicht nutzt sie dir auch was.

          Ich habe ein Forum geschrieben, so wie das selfhtml-Forum. Hier muß ich auch zwei SELECTs absetzen. Weil ich sonst die Postings nicht richtig einordnen kann, wegen gibt "Sortierprobleme" absteigend<->aufsteigend. Ab bestimmten Zeilen muß ich verschieden sortieren. Es gibt so etwas wie SQL-Baumstrukturen, mit der man dies lösen kann. Das werde ich aber erst in Version 4 oder so umsetzen, bin jetzt gerade mal bei Version 1 ;-).

          while($db->fkt_fetch())
             {
                if(is_null($db->fetch["vornummer"])) // --> Anfangspostings
                  {
                      $zeile_start_thread[$db->fetch["id_forum"]] = array(
                                                                      $db->fetch["id_forum"],
                                                                      $db->fetch["betreff"],
                                                                      $db->fetch["name"],
                                                                      $db->fetch["datum"]);
                   }
                   else // --> Antwortpostings
                      {
                          $zeile_antwort_thread[$db->fetch["id_forum"]] = array(
                                                                          $db->fetch["id_forum"],
                                                                          $db->fetch["thread"],
                                                                          $db->fetch["betreff"],
                                                                          $db->fetch["name"],
                                                                          $db->fetch["datum"],
                                                                          $db->fetch["vornummer"],
                                                                          $db->fetch["ebene"]);
                      }
             }

          z.B.
          while(list($id, $inneres_array) = each($zeile_start_thread))
             {
               list($id, $betreff, $name, $datum) = $inneres_array;

          ...

          }

          In kleinen Projekten ist das kein Problem. Bei größeren Projekten oder einer größeren Serverlast kann jeder zusätzliche Select sehr viel Ballast sein.

          Bei diesem Projekt könnte ich 100 SELECTs absetzen. Die Seite wird von dem Normaluser nicht genutzt, also eine Art "B2B", mit ich denke wenig Zugriffen. Der größte Teil läuft eh inerhalb eines CMS ab.

          Wenn ich so manche Scripte sehe, denke ich, arbeite ich recht sparsam. :-)

          MfG, André Laugks

    2. Hi Antje, André

      Persönlich würde ich mich in diesem Fall jedoch für folgende Variante entscheiden:

      select tab1.id,Name,info from tab1 left join tab2 on tab1_id=tab1.id order by id

      Hier erhälst du zwar keine Nullzellen, aber gleiche Name erfüllen ja prinzipiell den gleichen Effekt. Werte die einen Eintrag in der Tab1 haben und keinen Eintrag in Tab2 werden ja sowieso zurückgegeben.

      Die Frage ist hier, ob mit left joins zu arbeiten sinnvoll ist. So werden
      auch Namen ausgegeben, für die es gar keine Entsprechungen in Tabelle 2 gibt.
      Da MySQL in den neuen Versionen inner join kann, wäre das so besser, falls
      Namen ohne Entsprechung nicht ausgegeben werden sollen.
      Ein Sort auf die info von Tabelle 2 zusätzlich wäre wohl auch noch sinnvoll.

      select a.id, a.name, b.info
        from tab1 as a
           inner join tab2 as b on (a.id = b.tab1_id)
        order by a.id, b.info

      IMHO ist mit selbem Name sehr einfach das nachher per PGM-Code in die
      Struktur zu bringen.

      Lies erste Zeile
      mache mit dem namen zeile für die datenzeile
      mache infozeile 1 für die datenzeile
      for (alle zeilen ab erste zeile) {
         if (name = lastname) {
            mache infozeile n
         } else {
            mache namenzeile
            mache infozeile 1
         }
      }

      Ob die Variante mit der temporären Tabelle nicht schneller/resourcenschonender ist, müsste man
      bei grossen Datenmengen allerdings erst ausprobieren (BENCHMARK).

      Gruss Daniela

      1. Hallo!

        select a.id, a.name, b.info
          from tab1 as a
             inner join tab2 as b on (a.id = b.tab1_id)
          order by a.id, b.info

        Das selbe bekomme ich auch mit diesem Statement hin, was ja nichts anderes ist als ein inner Join.

        SELECT tab1.*, tab2.* FROM tab1, tab2 WHERE tab1.id=tab2.tab1_id;

        for (alle zeilen ab erste zeile) {
           if (name = lastname) {
              mache infozeile n
           } else {
              mache namenzeile
              mache infozeile 1
           }
        }

        Solch ein ähnlich Konstrukt hatte ich mir schon zusammen gefrickelt.

        MfG, André Laugks

        1. Hi

          SELECT tab1.*, tab2.* FROM tab1, tab2 WHERE tab1.id=tab2.tab1_id;

          Das stellt zumindest bei älteren MySQL Versionen einen grossen Performanceverlust dar.
          Damit wurde (ob immernoch ist mir nicht bekannt) erst jeder Eintrag von
          Tabelle 1 mit jedem Eintrag von Tabelle 2 verknüpft und erst dann alle
          unpassenden rausgefiltert.

          Auch Select * (auch tab.*) ist nicht ideal, die Gründe stehen im Archiv und auch im
          MySQL Manual. Im einfachsten Fall kriegst du nur überflüssige Spalten (Performance)
          im schlimmsten Fall kann es je nach zugehöriger Pgmiersprache sogar Abstürze
          geben.

          Gruss Daniela

          1. Hallo!

            Das stellt zumindest bei älteren MySQL Versionen einen grossen Performanceverlust dar.
            Damit wurde (ob immernoch ist mir nicht bekannt) erst jeder Eintrag von
            Tabelle 1 mit jedem Eintrag von Tabelle 2 verknüpft und erst dann alle
            unpassenden rausgefiltert.

            Aha! Gut zu wissen!

            Auch Select * (auch tab.*) ist nicht ideal, die Gründe stehen im Archiv und auch im
            MySQL Manual.

            Das Sternchen verwende ich so gut wie nie, da ich nie alle Spalten hole oder Spalten schon mit SQL-Funktionen formatiert ausgebe bzw. mir bereit lege. Wenn ich alle Werte hole, in der Regel Tabellen mit 2-3 Spalten, dann verwende ich ein Sternchen.
            Bei meine "Arbeiten" liegen die Datensätze pro Tabelle im Extremfall unter 10.000, für mySQL was die Performance angeht, ehr eine ganz kleine Zwischenmahlzeit. Alle Datensätze aus Spalten mit dem Type wie BLOB oder TEXT holt man sich i.d.R so und so nicht aufeinmal.

            Aber es ist ja so, schlechte Gewohnheiten, gewöhnt man sich lagsam ab!

            MfG, André Laugks

  3. Sup!

    Warum benutzt Du nicht Oracle, PostGreSQL oder Interbase?

    ;-)

    Gruesse,

    Bio

    1. Hallo!

      Warum benutzt Du nicht Oracle, PostGreSQL oder Interbase?

      Gebe mir das Geld für Oracle oder Interbase :-)! PostGreSQL ist doch OpenSource...!?  Da ich nicht in einer klassischen Onlineagentur arbeite, sondern alleine in einer TV-Firma mich um die firmeneigenen Onlineprojekte kümmere, bekomme ich ein Schlachtschiff wie Oracle oder Interbase nicht durch. Die Projekte haben einfach nicht die Größe. Vielleicht mal in einem Jahr |-).

      Mal schauen, was mySQL4 bringt!

      MfG, André Laugks

      1. Sup!

        Also, wir haben ein ganz kleines Projekt mit PostGreSQL, es laeuft ganz gut, PostGreSQL hat eine aehnliche Syntax wie Oracle, constraints, Fremdschluessel (kaskadierendes Löschen etc.), outer joins, Transaktionen, ist umsonst.
        Und Interbase soll angeblich auch umsonst zu haben sein neuerdings... habe da unten noch so einen Thread gesehen, xwolf sagte, er benutzt Interbase.

        Gruesse,

        Bio