DROP FUNCTION IF EXISTS discard_scores(); CREATE FUNCTION discard_scores (keep INTEGER) RETURNS VOID AS $$ BEGIN DELETE FROM scores WHERE score_dt < (SELECT score_dt FROM scores s WHERE s.alocidispid = scores.alocidispid AND s.blocidispid = scores.blocidispid ORDER BY score_dt DESC LIMIT 1 OFFSET (keep - 1)); RETURN; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION update_current_network_scores(aloc BIGINT, bloc BIGINT) RETURNS VOID STRICT VOLATILE AS $$ DECLARE newscore INTEGER; newscore_dt TIMESTAMP; newscore_limited BOOL; sum INTEGER; kount INTEGER; r RECORD; avgscore INTEGER; maxscore INTEGER; minscore INTEGER; BEGIN -- find the 6 most recent scores -- (supposedly newscore is the first...) -- hybrid scheme: compute the average of some recent scores, then limit newscore to be between 4/5 and 6/5 of the average newscore := NULL; newscore_dt := NULL; newscore_limited := FALSE; sum := 0; kount := 0; FOR r IN SELECT score, score_dt FROM scores WHERE alocidispid = aloc AND blocidispid = bloc ORDER BY score_dt DESC LIMIT 6 LOOP IF newscore IS NULL THEN newscore := r.score; newscore_dt := r.score_dt; ELSE sum := sum + r.score; kount := kount + 1; END IF; END LOOP; -- if no scores in query at all, then delete any current entry IF newscore IS NULL THEN DELETE FROM current_network_scores WHERE alocidispid = aloc AND blocidispid = bloc; IF aloc != bloc THEN DELETE FROM current_network_scores WHERE alocidispid = bloc AND blocidispid = aloc; END IF; END IF; -- if there are scores older than newscore, then use their average to limit the range of newscore IF kount > 0 THEN avgscore := sum / kount; maxscore := avgscore*6/5; minscore := avgscore*4/5; -- the score newscore will be inserted as the current value in current_network_scores, but we will limit it -- to be no greater than 120% of the average and no less than 80% of the average. this will dampen wild -- swings in the scores. IF newscore > maxscore THEN newscore := maxscore; newscore_limited := TRUE; ELSEIF newscore < minscore THEN newscore := minscore; newscore_limited := TRUE; END IF; END IF; UPDATE current_network_scores SET score = newscore, limited = newscore_limited, score_dt = newscore_dt WHERE alocidispid = aloc AND blocidispid = bloc; IF NOT FOUND THEN INSERT INTO current_network_scores (alocidispid, blocidispid, score, limited, score_dt) VALUES (aloc, bloc, newscore, newscore_limited, newscore_dt); END IF; IF aloc != bloc THEN UPDATE current_network_scores SET score = newscore, limited = newscore_limited, score_dt = newscore_dt WHERE alocidispid = bloc AND blocidispid = aloc; IF NOT FOUND THEN INSERT INTO current_network_scores (alocidispid, blocidispid, score, limited, score_dt) VALUES (bloc, aloc, newscore, newscore_limited, newscore_dt); END IF; END IF; -- keep the scores table clean, meaning only up to the most 5 recent scores per group & direction (scorer) DELETE FROM scores WHERE alocidispid = aloc AND blocidispid = bloc AND scorer = 0 AND score_dt < (SELECT score_dt FROM scores s WHERE s.alocidispid = aloc AND s.blocidispid = bloc AND s.scorer = 0 ORDER BY score_dt DESC LIMIT 1 OFFSET 4); DELETE FROM scores WHERE alocidispid = bloc AND blocidispid = aloc AND scorer = 1 AND score_dt < (SELECT score_dt FROM scores s WHERE s.alocidispid = bloc AND s.blocidispid = aloc AND s.scorer = 1 ORDER BY score_dt DESC LIMIT 1 OFFSET 4); END; $$ LANGUAGE plpgsql;