By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
443,652 Members | 1,317 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 443,652 IT Pros & Developers. It's quick & easy.

median query without using function

P: n/a
I have a table (cars) with 3 fields:

VIN, Class, sell_price
101, sports, 10000
102, sports, 11000
103, luxury, 9000
104, sports, 11000
105, sports, 11000
106, luxury, 5000
107, sports, 11000
108, sports, 11000
109, luxury, 9000

i need to write a query that WITHOUT USING A FUNCTION will return the
median selling price for each class of car. result should look like:

Class, Med_Price
luxury, 9000
sports, 11000

thanks to all u SQLers

Feb 24 '06 #1
Share this Question
Share on Google+
4 Replies


P: n/a
>From your sample data it looks like you want the most commonly
occurring price for each vehicle (I thought this was
called the "mode", not the "median").

CREATE TABLE Cars(VIN INT,Class VARCHAR(10),sell_price INT)
INSERT INTO Cars(VIN,Class,sell_price) VALUES(101, 'sports', 10000)
INSERT INTO Cars(VIN,Class,sell_price) VALUES(102, 'sports', 11000)
INSERT INTO Cars(VIN,Class,sell_price) VALUES(103, 'luxury', 9000)
INSERT INTO Cars(VIN,Class,sell_price) VALUES(104, 'sports', 11000)
INSERT INTO Cars(VIN,Class,sell_price) VALUES(105, 'sports', 11000)
INSERT INTO Cars(VIN,Class,sell_price) VALUES(106, 'luxury', 5000)
INSERT INTO Cars(VIN,Class,sell_price) VALUES(107, 'sports', 11000)
INSERT INTO Cars(VIN,Class,sell_price) VALUES(108, 'sports', 11000)
INSERT INTO Cars(VIN,Class,sell_price) VALUES(109, 'luxury', 9000)
GO
CREATE VIEW CarNums
AS
SELECT Class,sell_price,COUNT(*) as Num
FROM Cars
GROUP BY Class,sell_price

GO

SELECT c1.Class,c1.sell_price AS Med_Price
FROM CarNums c1
INNER JOIN (
SELECT Class,MAX(Num)
FROM CarNums
GROUP BY Class) C2(Class,Num) ON C2.Class=C1.Class
AND C2.Num=C1.Num

Feb 24 '06 #2

P: n/a

My mistake, you can get the medians using this
CREATE VIEW CarRank
AS
SELECT c1.VIN,
c1.Class,
c1.sell_price,
(SELECT COUNT(*) FROM Cars c2
WHERE c2.Class=c1.Class
AND ((c2.sell_price<c1.sell_price)
OR (c2.sell_price=c1.sell_price AND c2.VIN<=c1.VIN))) as
Rank,
(SELECT COUNT(*) FROM Cars c2
WHERE c2.Class=c1.Class) as MaxRank
FROM Cars c1

GO

SELECT Class,AVG(sell_price) AS Med_Price
FROM CarRank
WHERE Rank IN ((MaxRank+1)/2,(MaxRank/2)+1)
GROUP BY Class

Feb 25 '06 #3

P: n/a
I have a whoel chapter on verious ways to do this in SQL FOR SMARTIES.
Here is one answer.

Median with Characteristic Function

Anatoly Abramovich, Yelena Alexandrova, and Eugene Birger presented a
series of articles in SQL Forum magazine on computing the median (SQL
Forum 1993, 1994). They define a characteristic function, which they
call delta, using the Sybase sign() function. The delta or
characteristic function accepts a Boolean expression as an argument and
returns a 1 if it is TRUE and a zero if it is FALSE or UNKNOWN.

In SQL-92 we have a CASE expression, which can be used to construct the
delta function. This is new to SQL-92, but you can find vendor
functions of the form IF...THEN...ELSE that behave like the condition
expression in Algol or like the question markPcolon operator in C.

The authors also distinguish between the statistical median, whose
value must be a member of the set, and the financial median, whose
value is the average of the middle two members of the set. A
statistical median exists when there is an odd number of items in the
set. If there is an even number of items, you must decide if you want
to use the highest value in the lower half (they call this the left
median) or the lowest value in the upper half (they call this the right
median).

The left statistical median of a unique column can be found with this
query:

SELECT P1.bin
FROM Parts AS P1, Parts AS P2
GROUP BY P1.bin
HAVING SUM(CASE WHEN (P2.bin <= P1.bin) THEN 1 ELSE 0 END)
= (COUNT(*) + 1) / 2;

Changing the direction of the theta test in the HAVING clause will
allow you to pick the right statistical median if a central element
does not exist in the set. You will also notice something else about
the median of a set of unique values: It is usually meaningless. What
does the median bin number mean, anyway? A good rule of thumb is that
if it does not make sense as an average, it does not make sense as a
median.

The statistical median of a column with duplicate values can be found
with a query based on the same ideas, but you have to adjust the HAVING
clause to allow for overlap; thus, the left statistical median is found
by

SELECT P1.weight
FROM Parts AS P1, Parts AS P2
GROUP BY P1.weight
HAVING SUM(CASE WHEN P2.weight <= P1.weight
THEN 1 ELSE 0 END)
= ((COUNT(*) + 1) / 2) AND SUM(CASE WHEN P2.weight >= P1.weight
THEN 1 ELSE 0 END)= (COUNT(*)/2 + 1);
Notice that here the left and right medians can be the same, so there
is no need to pick one over the other in many of the situations where
you have an even number of items. Switching the comparison operators in
the two CASE expressions will give you the right statistical median.

The author's query for the financial median depends on some Sybase
features that cannot be found in other products, so I would recommend
using a combination of the right and left statistical medians to return
a set of values about the center of the data, and then averaging them,
thus:

SELECT AVG(P1.weight)
FROM Parts AS P1, Parts AS P2
HAVING (SUM(CASE WHEN P2.weight <= P1.weight -- left median
THEN 1 ELSE 0 END)= ((COUNT(*) + 1) / 2) AND SUM(CASE WHEN P2.weight >= P1.weight
THEN 1 ELSE 0 END)= (COUNT(*)/2 + 1)) OR (SUM(CASE WHEN P2.weight >= P1.weight -- right median
THEN 1 ELSE 0 END)= ((COUNT(*) + 1) / 2) AND SUM(CASE WHEN P2.weight <= P1.weight
THEN 1 ELSE 0 END)= (COUNT(*)/2 + 1));


An optimizer may be able to reduce this expression internally, since
the expressions involved with COUNT(*) are constants. This entire query
could be put into a FROM clause and the average taken of the one or two
rows in the result to find the financial median. In SQL-89, you would
have to define this as a VIEW and then take the average.

If you have SQL-2005, you can try something like (untested):

SELECT AVG(x),
ROW_NUMBER () OVER (ORDER BY x ASC) AS hi,
ROW_NUMBER () OVER (ORDER BY x DESC) AS lo,
FROM Foobar
WHERE hi IN (lo, lo-1, lo+1);

Feb 26 '06 #4

P: n/a
i don't do homework for college students.

Feb 27 '06 #5

This discussion thread is closed

Replies have been disabled for this discussion.