I have a table like this:
ID | score1 | score2 | score3 | score4 | score5 | score6 | score7 |
---|---|---|---|---|---|---|---|
1 | 6 | 6 | 7 | 7.5 | 6.5 | 7 | 5 |
2 | 9 | 8 | 7 | 8 | 7 | 8.5 | 7.5 |
3 | 7 | 7 | 7 | 7 | 7 | 7 | 7 |
I want to calculate the average score of each row by removing one of the highest and one of the lowest scores and calculating the average of the remaining scores
.
For example,
ID 1: avg=(6+6+7+6.5+7)/5=6.5
ID 2: avg=(8+7+8+8.5+7.5)/5=7.8
ID 3: avg=(7+7+7+7+7)/5=7
The following is the method I use. It is very bloated and the execution time is too long. Especially if there are more score columns(score8, score9…), more UNION ALL
will be piled up.
SELECT
id,
(SUM(scores) - MAX(scores) - MIN(scores)) / 5 AS adjusted_average
FROM
(SELECT id, score1 AS scores FROM scores
UNION ALL
SELECT id, score2 AS scores FROM scores
UNION ALL
SELECT id, score3 AS scores FROM scores
UNION ALL
SELECT id, score4 AS scores FROM scores
UNION ALL
SELECT id, score5 AS scores FROM scores
UNION ALL
SELECT id, score6 AS scores FROM scores
UNION ALL
SELECT id, score7 AS scores FROM scores) subquery
GROUP BY id
ORDER BY id
So I would like to ask if there is a neat way to achieve my needs? (You can use PostgreSQL for demo, thank you!)
3
Answers
First you should normalize data, so any addition of new scores does not have (much) impact on your query:
Then you can do, and the query needs no change when you add a new score!:
output:
see: DBFIDDLE
Your original score list can be retrieved using this SQL, which is easily adapted when a new score is added.
output:
You can use
GREATEST()
andLEAST()
, it has simple query and faster execution:dbfiddle:https://dbfiddle.uk/G80L1b_d