jam-cloud/db/up/discard_scores_optimized.sql

89 lines
3.6 KiB
PL/PgSQL

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;