Moses: Alle auswählen die nicht in anderer Tabelle vorkommen?

Hallo,
habe mal eine Frage zum Abfrage-Query.
Arbeite mit MySql Datenbank.
Habe zwei Tabellen, eine mit Personen die andere mit Ereignissen.
Die beiden Tabellen sind über die Personen ID "verknüp

  1. Da das Posting anscheinend nur zur Hälfte abgeschickt wurde hier nochmal:

    Hallo,
    habe mal eine Frage zum Abfrage-Query.
    Arbeite mit MySql Datenbank.
    Habe zwei Tabellen, eine mit Personen die andere mit Ereignissen.
    Die beiden Tabellen sind über die Personen ID "verknüpft" (bestimmt die falsche Terminologie, aber ich denke Ihr wisst was ich meine Feld Personen_ID ist in beiden Tabellen vorhanden einmal als primary KEy einmal als secondary key, sprich dem ereignis wird eine Person über die ID zugewiesen). Und ja, die Zuordnung ist eindeutig ein Ereignis kann nur eine Person zugewiesen bekommen (eine Person kann allerdings mehrere Ereignisse "haben" ;-)

    Jetzt möchte ich allerdings alle die Personen rausfiltern, denen bisher noch kein Ereignis zugewiesen wurde (sprich die "Karteileichen" finden) - wie muß so eine Abfrage formuliert werden? Select alle Personen_IDs die nicht in Tabelle Ereignis vorkommen ?? (nur wie schreibt man das logisch?)

    Dankbar für jede Hilfe, Link, Suchwort zu diesem Thema und beste Grüße,
    Moses

    1. Hi Moses,

      Arbeite mit MySql Datenbank.

      uh, schade.

      Jetzt möchte ich allerdings alle die Personen rausfiltern, denen bisher noch kein Ereignis zugewiesen wurde (sprich die "Karteileichen" finden) - wie muß so eine Abfrage formuliert werden?

      Du suchst eine Differenzmenge zweier Mengen. In SQL wäre Dein Problem über den MINUS-Operator lösbar - in mySQL dagegen ... (http://www.mysql.com/doc/en/TODO_sometime.html)

      Viele Grüße
            Michael

      --
      T'Pol: I apologize if I acted inappropriately.
      V'Lar: Not at all. In fact, your bluntness made me reconsider some of my positions. Much as it has now.
      (sh:| fo:} ch:] rl:( br:^ n4:( ie:% mo:) va:| de:/ zu:| fl:( ss:) ls:~ js:|)
       => http://www.peter.in-berlin.de/projekte/selfcode/?code=sh%3A|+fo%3A}+ch%3A]+rl%3A(+br%3A^+n4%3A(+ie%3A%25+mo%3A)+va%3A|+de%3A%2F+zu%3A|+fl%3A(+ss%3A)+ls%3A~+js%3A|
      Auch diese Signatur wird an korrekt konfigurierte Browser gzip-komprimiert übertragen.
      1. Hi Michael,
        erstmal danke, auch wenn es mir momentan nicht wirklich weiterhilft.

        Auch wenn es da keinen direkten "Befehl" (ich bin vorsichtig geworden mit diesen Bezeichnungen) gibt, muß es doch möglich sein  eine vernünftige Abfrage zu schreiben die das Problem löst oder?

        Grüße,
        Moses

        1. Hi Moses,

          Auch wenn es da keinen direkten "Befehl" (ich bin vorsichtig geworden mit diesen Bezeichnungen) gibt, muß es doch möglich sein  eine vernünftige Abfrage zu schreiben die das Problem löst oder?

          das Problem dabei ist das Wort "eine".

          Du kannst natürlich das MINUS in Deiner 3GL-Schicht emulieren - was jedoch bedeutet, daß Du beide Mengen (alle Personen, und alle Personen mit mindestens einem Ereignis) unabhängig voneinander berechnen und jeweils sortieren lassen müßtest (das macht MINUS sonst implizit, und bei Deinen PRIMARY KEYs ist das auch sehr performant machbar).

          Mit zwei offenen Cursors, von denen Du jeweils ein aktuelles Element gegeneinander abgleichst, ist das dann wahrscheinlich ziemlich einfach zu bauen.
          (Weil wir vorhin einen Thread über Informatik hatten: Genau das ist eines der Verfahren, das im "Wirth" drin steht: "Zwei-Bänder-Mischen", eigentlich zum Sortieren großer Datenmengen über "divide et impera" gedacht, erlaubt aber nebenbei auch die Bildung von Mengendifferenzen und ähnliches.)

          Viele Grüße
                Michael

          --
          T'Pol: I apologize if I acted inappropriately.
          V'Lar: Not at all. In fact, your bluntness made me reconsider some of my positions. Much as it has now.
          (sh:| fo:} ch:] rl:( br:^ n4:( ie:% mo:) va:| de:/ zu:| fl:( ss:) ls:~ js:|)
           => http://www.peter.in-berlin.de/projekte/selfcode/?code=sh%3A|+fo%3A}+ch%3A]+rl%3A(+br%3A^+n4%3A(+ie%3A%25+mo%3A)+va%3A|+de%3A%2F+zu%3A|+fl%3A(+ss%3A)+ls%3A~+js%3A|
          Auch diese Signatur wird an korrekt konfigurierte Browser gzip-komprimiert übertragen.
          1. Hi Michael,
            *staun*, *wirr guck* - komme ich jetzt nicht mehr mit...
            Im prinzip müßte ich also wie folgt vorgehen (mal ganz vereinfacht geschrieben):

            select Personen_ID from T_Personen
            while $Aus=mysql_fetch_array
            {
            $ID=$Aus["Personen_ID"]
            select Personen_ID from T_Ereignis where T_Ereignis.Personen_ID=$ID
            if mysql_num rows=0
            {
            echo $ID
            }
            }

            Aber das kann es ja wohl nicht sein. OK, bei der momentanen größe meiner Datenbank stellt das noch kein Problem da (etwa 1500 Ereignisse und 300 Personen) aber das ganze wächst ja mit der Zeit und dann wird das mit der Performance schon zum Problem.
            Außerdem suche ich ja dadurch über die größere Menge (es gibt mehr Personen die mindestens einem Ereignis zugeordnet wurden, als Personen die keinem Ereignis zugeordnet wurden.)

            Tschuldige wenn ich etwas schwer von Begriff bin,
            Moses

            1. Hi Moses,

              Im prinzip müßte ich also wie folgt vorgehen (mal ganz vereinfacht geschrieben):

              nein, das wäre in der Tat viel zu teuer.

              Berechne beide Mengen nacheinander und lasse sie jeweils nach Personen_ID sortieren.

              Anschließend kannst Du eine Schleife über die erste Menge laufen lassen und jeweils vergleichen, ob das aktuelle Element mit dem _ersten_ bisher gelesenen Element der zweiten Menge übereinstimmt.
              Ist es kleiner, dann gehört es zu Deiner Ergebnismenge; ist es gleich, dann verwirf es und lies das nächste Element für _beide_ Mengen ...

              Der Trick dabei ist, daß dies genau dann funktioniert, wenn beide "Mengen" eben als sortierte Listen vorliegen. Stell Dir vor, Du mischst zwei sortierte Folgen zusammen, indem Du mit zwei Tonköpfen über zwei Bänder fährst (deshalb Zwei-Bänder-Mischen) - hier tust Du fast dasselbe, Du wertest die Erkenntnisse nur anders aus.

              Außerdem suche ich ja dadurch über die größere Menge (es gibt mehr Personen die mindestens einem Ereignis zugeordnet wurden, als Personen die keinem Ereignis zugeordnet wurden.)

              Das ist nicht zu vermeiden, weil Deine Differenzmenge durch Dein Datenmodell nicht in anderer Art und Weise definiert ist.

              Wenn das eine _häufige_ Operation in Deinem Universum sein sollte, könntest Du jeder Person ein "Nicht-Ereignis" geben. (Als zusätzliche Spalte in der Personen-Tabelle!)
              Du müßtest dann allerdings bei sämtlichen Einfüge- und Lösch-Operationen für Ereignisse dieses "Flag" konsistent pflegen. Dies wäre sehr fehleranfällig in der Programmierung (Trigger/Stored Procedures wären hier hilfreich!), und es ist natürlich ein ästhetischer Bruch im Datenmodell - aber es wäre für diese hochspezialisierte Anwendung ein riesiger Performance-Gewinn erzielbar ... (auch gegenüber MINUS, was sich ja algorithmisch von der obigen 3GL-Lösung kaum unterscheidet)

              Man muß halt wissen, an welchen Stellen es sich lohnt, gegen den "guten Geschmack" zu verstoßen.

              Viele Grüße
                    Michael

              --
              T'Pol: I apologize if I acted inappropriately.
              V'Lar: Not at all. In fact, your bluntness made me reconsider some of my positions. Much as it has now.
              (sh:| fo:} ch:] rl:( br:^ n4:( ie:% mo:) va:| de:/ zu:| fl:( ss:) ls:~ js:|)
               => http://www.peter.in-berlin.de/projekte/selfcode/?code=sh%3A|+fo%3A}+ch%3A]+rl%3A(+br%3A^+n4%3A(+ie%3A%25+mo%3A)+va%3A|+de%3A%2F+zu%3A|+fl%3A(+ss%3A)+ls%3A~+js%3A|
              Auch diese Signatur wird an korrekt konfigurierte Browser gzip-komprimiert übertragen.
              1. Hallo Michael,
                alles klar - vielen Dank!
                Stimmt, das macht ja auch Sinn (jetzt muß ich mich nur noch ein bisschen mehr mit den Array Funktionen auseinander setzen, damit ich die beiden vergleichen kann, aber das Prinzip habe ich jetzt verstanden).

                Nochmals Danke für die Geduld und Hilfe,
                Moses

                1. Hi Moses,

                  (jetzt muß ich mich nur noch ein bisschen mehr mit den Array Funktionen auseinander setzen, damit ich die beiden vergleichen kann, aber das Prinzip habe ich jetzt verstanden).

                  nein - möglichst keine Arrays! (Dann müßtest Du beide Mengen komplett im Arbeitsspeicher halten.)
                  Nimm das Beispiel mit den Tonköpfen mal _noch_ etwas wörtlicher ... ("Cursor"!)

                  Viele Grüße
                        Michael

                  --
                  T'Pol: I apologize if I acted inappropriately.
                  V'Lar: Not at all. In fact, your bluntness made me reconsider some of my positions. Much as it has now.
                  (sh:| fo:} ch:] rl:( br:^ n4:( ie:% mo:) va:| de:/ zu:| fl:( ss:) ls:~ js:|)
                   => http://www.peter.in-berlin.de/projekte/selfcode/?code=sh%3A|+fo%3A}+ch%3A]+rl%3A(+br%3A^+n4%3A(+ie%3A%25+mo%3A)+va%3A|+de%3A%2F+zu%3A|+fl%3A(+ss%3A)+ls%3A~+js%3A|
                  Auch diese Signatur wird an korrekt konfigurierte Browser gzip-komprimiert übertragen.
                  1. Hi Michael,
                    und ich dachte jetzt hätte ich es begriffen, aber....
                    anscheinend fehlt mir da wirklich das Verständnis.
                    Was meinst Du?
                    Vielleicht komme ich ja drauf, wenn ich mal eine Nacht drüber schlafe (falls es keine schlaflose Nacht wird)
                    Ansonsten komme ich nochmal darauf zurück...
                    So und jetzt gehe ich ins Tonstudio und check die Mischpulte!

                    Grüße,
                    Moses

                    1. Hi Moses,

                      Hi Michael,
                      und ich dachte jetzt hätte ich es begriffen, aber....
                      anscheinend fehlt mir da wirklich das Verständnis.
                      Was meinst Du?

                      Du liest beide Sortier-Ergebnisse jeweils mit "fetch" einzeln aus den beiden Cursors. Jeweils ein Element pro Cursor reicht Dir aus, um über die Verwendung des Elements aus dem ersten Cursor zu entscheiden; nach dem Vergleich liest Du ein weiteres Element aus einem bzw. beiden Cursors - je nach dem Ergebnis der Vergleichs.
                      Du hast also zu jedem Zeitpunkt genau zwei Datensätze im Speicher - mehr nicht.

                      Selbst die "Ausgabe" mußt Du nur dann in einen Array schreiben (d. h. "hinten anfügen"), wenn Du die Schleife, mit der Du dieses Ergebnis möglicherweise verarbeiten willst, nicht in die oben skizzierte Schleife einbetten kannst. Ist Deine Verarbeitung für jedes einzelne Element sofort durchführbar, dann brauchst Du das "MINUS-Ergebnis" nicht in einem Array zu puffern, sondern rufst diese Verarbeitung einfach auf - direkt bevor Du erneut aus beiden Cursors liest.

                      Viele Grüße
                            Michael

                      --
                      T'Pol: I apologize if I acted inappropriately.
                      V'Lar: Not at all. In fact, your bluntness made me reconsider some of my positions. Much as it has now.
                      (sh:| fo:} ch:] rl:( br:^ n4:( ie:% mo:) va:| de:/ zu:| fl:( ss:) ls:~ js:|)
                       => http://www.peter.in-berlin.de/projekte/selfcode/?code=sh%3A|+fo%3A}+ch%3A]+rl%3A(+br%3A^+n4%3A(+ie%3A%25+mo%3A)+va%3A|+de%3A%2F+zu%3A|+fl%3A(+ss%3A)+ls%3A~+js%3A|
                      Auch diese Signatur wird an korrekt konfigurierte Browser gzip-komprimiert übertragen.
    2. Hallo,

      Jetzt möchte ich allerdings alle die Personen rausfiltern, denen bisher noch kein Ereignis zugewiesen wurde (sprich die "Karteileichen" finden) - wie muß so eine Abfrage formuliert werden? Select alle Personen_IDs die nicht in Tabelle Ereignis vorkommen ?? (nur wie schreibt man das logisch?)

      Mittels Outer-Join und Gruppierung könntest Du so etwas erledigen:

      SELECT p.id_person, count(e.id_person) Anzahl FROM personen p
        LEFT OUTER JOIN ereignisse e p.id_person = e.id_person
        GROUP BY p.id
        HAVING Anzahl = 0

      Wobei ich nicht so fitt mit mySQL-Syntax bin, daher kann das Statement im Detail auch falsch sein. Vom Prinzip her sollte es allerdings funktionieren.

      Grüße
        Klaus

      1. Hallo Klaus,
        ja so funktioniert es.
        Das select Statement heißt richtig:
        SELECT T_Personen.* FROM T_Personen LEFT JOIN T_Ereignis ON T_Personen.Personen_ID=T_Ereignis.Personen_ID where T_Ereignis.Personen_ID IS NULL

        Wobei das * natürlich mit genaueren Angaben ersatzbar ist....

        Danke für den Hinweis,
        Moses

        Und auch noch mal ein dickes DANKE SCHÖN an Michael für die ausführliche Hilfe und die viele Geduld (manchmal stehe ich einfach auf dem Schlauch).