Saturday, May 28, 2011

Find Nth Highest values -- MySQL


Find Nth Highest values in MySQL.


To explain the Nth highest value I have created a product table with dummy data to simulate the scenario.
Table structure
product_id *
product_name *
product_cost
1
P1
1
3
P3
1
5
P5
2
6
P6
3
7
P7
7
10
P10
7
8
P8
8
9
P9
8
11
P11
11
12
P12
11
13
P13
12
14
P14
20
2
P2
25
4
P4
25
15
P15
27

SQL queries to create product table.

CREATE TABLE `product` (
  `product_id` int(11) NOT NULL,
  `product_name` varchar(100) NOT NULL,
  `product_cost` decimal(9,2) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Insert dummy records

insert into product
values (1,'P1',1),(2,'P2',25),(3,'P3',1),(4,'P4',25),(5,'P5',2),
(6,'P6',3),(7,'P7',7),(8,'P8',8),(9,'P9',8),(10,'P10',7),
(11,'P11',11),(12,'P12',11),(13,'P13',12),(14,'P14',20),(15,'P15',27);


All RDBM database support the Min and Max aggregation functions which returns the Minimum and maximum value for the respective columns.

Find the highest purchase cost from the product table


select max(p.product_cost) from product as p
Output
max(p.product_cost)
27

Find the lowest purchase cost from the product table

select min(p.product_cost) from product as p
Output
min(p.product_cost)
1

Find the product having highest purchase cost from the product table


Wrong solution
The below queries gives the wrong output
select p.product_id,p.product_name,max(p.product_cost) from product as p
Output
product_id *
product_name *
max(p.product_cost)
1
P1
27

Actual expected output is
product_id
product_name
max(p.product_cost)
15
P15
27

Correct solution
select p.product_id,p.product_name,max(p.product_cost) from product as p where p.product_cost= (select max(product_cost)from product )


Find distinct purchase cost from product table

select distinct(product_cost) from product order by 1
Output
product_cost
1
2
3
7
8
11
12
20
25
27

According to above result we categories highest and lowest value as below


product_cost
Highest  Value
Lowest Value
1
10
1
2
9
2
3
8
3
7
7
4
8
6
5
11
5
6
12
4
7
20
3
8
25
2
9
27
1
10
The 1st highest value is 27, 2nd highest 25, 3rd highest 20 ….
Hence we can describe as nth highest value is X. if X = 1 then highest value is 27 and if x=10 then highest value is 1.

Let’s find list of all product having Nth highest purchase cost.


select p.product_id,p.product_name,p.product_cost from product as p  where (Nth - 1) = (select count(distinct(p1.product_cost))from product as p1 where p.product_cost <p1.product_cost );

Now place n=1 means list of all product having highest purchase cost
Output
product_id *
product_name *
product_cost
15
P15
27

Now place n=4 means list of all product having 4th highest purchase cost
product_id *
product_name *
product_cost
13
P13
12

Now place n=6 means list of all product having 6th highest purchase cost
product_id *
product_name *
product_cost
8
P8
8
9
P9
8

Let’s find list of all product having Nth lowest purchase cost.


select p.product_id,p.product_name,p.product_cost from product as p  where (Nth - 1) = (select count(distinct(p1.product_cost))from product as p1 where p.product_cost > p1.product_cost );

Now place n=1 means list of all product having lowest purchase cost
product_id *
product_name *
product_cost
1
P1
1
3
P3
1



ROW_NUMBER generation in MySQL


SQL server support Row_Number() function by which we can obtain the row number along with the resulting query. Unfortunately MySQL does not provided such a utility function but there is other ways around to obtain the result.

select * from (
SELECT  @rownum:=@rownum+1 as row_number ,product_id, product_name, product_cost
FROM (SELECT * FROM product p ORDER BY product_cost DESC ) my_row_number, (SELECT @rownum:=0) r ) my_product

Output
row_number
product_id *
product_name *
product_cost
1
15
P15
27
2
2
P2
25
3
4
P4
25
4
14
P14
20
5
13
P13
12
6
12
P12
11
7
11
P11
11
8
9
P9
8
9
8
P8
8
10
10
P10
7
11
7
P7
7
12
6
P6
3
13
5
P5
2
14
3
P3
1
15
1
P1
1

4 comments:

  1. select *
    from (select product_id, product_name, product_cost
    dense_rank() over
    (partition by product_id order by product_cost desc ) dr
    from emp )
    where dr = 4;
    -- Oh, you are using a database that does not do analytics. Sorry to hear that ;-)

    ReplyDelete
  2. Hi,

    Yes MySQL server yet not support the features like dense_rank, ROW_NUMBER etc. whereas SQL Server , Oracle supports such utility functions.

    :)

    ReplyDelete
  3. Instead of inner query try and use a join .
    Inner query takes more memory as compared to join. :)

    ReplyDelete
  4. Hi Ashish,

    This query is really helpful for me.

    ReplyDelete