Klaus: Problem bei MySQL-Abfrage

Hallo,

ich habe 2 Tabellen, die 1. Tabelle enthält bspw. Name und Kontaktdaten von Personen, die 2. Tabelle alle Urlaubsziele, die eine Person bereits besucht hat.

Eine Abfrage, für alle Personen aus Hamburg und die bereits Paris besucht haben, funktioniert prächtig:

select p.*,z.* from personen as p left join ziele as z on p.idnr=z.pidnr where p.ort like '%Hamburg%' AND z.ziel like '%Paris%'

Aber wenn ich jetzt alle Personen aus Hamburg wissen möchte, die noch NICHT in Paris waren, funktioniert z.B. die Abfrage nicht korrekt:

select p.*,z.* from personen as p left join ziele as z on p.idnr=z.pidnr where p.ort like '%Hamburg%' AND z.ziel not like '%Paris%'

Das Problem für mich ist, dass die Abfrage dynamisch erstellt wird, d.h. der Anwender kann aussuchen, nach welchen Feldern er nach was suchen möchte und die Abfrage wird dann entsprechend zusammengebaut. D.h. die Abfrage müsste für "like" und "not like" sehr ähnlich aussehen.

Hat jemand eine Idee?

Klaus

  1. Hi,

    ich habe 2 Tabellen, die 1. Tabelle enthält bspw. Name und Kontaktdaten von Personen, die 2. Tabelle alle Urlaubsziele, die eine Person bereits besucht hat.

    Eine Abfrage, für alle Personen aus Hamburg und die bereits Paris besucht haben, funktioniert prächtig:

    select p.*,z.* from personen as p left join ziele as z on p.idnr=z.pidnr where p.ort like '%Hamburg%' AND z.ziel like '%Paris%'
    

    Es gibt Ortsnamen, die Teil eines anderen Ortsnamen sind, die Abfrage nach z.B. '%York%' wird also nicht nur die Besucher der nord-englischen Stadt York treffen, sondern auch die der US-amerikanischen Stadt New York.

    Aber wenn ich jetzt alle Personen aus Hamburg wissen möchte, die noch NICHT in Paris waren, funktioniert z.B. die Abfrage nicht korrekt:

    select p.*,z.* from personen as p left join ziele as z on p.idnr=z.pidnr where p.ort like '%Hamburg%' AND z.ziel not like '%Paris%'
    

    Natürlich nicht, für jeden Hamburger, der bereits eine Stadt besucht hat, auf die not like '%Paris%' zutrifft, wird trotzdem gefunden.

    Da müßte mit einem Konstrukt wie not exists(... z.ziel = 'Paris') gesucht werden.

    cu,
    Andreas a/k/a MudGuard

    1. Hallo,

      Aber wenn ich jetzt alle Personen aus Hamburg wissen möchte, die noch NICHT in Paris waren, funktioniert z.B. die Abfrage nicht korrekt:

      select p.*,z.* from personen as p left join ziele as z on p.idnr=z.pidnr where p.ort like '%Hamburg%' AND z.ziel not like '%Paris%'
      

      Natürlich nicht, für jeden Hamburger, der bereits eine Stadt besucht hat, auf die not like '%Paris%' zutrifft, wird trotzdem gefunden.

      Ist das nicht genau das, was Klaus sucht? @Klaus: kannst du das näher beschreiben, inwiefern die Abfrage nicht korrekt funktioniert?

      Gruß
      Kalk

      1. Hi,

        Hallo,

        Aber wenn ich jetzt alle Personen aus Hamburg wissen möchte, die noch NICHT in Paris waren, funktioniert z.B. die Abfrage nicht korrekt:

        select p.*,z.* from personen as p left join ziele as z on p.idnr=z.pidnr where p.ort like '%Hamburg%' AND z.ziel not like '%Paris%'
        

        Natürlich nicht, für jeden Hamburger, der bereits eine Stadt besucht hat, auf die not like '%Paris%' zutrifft, wird trotzdem gefunden.

        Ist das nicht genau das, was Klaus sucht?

        Nein.

        Wer Rom und Paris besucht hat, wird mit seiner Query gefunden, weil Rom not like Paris ist. Klaus will aber die Parisbesucher gar nicht.

        cu,
        Andreas a/k/a MudGuard

        1. Hallo,

          Wer Rom und Paris besucht hat, wird mit seiner Query gefunden, weil Rom not like Paris ist. Klaus will aber die Parisbesucher gar nicht.

          Genau. Ich wollte eben genau alle die Personen, die noch nicht in Paris waren.

          Natürlich war das auch nur ein Beispiel zum Vereinfachen. Tatsächlich sind 5 Tabellen involviert und das das gesamte Select dynamisch aufgebaut wird, ist es letztlich einiges komplexer, da der Benutzer selber auswählen kann, wieviele Felder, welche Felder und wie er die Felder überprüfen möchte (enthält, enthält nicht, ist gleich, ist nicht, größer gleich, kleiner gleich)

          Aber Dank Deines Hinweises auf exists() konnte ich das Problem lösen. Danke nochmal.

          Klaus

    2. Moin!

      Eine Abfrage, für alle Personen aus Hamburg und die bereits Paris besucht haben, funktioniert prächtig:

      select p.*,z.* from personen as p left join ziele as z on p.idnr=z.pidnr where p.ort like '%Hamburg%' AND z.ziel like '%Paris%'
      

      Klaus: Definiere mal "prächtig":

      Mysql sagt:

      The following SELECT statements do not use indexes:

      SELECT * FROM tbl_name WHERE key_col LIKE '%Patrick%';
      

      In the first statement, the LIKE value begins with a wildcard character.

      Anders ausgedrückt: Wenn Deine Datenbank eines Tages eine Anzahl von Datensätzen enthält, welche die Nutzung einer Datenbank rechtfertigt, dann wirst Du (Klaus) in genau diesem Forum fragen, wie Du diese Abfrage beschleunigen kannst.

      Jörg Reinholz

    3. Hallo Andreas,

      Da müßte mit einem Konstrukt wie not exists(... z.ziel = 'Paris') gesucht werden.

      Du hast vollkommen recht, mit einem exits() bzw. einem not exists() habe ich die Abfrage korrekt umbauen können.

      Vielen Dank.

      Klaus

  2. Tach!

    Eine Abfrage, für alle Personen aus Hamburg und die bereits Paris besucht haben, funktioniert prächtig:

    select p.*,z.* from personen as p left join ziele as z on p.idnr=z.pidnr where p.ort like '%Hamburg%' AND z.ziel like '%Paris%'
    

    Was soll das Ergebnis sein? Willst du eigentlich nur die Personen-Daten haben und das z.* in der SELECT-Klausel ist eigentlich überflüssig? Oder brauchst du auch die Ziele-Daten? Wenn nicht, wäre statt eines Joins eine Correlated Subquery in einem EXISTS() in der WHERE-Klausel verständlicher. Diese Subquery müsste Datensätze mit pidnr gleich p.idnr und z.ziel like '%Paris%' liefern.

    Aber wenn ich jetzt alle Personen aus Hamburg wissen möchte, die noch NICHT in Paris waren, funktioniert z.B. die Abfrage nicht korrekt:

    select p.*,z.* from personen as p left join ziele as z on p.idnr=z.pidnr where p.ort like '%Hamburg%' AND z.ziel not like '%Paris%'
    

    Wenn sie noch nicht in Paris waren, wird es vermutlich keinen ziele-Datensatz mit Paris geben. Ein Left Join liefert dann NULL in den ziele-Daten.

    Mit obiger Subquery müsstest du nur NOT vor das EXISTS() schreiben.

    Kommst du mit den Hinweisen zurecht oder brauchst du weitergehende Unterstützung? Grundlegende Informationen zu EXISTS() gibt es im MySQL-Handbuch: Subqueries with EXISTS or NOT EXISTS.

    dedlfix.

    1. Hallo,

      Mit obiger Subquery müsstest du nur NOT vor das EXISTS() schreiben.

      Kommst du mit den Hinweisen zurecht oder brauchst du weitergehende Unterstützung? Grundlegende Informationen zu EXISTS() gibt es im MySQL-Handbuch: Subqueries with EXISTS or NOT EXISTS.

      Vielen Dank für den Tipp mit dem Exists(). Dank Deiner Hilfe konnte ich mein Select so umbauen, dass ich korrekte Werte erhalte.

      Klaus

  3. Hallo Klaus,

    Unabhängig von deiner tatsächlichen Fragestellung: Auch SQL-Code muss man nicht als Wurst schreiben.

    select p.*,z.* from personen as p left join ziele as z on p.idnr=z.pidnr where p.ort like '%Hamburg%' AND z.ziel like '%Paris%'
    
    select p.*, z.* 
    	from personen as p 
    	left join ziele as z 
    		on p.idnr = z.pidnr 
    		where p.ort like '%Hamburg%' AND z.ziel like '%Paris%'
    

    Es ist selten eine gute Idee, alle Datenfelder haben zu wollen, selektiere nur die, die du tatsächlich brauchst. Die as sind hier zumindest für MYSQL überflüssig.

    Bis demnächst
    Matthias

    --
    Das Geheimnis des Könnens liegt im Wollen. (Giuseppe Mazzini)