Hi,
I don't know why, anyhow, there are many solutions proposed on the web,
here is one that works in Access 2003 (with MS SQL Server, you can change
the LAST aggregate with MIN, MAX, or SUM, in this case, here, since it
operates on records that just have all the same value, for the implied
group).
------------
SELECT x, AVG(value) As Median
FROM (SELECT a.x, a.value
FROM (MedianForX As a INNER JOIN MedianForX As b
ON a.x=b.x AND a.value>=b.value)
INNER JOIN ( SELECT x, COUNT(*) As s
FROM MedianForX
GROUP BY x) As c
ON a.x=c.x
GROUP BY a.x, a.value
HAVING 2*(COUNT(*)-1)=LAST(c.s)
OR COUNT(*) =INT(0.5+ LAST(c.s)/2)
)
GROUP BY x
------------
with the data:
MedianForX
x value
A 1
A 2
A 3
B 1
B 2
B 3
B 4
it returns
Query39
x Median
A 2
B 2.5
Note that I do not handle duplicated values with the previous formulation.
The having clause handle the even/odd case, returning two groups, or one,
respectively, and if two groups are returned, the average is taken as
median. Basically, the inner query makes so all the job:
SELECT a.x, a.value
FROM (MedianForX As a INNER JOIN MedianForX As b
ON a.x=b.x AND a.value>=b.value)
INNER JOIN ( SELECT x, COUNT(*) As s
FROM MedianForX
GROUP BY x) As c
ON a.x=c.x
GROUP BY a.x, a.value
HAVING 2*(COUNT(*)-1)=LAST(c.s)
OR COUNT(*) =INT(0.5+ LAST(c.s)/2)
To understand it, it may help to observe that COUNT(*) is the actual rank
for the actual group, among a total of c.s values sharing the value x. If
there is an odd number of records, for a given x value, say c.s=3, then, the
HAVING clause is true only if the rank, COUNT(*), has a value of 2. If there
is an even number of records, say c.s=4, as for x='B' here up, then the
having clause is true if the rank is either 2, either 3. So, basically,
that's all. I may end up with a nicer HAVING clause, some day, but that
ugly one works none the less...
Hoping it may help,
Vanderghast, Access MVP