Postgres - Fensterfunktion Rang und Anzahl

8

Ich habe einige Tische, an denen die Zeit der Athleten auf einer Strecke aufgezeichnet wird:

Wettbewerbe, Wettbewerbsbenutzer und Wettbewerbsbenutzersitzungen

Ich erstelle eine Abfrage, die den aktuellen Rang und die Gesamtzahl der Konkurrenten für jeden bereitgestellten "Competition_user" extrahiert. Ich kann den Rang in Ordnung bringen, aber meine Anzahl (totalUsers) zählt nicht alle Konkurrenten im Wettbewerb, sondern scheint nur zu zählen an den angegebenen Benutzer (zB gibt das gleiche wie der Rang)

SELECT compUserId, rank, totalUsers 
    FROM (
        SELECT cu.competition_user_id as compUserId, cu.user_id as userId,  
    count(*) OVER w as totalUsers, rank() OVER w as rank 
        FROM competition_users cu 
        LEFT JOIN current_competition_sessions ccs ON cu.competition_user_id = ccs.competition_user_id 
        LEFT JOIN competition_user_sessions cus ON cus.competition_user_session_id = ccs.competition_user_session_id 
        WHERE cu.left_competition = false 
        AND cu.competition_id in (:compIds)
        WINDOW w AS (PARTITION BY cu.competition_id ORDER BY cus.time_in_seconds ASC) 
    ) as sub 
WHERE compUserId in (:compUserIds)

Mein Verständnis war, dass der Standardrahmen für das gesamte Fenster war, während dies vom Rahmenstart bis zur aktuellen Zeile zu zählen scheint.

DaveB
quelle
Verwandte Antwort auf SO mit Zitat aus dem Handbuch, das das Verhalten dokumentiert: stackoverflow.com/a/15839234/939860
Erwin Brandstetter

Antworten:

14

Ihr Problem scheint zu sein, dass Sie für Sie und Ihre Person den gleichen WINDOW(benannten w) Namen verwenden . COUNT(*)rank()

Wenn Sie eine verwenden, WINDOWdie eine ORDER BYKlausel enthält , und dann bestimmte Aggregationen wie SUModer anwenden COUNT, wird die Aggregation kontinuierlich auf die Reihenfolge angewendet , weshalb Ihre COUNTund rank()identisch sind.

Wenn Sie Ihre Abfrage ändern, haben Sie mehrere Fenster als

SELECT compUserId, rank, totalUsers 
FROM (
    SELECT cu.competition_user_id as compUserId, cu.user_id as userId,  
    count(*) OVER (PARTITION BY cu.competition_id) as totalUsers, 
    rank() OVER (PARTITION BY cu.competition_id ORDER BY cus.time_in_seconds ASC) as rank 
    FROM competition_users cu 
    LEFT JOIN current_competition_sessions ccs ON cu.competition_user_id = ccs.competition_user_id 
    LEFT JOIN competition_user_sessions cus ON cus.competition_user_session_id = ccs.competition_user_session_id 
    WHERE cu.left_competition = false 
    AND cu.competition_id in (:compIds)
) as sub 
WHERE compUserId in (:compUserIds);

Damit Sie das nur PARTITION BYauf Ihr COUNT(*)Fenster anwenden und sowohl PARTITION BYals auch ORDER BYKlauseln für Ihr Fenster haben rank(), werden Sie meiner Meinung nach die gewünschten Ergebnisse erzielen.

Verweisen Sie auf diese SQL-FIDDLE als Referenz, wobei ich ein generisches idFeld, ein com_numFeld zur Darstellung der Wettbewerbs-ID und ein com_timeFeld zur Darstellung der Zeit eines Wettbewerbers habe.

Chris
quelle
2
Erwähnen Sie vielleicht den Fensterrahmen und zitieren Sie die spezifische Zeile in den Dokumenten, die dies verdeutlicht. Nämlich: Wenn ORDER BY angegeben wird, besteht der Frame standardmäßig aus allen Zeilen vom Beginn der Partition bis zur aktuellen Zeile sowie allen folgenden Zeilen, die der aktuellen Zeile gemäß der ORDER BY-Klausel entsprechen. Wenn ORDER BY weggelassen wird, besteht der Standardrahmen aus allen Zeilen in der Partition.
Davetapley