The RANK and DENSE_RANK functions are advanced functions but they don’t need to be complicated. Learn how to use the Oracle RANK function and Oracle DENSE_RANK function in this article.

## Purpose of the Oracle RANK Function

The Oracle RANK function allows you to calculate the rank of a value in a group of values. It returns a NUMBER value.

It can be used in two ways in Oracle – as an aggregate function, or as an analytical function.

I’ll look at both of them in this article.

## Syntax

The syntax for the RANK function depends on your usage of it

Using RANK as an **aggregate** function (takes multiple rows and returns a single number):

`RANK ( expr ) WITHIN GROUP ( ORDER BY ( order_expr [NULLS FIRST/LAST] ) )`

Using RANK as an **analytical** function (takes criteria and shows a number for each record)”

`RANK () OVER ( [query_partition_clause] order_by_clause )`

This is a little more complicated than many other Oracle functions, but I’ll explain the function in this article.

## Parameters – Aggregate RANK Function

The parameters of the RANK function are different, depending on whether you’re using it as an aggregate or analytical function

For an aggregate function, the parameters are:

- expr (mandatory): This is the value to search for to find a particular record in the table. You can specify more than one
*expr*value, separated by a comma. *expr*is ordered by to determine a RANK value. This is because you get different RANK values depending on what order you want to rank data in.- NULLS FIRST/LAST (optional): This option specifies whether you want to have NULLS at the top or the bottom of the ranking order. NULLS FIRST means that NULL values are considered a high rank, and NULLS LAST means that NULL values are considered a low rank.

Some notes about this function used as an aggregate:

- The number of expressions within RANK (
*expr*) must be the same as the number of expressions in the ORDER BY clause. - The expression lists in RANK and ORDER BY are matched by position, so they must be the same data type.

** **

## Parameters – Analytical RANK Function

If you’re using RANK as an analytical function (i.e. one RANK value per record returned), then the parameters are:

- query_partition_clause (optional): This is the expression to “group” the ranking by. You can rank data within groups in your query, and the PARTITION BY does this.
- order_by_clause (mandatory): The expression to order your results by to determine a rank.

Some notes:

- If two records have the same values according to the RANK function, then they will have the same RANK value. This will then cause a “gap” in the rankings. The DENSE_RANK function is similar to RANK but does not cause a gap in the rankings.

## Purpose of the Oracle DENSE_RANK Function

The purpose of the DENSE_RANK function is to calculate a rank of a row in a group of rows, and returns this rank as a NUMBER value.

It can be used as either an aggregate function or an analytical function.

Let’s look at both of them in this article.

## Syntax

The syntax of DENSE_RANK depends on how you use it.

Using it as an aggregate function (which takes multiple rows and returns a single number):

`DENSE_RANK ( expr, [expr(n)] ) WITHIN GROUP ( ORDER BY (order_expr [ASC|DESC] [NULLS FIRST|LAST] )`

Using it as an analytical function (takes criteria and shows a number for each record):

`DENSE_RANK() OVER ( [query_partition_clause] order_by_clause)`

This is a little more complicated than many other Oracle functions, but I’ll explain the function in this article.

## Parameters – Aggregate DENSE_RANK Function

The parameters for this function are different, depending on whether you use it as an aggregate function or an analytical function.

The parameters of the Oracle DENSE_RANK function when used as an aggregate function are:

- expr (mandatory): This is the value to search for to find a particular record in the table. You can specify more than one
*expr*value, separated by a comma. *expr*by. THis is because you get different values depending on what order you rank the data in.- NULLS FIRST|LAST (optional). This option specifies whether you want to have NULLS at the top or the bottom of the ranking order. NULLS FIRST means that NULL values are considered a high rank, and NULLS LAST means that NULL values are considered a low rank.

- The number of expressions within DENSE_RANK (
*expr*) must be the same as the number of expressions in the ORDER BY clause. - The expression lists in DENSE_RANK and ORDER BY are matched by position, so they must be the same data type.

## Parameters – Analytical DENSE_RANK Function

Now, let’s take a look at the parameters if you’re using DENSE_RANK as an analytical function:

- query_partition_clause (optional): This is the expression to “group” the ranking by. You can rank data within groups in your query, and the PARTITION BY does this.
- order_by_clause (mandatory): The expression to order your results by to determine a rank.

Some notes:

- If two records have the same values according to the DENSE_RANK function, then they will have the same RANK value. However, this will NOT cause a “gap” in the rankings. It is a similar function to RANK but does not cause a gap in the rankings.

## What’s the Difference Between RANK, DENSE_RANK, and ROW_NUMBER in Oracle?

The main differences between RANK, DENSE_RANK, and ROW_NUMBER in Oracle are:

- RANK is based on a column or expression and may generate the same value twice. Rank numbers are skipped so there may be a gap in rankings.
- DENSE_RANK is also based on a column or expression and may generate the same value twice. Rank numbers are not skipped so there will not be a gap in rankings.
- ROW_NUMBER is generated once for each row so there are no duplicates or gaps.

## Examples of the Oracle RANK Function

Here are some examples of the Oracle RANK function. I find that examples are the best way for me to learn about code, even with the explanation above.

### Sample data

This is the student table that is being used for these examples.

```
SELECT *
FROM student
ORDER BY fees_paid;
```

Result:

STUDENT_ID |
FIRST_NAME |
LAST_NAME |
FEES_PAID |
GENDER |

5 | Steven | Webber | 0 | M |

10 | Tanya | Hall | 50 | F |

4 | Mark | Holloway | 100 | M |

6 | Julie | Armstrong | 150 | F |

7 | Michelle | Randall | 150 | F |

1 | John | Smith | 200 | M |

3 | Tom | Capper | 350 | M |

2 | Susan | Johnson | 500 | F |

8 | Andrew | Cooper | 800 | M |

9 | Robert | Pickering | 900 | M |

### Example 1 (Aggregate) – Basic

This example ranks the value of 200 in the fees_paid column.

```
SELECT
RANK(200) WITHIN GROUP (ORDER BY fees_paid) AS rank_val
FROM student;
```

Result:

RANK_VAL |

6 |

It shows 6, because the value of 200 is the 6th lowest in the fees_paid column in the student table.

### Example 2 (Aggregate) – Duplicate value

This example uses a value that is duplicated in the fees_paid column

```
SELECT
RANK(150) WITHIN GROUP (ORDER BY fees_paid) AS rank_val
FROM student;
```

Result:

RANK_VAL |

4 |

It shows 4, because the value of 150 is the 4th lowest in the fees_paid column in the student table.

### Example 3 (Aggregate) – Second column

This example uses the same duplicated value but searches for a second column.

```
SELECT
RANK(150, 'Minson') WITHIN GROUP (ORDER BY fees_paid, last_name) AS rank_val
FROM student;
```

Result:

RANK_VAL |

5 |

It shows 5, because even though the value of 150 is the 4th lowest in the fees_paid column in the student table, it also searches by last_name, which comes after Armstrong but before Randall.

### Example 4 (Aggregate) – Using NULLS FIRST

This example uses the NULLS FIRST parameter.

```
SELECT
RANK(150) WITHIN GROUP (ORDER BY fees_paid NULLS FIRST) AS rank_val
FROM student;
```

Result:

RANK_VAL |

4 |

It still shows a value of 4, because there are no NULL values in this column.

### Example 5 (Aggregate) – Use Name, but number in Order By

This example attempts to specify a name value, but ordering by the fees_paid column.

```
SELECT
RANK('Tom') WITHIN GROUP (ORDER BY fees_paid) AS rank_val
FROM student;
```

Result:

`ORA-01722: invalid number`

This causes an error, because the RANK parameter needs to be the same data type as the ORDER BY parameter.

### Example 6 (Aggregate) – Use a text value

This example performs a RANK function using a text value (the first_name).

```
SELECT
RANK('Steven') WITHIN GROUP (ORDER BY first_name) AS rank_val
FROM student;
```

Result:

RANK_VAL |

7 |

The result is 7, because the name Steven is 7th in the list when ordered by first_name.

### Example 7 (Aggregate) – Use a text value that is not in the database.

This example ranks a first_name value that is not in the database table.

```
SELECT
RANK('Brad') WITHIN GROUP (ORDER BY first_name) AS rank_val
FROM student;
```

Result:

RANK_VAL |

2 |

The result is 2. Even though Brad is not a record in the table, it would still have been ranked #2.

### Example 8 (Analytical) – Partition by gender

This example partitions (groups) the data by gender, then ranks the values according to fees_paid.

```
SELECT
student_id, first_name, last_name, gender, fees_paid,
RANK() OVER (PARTITION BY gender ORDER BY fees_paid) AS rank_val
FROM student;
```

Result:

STUDENT_ID |
FIRST_NAME |
LAST_NAME |
GENDER |
FEES_PAID |
RANK_VAL |

10 | Tanya | Hall | F | 50 | 1 |

7 | Michelle | Randall | F | 150 | 2 |

6 | Julie | Armstrong | F | 150 | 2 |

2 | Susan | Johnson | F | 500 | 4 |

5 | Steven | Webber | M | 0 | 1 |

4 | Mark | Holloway | M | 100 | 2 |

1 | John | Smith | M | 200 | 3 |

3 | Tom | Capper | M | 350 | 4 |

8 | Andrew | Cooper | M | 800 | 5 |

9 | Robert | Pickering | M | 900 | 6 |

As you can see, each row has a RANK value. There is a duplicate within a gender, as that is expected in the RANK function. The RANK values are separated for M and F gender values.

### Example 9 (Analytical) – Partition by fees paid

This example partitions the data by fees_paid, and ranks according to last name, and first name.

```
SELECT
student_id, first_name, last_name, gender, fees_paid,
RANK() OVER (PARTITION BY fees_paid ORDER BY last_name, first_name) AS rank_val
FROM student;
```

Result:

STUDENT_ID |
FIRST_NAME |
LAST_NAME |
GENDER |
FEES_PAID |
RANK_VAL |

5 | Steven | Webber | M | 0 | 1 |

10 | Tanya | Hall | F | 50 | 1 |

4 | Mark | Holloway | M | 100 | 1 |

6 | Julie | Armstrong | F | 150 | 1 |

7 | Michelle | Randall | F | 150 | 2 |

1 | John | Smith | M | 200 | 1 |

3 | Tom | Capper | M | 350 | 1 |

2 | Susan | Johnson | F | 500 | 1 |

8 | Andrew | Cooper | M | 800 | 1 |

9 | Robert | Pickering | M | 900 | 1 |

As you can see, most of the RANK values are 1 because the fees_paid is mostly unique. Where the fees_paid is not unique, it has been ranked based on last_name then first_name, causing a value of 2 for Michelle Randall.

### Example 10 (Analytical) – No partition

This example uses the RANK function without a partition clause.

```
SELECT
student_id, first_name, last_name, gender, fees_paid,
RANK() OVER (ORDER BY fees_paid) AS rank_val
FROM student;
```

Result:

STUDENT_ID |
FIRST_NAME |
LAST_NAME |
GENDER |
FEES_PAID |
RANK_VAL |

5 | Steven | Webber | M | 0 | 1 |

10 | Tanya | Hall | F | 50 | 2 |

4 | Mark | Holloway | M | 100 | 3 |

6 | Julie | Armstrong | F | 150 | 4 |

7 | Michelle | Randall | F | 150 | 4 |

1 | John | Smith | M | 200 | 6 |

3 | Tom | Capper | M | 350 | 7 |

2 | Susan | Johnson | F | 500 | 8 |

8 | Andrew | Cooper | M | 800 | 9 |

9 | Robert | Pickering | M | 900 | 10 |

Each row has been assigned a RANK value, according to fees_paid.

## Examples of the DENSE_RANK Function

Here are some examples of the Oracle DENSE_RANK function. I find that examples are the best way for me to learn about code, even with the explanation above.

### Sample Data

We’re using a student database table for the examples here.

`SELECT * FROM student ORDER BY fees_paid;`

Result:

STUDENT_ID |
FIRST_NAME |
LAST_NAME |
FEES_REQUIRED |
FEES_PAID |
GENDER |

6 | Julie | Armstrong | 100 | 0 | F |

5 | Steven | Webber | 100 | 80 | M |

1 | John | Smith | 500 | 100 | M |

9 | Robert | Pickering | 110 | 100 | M |

10 | Tanya | Hall | 150 | 150 | F |

2 | Susan | Johnson | 150 | 150 | F |

3 | Tom | Capper | 350 | 320 | M |

8 | Andrew | Cooper | 800 | 400 | M |

4 | Mark | Holloway | 500 | 410 | M |

7 | Michelle | Randall | 250 | F |

### Example 1 (Aggregate) – Basic

This example ranks the value of 100 in the fees_paid column.

```
SELECT
DENSE_RANK(100) WITHIN GROUP (ORDER BY fees_paid) AS rank_val
FROM student;
```

Result:

RANK_VAL |

3 |

It shows 3, because the value of 100 is the 3rd lowest in the fees_paid column.

### Example 2 (Aggregate) – Duplicate Value

This example uses a value that is duplicated in the fees_paid column.

```
SELECT
DENSE_RANK(150) WITHIN GROUP (ORDER BY fees_paid) AS rank_val
FROM student;
```

Result:

RANK_VAL |

4 |

It shows 4, because 150 is the 4th lowest in the fees_paid column in the student table.

### Example 3 (Aggregate) – Second column

This example uses the same duplicated value, but searches for a second column.

```
SELECT
DENSE_RANK(150, 'Minson') WITHIN GROUP (ORDER BY fees_paid, last_name) AS rank_val
FROM student;
```

Result:

RANK_VAL |

7 |

It shows 7, because even though the value of 150 is the 4th lowest in the fees_paid column in the student table, it also searches by last_name, which comes after Armstrong but before Randall.

### Example 4 (Aggregate) – Using NULLS FIRST

This example uses the NULLS FIRST parameter.

```
SELECT
DENSE_RANK(150) WITHIN GROUP (ORDER BY fees_paid NULLS FIRST) AS rank_val
FROM student;
```

Result:

RANK_VAL |

5 |

It shows a value of 5, because there are no NULL values in this column.

### Example 5 (Aggregate) – Use Name, but number in Order By

This example attempts to specify a name value, but ordering by the fees_paid column.

```
SELECT
DENSE_ RANK('Julie') WITHIN GROUP (ORDER BY fees_paid) AS rank_val
FROM student;
```

Result:

`ORA-01722: invalid number`

This causes an error, because the DENSE_ RANK parameter needs to be the same data type as the ORDER BY parameter.

### Example 6 (Aggregate) – Use a text value

This example performs a DENSE_RANK function using a text value (the first_name).

```
SELECT
DENSE_ RANK('Julie') WITHIN GROUP (ORDER BY first_name) AS rank_val
FROM student;
```

Result:

RANK_VAL |

3 |

The result is 3, because the name Julie is 3rd in the list when ordered by first_name.

### Example 7 (Aggregate) – Use a text value that is not in the database.

This example ranks a first_name value that is not in the database table.

```
SELECT
DENSE_RANK('Boris') WITHIN GROUP (ORDER BY first_name) AS rank_val
FROM student;
```

Result:

RANK_VAL |

2 |

The result is 2. This is because Boris is not in the table, but if he was, he would be ranked number 2.

### Example 8 (Analytical) – Partition by gender

This example partitions (groups) the data by gender, then ranks the values according to fees_paid.

```
SELECT
student_id, first_name, last_name, gender, fees_paid,
DENSE_RANK() OVER (PARTITION BY gender ORDER BY fees_paid) AS rank_val
FROM student;
```

Result:

STUDENT_ID |
FIRST_NAME |
LAST_NAME |
GENDER |
FEES_PAID |
RANK_VAL |

6 | Julie | Armstrong | F | 0 | 1 |

2 | Susan | Johnson | F | 150 | 2 |

10 | Tanya | Hall | F | 150 | 2 |

7 | Michelle | Randall | F | 3 | |

5 | Steven | Webber | M | 80 | 1 |

1 | John | Smith | M | 100 | 2 |

9 | Robert | Pickering | M | 100 | 2 |

3 | Tom | Capper | M | 320 | 3 |

8 | Andrew | Cooper | M | 400 | 4 |

4 | Mark | Holloway | M | 410 | 5 |

As you can see, each row has a DENSE_RANK value. There is a duplicate within a gender, as that is expected in the DENSE_RANK function. The RANK values are separated for M and F gender values.

### Example 9 (Analytical) – Partition by fees paid

This example partitions the data by fees_paid, and ranks according to last name, and first name.

```
SELECT
student_id, first_name, last_name, gender, fees_paid,
DENSE_RANK() OVER (PARTITION BY fees_paid ORDER BY last_name, first_name) AS rank_val
FROM student;
```

Result:

STUDENT_ID |
FIRST_NAME |
LAST_NAME |
GENDER |
FEES_PAID |
RANK_VAL |

6 | Julie | Armstrong | F | 0 | 1 |

5 | Steven | Webber | M | 80 | 1 |

9 | Robert | Pickering | M | 100 | 1 |

1 | John | Smith | M | 100 | 2 |

10 | Tanya | Hall | F | 150 | 1 |

2 | Susan | Johnson | F | 150 | 2 |

3 | Tom | Capper | M | 320 | 1 |

8 | Andrew | Cooper | M | 400 | 1 |

4 | Mark | Holloway | M | 410 | 1 |

7 | Michelle | Randall | F | 1 |

As you can see, most of the DENSE_RANK values are 1 because the fees_paid is mostly unique. Where the fees_paid is not unique, it has been ranked based on last_name then first_name, causing a value of 2 for Michelle Randall.

### Example 10 (Analytical) – No partition

This example uses the RANK function without a partition clause.

```
SELECT
student_id, first_name, last_name, gender, fees_paid,
DENSE_RANK() OVER (ORDER BY fees_paid) AS rank_val
FROM student;
```

Result:

STUDENT_ID |
FIRST_NAME |
LAST_NAME |
GENDER |
FEES_PAID |
RANK_VAL |

6 | Julie | Armstrong | F | 0 | 1 |

5 | Steven | Webber | M | 80 | 2 |

1 | John | Smith | M | 100 | 3 |

9 | Robert | Pickering | M | 100 | 3 |

10 | Tanya | Hall | F | 150 | 4 |

2 | Susan | Johnson | F | 150 | 4 |

3 | Tom | Capper | M | 320 | 5 |

8 | Andrew | Cooper | M | 400 | 6 |

4 | Mark | Holloway | M | 410 | 7 |

7 | Michelle | Randall | F | 8 |

Each row has been assigned a DENSE_RANK value, according to fees_paid.

### Example 11 – Compare RANK and DENSE_RANK

This example is similar to example 8, but we are using both RANK and DENSE_RANK to see the differences.

```
SELECT
student_id, first_name, last_name, gender, fees_paid,
RANK() OVER (PARTITION BY gender ORDER BY fees_paid) AS rank_val,
DENSE_RANK() OVER (PARTITION BY gender ORDER BY fees_paid) AS dense_rank_val
FROM student;
```

Result:

STUDENT_ID |
FIRST_NAME |
LAST_NAME |
GENDER |
FEES_PAID |
RANK_VAL |
DENSE_RANK_VAL |

6 | Julie | Armstrong | F | 0 | 1 | 1 |

2 | Susan | Johnson | F | 150 | 2 | 2 |

10 | Tanya | Hall | F | 150 | 2 | 2 |

7 | Michelle | Randall | F | 4 | 3 | |

5 | Steven | Webber | M | 80 | 1 | 1 |

1 | John | Smith | M | 100 | 2 | 2 |

9 | Robert | Pickering | M | 100 | 2 | 2 |

3 | Tom | Capper | M | 320 | 4 | 3 |

8 | Andrew | Cooper | M | 400 | 5 | 4 |

4 | Mark | Holloway | M | 410 | 6 | 5 |

Both the RANK and DENSE_RANK take the same parameters, but you can see the output is different. RANK leaves some numbers out of the sequence, but DENSE_RANK does not show any gaps.

*Image courtesy of digitalart / FreeDigitalPhotos.net*