2016-05-31 11 views
3

Ich habe ein Problem ähnlich LIMITing a SQL JOIN, aber mit einer etwas komplexeren Anforderung.LIMITIEREN eines SQL-JOIN, mit JOIN-Bedingungen

Ich möchte für Benutzer und die damit verbundenen Transaktionen suchen, die innerhalb eines Zeitbereichs liegen:

SELECT u.*, t.* 
FROM User u 
JOIN Transaction t ON t.user_id = u.id 
WHERE t.timestamp >= ? and t.timestamp <= ?; 

So weit, so gut. Jetzt möchte ich die Abfrage wiederholen, aber mit einem LIMIT auf die Anzahl der zurückgegebenen Benutzer. Die Anzahl der für einen bestimmten Benutzer zurückgegebenen Transaktionen sollte jedoch nicht begrenzt sein.

Wenn ich den Ansatz vorgeschlagen, in der anderen Frage folgen, würde dies übersetzen in:

SELECT u.*, t.* 
FROM (SELECT * FROM User LIMIT 10) u 
JOIN Transaction t ON t.user_id = u.id 
WHERE t.timestamp >= ? and t.timestamp <= ?; 

Dies wird nicht produzieren, was ich will: es werden die ersten 10 Benutzer zurückgeben, die keine Transaktionen haben könnten assoziiert .

Ich möchte 10 Benutzer , die mindestens eine verknüpfte Transaktion in dem angegebenen Zeitraum haben.

Wie kann ich dies mit MySQL erreichen?

Antwort

1

Sie können Variablen für diesen Einsatz:

SELECT * 
FROM (
    SELECT *, 
     @rn := IF(@uid = user_id, @rn, 
        IF(@uid := user_id, @rn +1, @rn + 1)) AS rn 
    FROM (
    SELECT u.*, t.* 
    FROM User u 
    JOIN Transaction t ON t.user_id = u.id 
    WHERE t.timestamp >= x and t.timestamp <= y) AS t 
    CROSS JOIN (SELECT @rn := 0, @uid := 0) AS vars 
    ORDER BY user_id) AS x 
WHERE x.rn <= 10 

Variable @rn um 1 wird jedes Mal erhöht wird, ein neuer Benutzer von der Abfrage zurückgegeben. So können wir die Anzahl der zurückgegebenen Benutzer unter Verwendung von @rn <= 10 steuern.

+0

Danke, das funktioniert in der Tat, außer einer fehlenden schließenden Klammer am Ende des zweiten 'IF()'. Gibt es eine Auswirkung auf die Leistung der beiden Subselects? – Benjamin

+0

Außerdem denke ich, dass MySQL das komplette Ergebnis (alle Zeilen) des ursprünglichen JOIN (wahrscheinlich in einer temporären Tabelle?) Lesen muss, bevor es nur die ersten zurückgibt, während das Programm ausgeführt wird äußere SELECT. Kann dies nicht ein großer Leistungsengpass sein, wenn Sie viele Benutzer haben und nur wenige von ihnen übereinstimmende Transaktionen haben? – Benjamin

+0

@Benjamin Sie können alle vorgeschlagenen Abfragen mit Ihren tatsächlichen Daten testen und uns sagen, wie sie miteinander verglichen werden. –

1

Sie können dies tun, ohne Variablen, aber es erfordert Wiederholung der join Logik:

SELECT u.*, t.* 
FROM (SELECT * 
     FROM User 
     WHERE EXISTS (SELECT 1 
        FROM Transaction t 
        WHERE t.user_id = u.id AND 
          t.timestamp >= ? and t.timestamp <= ? 
        ) 
     LIMIT 10 
    ) u JOIN 
    Transaction t 
    ON t.user_id = u.id 
WHERE t.timestamp >= ? and t.timestamp <= ?; 

EDIT:

Wahrscheinlich ist die schnellste Antwort so etwas wie dieses:

select u.*, t.* 
from (select user_id 
     from (select user_id 
      from transaction t 
      where t.timestamp >= ? and t.timestamp <= ? 
      limit 1000 
      ) t 
     limit 30 
    ) tt join 
    user u 
    on tt.userid = u.id join 
    transaction t 
    on tt.userid = t.userid and t.timestamp >= ? and t.timestamp <= ?; 

der ersten Unterabfrage wählt 1.000 übereinstimmende Datensätze in der Transaktionstabelle aus. Ich schätze, das ist mehr als genug, um 30 Benutzer zu bekommen. Diese Liste wird dann mit der Benutzer- und Transaktionstabelle verknüpft, um die endgültigen Ergebnisse zu erhalten. Wenn Sie die Liste einschränken, ohne einen vollständigen Tabellenscan durchführen zu müssen, sollte die erste Abfrage ziemlich schnell sein. . . besonders mit einem zusätzlichen Index auf (timestamp, user).

+0

Danke, ich mag selbst Variablen-basierte Abfragen nicht, aber das JOIN zu wiederholen ist noch umständlicher, würde ich sagen, besonders wenn die Abfrage komplexer wird (und es wird). Wie auch immer, welche Auswirkung hat die Wiederholung des JOIN? Muss MySQL die Aufgabe zweimal erledigen oder wird irgendwo eine Leistungsoptimierung durchgeführt? – Benjamin