Vinzenz Mai: Subselect als Join

Beitrag lesen

Hallo Klaus,

SELECT count(id) as anzahl

FROM results r1
WHERE scan_id <= ? AND ip = ? AND p_id NOT IN
( SELECT r2.p_id FROM results r2 WHERE scan_id <= ? AND ip != ? AND scan_id > r1.scan_id)
ORDER BY id LIMIT 1;

  

> Jetzt habe ich sehr große Performance Probleme. Auch unter Verwendung von Indices.  
  

> <http://www.codersrevolution.com/index.cfm/2008/7/31/MySQL-performance-INNER-JOIN-vs-subselect>  
  

> Kann mir jemand erklären, wie ich das auf meine Abfrage anwende?  
  
ich habe das schon erklärt: ein [Selfjoin](http://aktuell.de.selfhtml.org/artikel/datenbanken/fortgeschrittene-joins/selfjoin.htm) in Verbindung mit einem [Thetajoin](http://aktuell.de.selfhtml.org/artikel/datenbanken/fortgeschrittene-joins/thetajoin.htm):  
  
Soweit ich Dich verstehe möchtest Du die Anzahl des Auftreten der kleinsten id der Datensätze haben, die folgende Bedingungen erfüllen:  
  
a) die scan\_id ist kleiner oder gleich ein bestimmter Wert  
b) die ip-Adresse hat einen bestimmten Wert  
c) die p\_id ist nicht in der Menge der p\_ids enthalten, die aus  
   Datensätzen der gleichen Tabelle stammen, die wiederum bestimmte  
   Bedingungen erfüllen:  
  
   1) die scan\_id liegt zwischen zwei bestimmten Werten  
   2) die ip ist ungleich einem bestimmten Wert  
   (beachte, dass 1) zwei Bedingungen zusammenfasst :-))  
  
Somit können wir grundsätzlich schon mal das ganze mit einem Selfjoin umschreiben:  
  
~~~sql
SELECT  
    r1.id  
FROM  
    results r1  
INNER JOIN  
    results r2  
ON  
    p1.p_id = r2.p_id  
WHERE  
    r1.scan_id <= ? AND ip = ? AND  
    -- mit ein paar redundanten Klammern  
    -- die Bedingung Deines Subselects  
    NOT ( (r2.scan_id BETWEEN ? AND ?) AND (r2.id != ?) )  

Darauf COUNT und LIMIT anzuwenden, solltest Du selbst hinbekommen.

Nicht (a UND b) ist übrigens äquivalent zu (nicht a) ODER (nicht b),
was Du nutzen könntest, um die Bedingung positiver und lesbarer zu gestalten.

r2.scan_id NOT BETWEEN ? AND ? OR r2.id = ?

Achtung: Eine kleine Ungenauigkeit habe ich mir geleistet:

expr BETWEEN wert1 AND wert2 entspricht expr >= wert1 AND expr <= wert2, d.h. beide Grenzen werden mitgenommen. Du müsstest, da Du einmal die Grenze nicht mitnimmst, den übergebenen Grenzwert anpassen (falls dies möglich ist). Wenn nicht, dann formuliere die Bedingungen wie bisher.

Befrage EXPLAIN, was es von den verschiedenen Varianten hält.

Freundliche Grüße

Vinzenz