365 Days of Daily Coding: Day 8

I did a Leetcode challenge today instead of a HackerRank challenge cause I find that the challenges in HackerRank are not easily understandable whereas description of challenges in Leetcode are short and concise.

The challenge: Given the ID no. and Salary, I had to find out the nth highest salary.

IDSalary
589100
590200
591300
Sample of the table ‘Employee’

Expected Output:

getNthHighestSalary(2)
200
Sample of the expected output

My initial code which did not give the expected output is as follows:

CREATE FUNCTION getNthHighestSalary(N IN NUMBER) 
 RETURN NUMBER 
 IS result NUMBER;
 BEGIN
     SELECT salary
     INTO result
     FROM (SELECT DISTINCT salary
           FROM Employee order by salary desc)
     WHERE ROWNUM = N and ROWNUM <=1;
     RETURN result;
 END getNthHighestSalary;

When tested against a query to fetch the second highest salary, it returns a null. I can’t figure out why it is failing. I will have to approach my colleague for guidance. The other solution that did work out was from the discussion forums and below are the solutions.

Solution 1:

 CREATE FUNCTION getNthHighestSalary(N IN NUMBER) 
 RETURN NUMBER 
 IS result NUMBER;
 BEGIN
     /* Write your PL/SQL query statement below */
     SELECT salary
     INTO result
     FROM (SELECT salary, DENSE_RANK() OVER (ORDER BY Salary Desc) 
           as r
           FROM Employee)
     WHERE r = N and ROWNUM <=1;
     RETURN result;
 END getNthHighestSalary; 

Breaking down the code:

Query 1:

This was my first time encountering a function so I had to do a little study. Besides, readily available functions like LEN(), MAX() and so on, users are also able to create user defined functions. The skeleton to create a user defined functions looks like below as taken from a source.

 CREATE [OR REPLACE] FUNCTION function_name
    [ (parameter [,parameter]) ]
 RETURN return_datatype
 IS | AS
 [declaration_section]
 BEGIN
    executable_section
 [EXCEPTION
    exception_section]
 END [function_name]; 

Example of creating a function. Source

The following statement creates the function get_bal on the sample table oe.orders (the PL/SQL is in italics)

 CREATE FUNCTION get_bal(acc_no IN NUMBER) 
    RETURN NUMBER 
    IS acc_bal NUMBER(11,2);
    BEGIN 
       SELECT order_total 
       INTO acc_bal 
       FROM orders 
       WHERE customer_id = acc_no; 
       RETURN(acc_bal); 
     END; 

Hence in this solution, I have created a function called ‘getNthHighestSalary” which is of the datatype Number and returns the variable ‘result’.

 CREATE FUNCTION getNthHighestSalary(N IN NUMBER) 
 RETURN NUMBER 
 IS result NUMBER;

Query 2:

Another aspect of the solution is the understanding of the ‘ROWNUM’ and ‘DENSE_RANK()’.

The function ‘ROWNUM’ returns a number of a selected row from a table or joined table. Source

SELECT salary, ROWNUM 
FROM Employee
ORDER BY salary DESC;
IDSalaryROWNUM
5913001
5902002
5891003
Sample of the query with ‘ROWNUM’

The ‘DENSE_RANK()’ function on the other hand, assigns rank consecutively to each of the records without skipping any numbers. There is another function called ‘RANK’ which behavies similar to a ‘DENSE_RANK()’ function however, if there are two records with the same ranking, the rank number is skipped. The following point taken from a stackoverflow perfectly illustrates the point.

with q as (
 select 10 deptno, 'rrr' empname, 10000.00 sal from dual union all
 select 11, 'nnn', 20000.00 from dual union all
 select 11, 'mmm', 5000.00 from dual union all
 select 12, 'kkk', 30000 from dual union all
 select 10, 'fff', 40000 from dual union all
 select 10, 'ddd', 40000 from dual union all
 select 10, 'bbb', 50000 from dual union all
 select 10, 'xxx', null from dual union all
 select 10, 'ccc', 50000 from dual)
 select empname, deptno, sal
      , rank() over (partition by deptno order by sal nulls first) r
      , dense_rank() over (partition by deptno order by sal nulls first) dr1
      , dense_rank() over (partition by deptno order by sal nulls last) dr2
  from q;
EMPDEPTNOSALRDR1DR2
Lalice10114
Jenny1010000221
Minzy1040000332
Chuck1040000332
Tom1050000543
Janice1050000543
Lulu11500111
Mike1120000222
Matt1230000111
Sample of query with ‘DENSE_RANK()’

With the partiton by arugument missing, the below query ranks the salary by descending order.

SELECT salary, DENSE_RANK() OVER (ORDER BY Salary Desc) 
     as r
     FROM Employee;
IDSalaryr
5913001
5902002
5891003
Sample of the original query with ‘DENSE_RANK()’

Query 3: The query returns the nth highest salary and passes it to the variable ‘result’.

SELECT salary
     INTO result
     FROM (SELECT salary, DENSE_RANK() OVER (ORDER BY Salary Desc) 
           as r
           FROM Employee)
     WHERE r = N AND ROWNUM <=1;

Output:

IDSalaryr
5913001
Sample of output of Query 3

It may seem unnecessary to limit selection to only one row, ‘ROWNUM <=1’. In real world, there could be multiple records with the same salary and hence, same rank, hence, we only want to display the first nth salary.

Solution 2: This was another solution in the forum but was originally submitted for MSSQL so I translated it into Oracle and this solution has a much faster runtime than the first solution.

 CREATE FUNCTION getNthHighestSalary(N INT) 
 RETURN INT
 IS result INT;
 BEGIN
       WITH ranks AS
       (SELECT
         salary,
         row_number() OVER (ORDER BY salary DESC) AS salaryRank
       FROM
         (SELECT DISTINCT
             salary
          FROM employee) )
     SELECT salary
     INTO result
     FROM (
       SELECT
         salary
       FROM
         ranks
       WHERE
         ranks.salaryRank = N);
   RETURN result;
 END; 

The above solution is pretty intuitive hence, I will not be breaking down the queries.

I have been wanting to take the Oracle SQL certification for sometime and have been doing research on it. I will create a post to share on it.

It’s a debate as to whether certification are good investments. In my perspective, these certification are motivation to strive for excellence at the same time a proof of your knowledge. That ofcourse may not necessarily mean that you are good which I believe can only be acquired by experience and trial and error experiments.

One response to “365 Days of Daily Coding: Day 8”

  1. […] function DENSE_RANK() was explained yesterday in my blog for Day 8. That’s it for today. I took a quite a chill pill today but I plan to solve more of the […]

    Like

Leave a comment