Can anybody explain how this query works? - oracle

This is a SQL query to find the Nth highest salary of employees:
SELECT *
FROM emp t
WHERE 1 = (SELECT COUNT(DISTINCT sal)
FROM emp t2
WHERE t2.sal > t.sal)
I don't know how it returns the result. If you put 1 in the WHERE clause, it will return the second highest, and for 2 the 3rd highest salary, and so on.
Please explain the query as I am unsure.

Let me start by saying a better way to write the query is:
select e.*
from (select e.*, dense_rank() over (order by sal desc) as seqnum
from emp e
) e
where seqnum = 2;
What is your query doing? Go step-by-step:
The outer query is doing a comparison for every row in emp.
The comparison counts the number of distinct salaries that are larger than the salary in the row.
The row is kept if there is exactly 1 salary that is larger.
In other words, this is keeping all rows that have the second largest salary. dense_rank() is a much saner way to write the query (and it has better performance too).

Related

In case second highest value is missing then use the highest value of salary from employee table

I have an employee table as below. As you can see that second highest salary is 200
Incase the second highest salary is missing then there will be only one row as shown at last . In this case the query should fetch only 100
I have written query as but it is not working. Please help! Thanks
select salary "SecondHighestSalary" from(
(select id,salary,rank() over(order by salary desc) rnk
from employee2)
)a
where (rnk) in coalesce(2,1)
I have also tried the following but it is fetching 2 rows but i need only 1
It sounds like you'd want something like
with ranked_emp as (
select e.*,
rank() over (order by e.sal desc) rnk,
count(*) over () cnt
from employee2 e
)
select salary "SecondHighestSalary"
from ranked_emp
where (rnk = 2 and cnt > 1)
or (rnk = 1 and cnt = 1)
Note that I'm still using rank since you're using that in your approach and you don't specify how you want to handle ties. If there are two employees with the same top salary, rank will assign both a rnk of 1 and no employee would have a rnk of 2 so the query wouldn't return any data. dense_rank would ensure that there was at least one employee with a rnk of 2 if there were employees with at least 2 different salaries. If there are two employees with the same top salary, row_number would arbitrarily assign one the rnk of 2. The query I posted isn't trying to handle those duplicate situations because you haven't outlined exactly how you'd want it to behave in each instance.
If you are in Oracle 12.2 or higher, you can try:
select distinct id,
nvl
(
nth_value(salary, 2) from first
over(partition by id
order by salary desc
range between unbounded preceding and unbounded
following),
salary
) second_max_salary
from employee2

use RANK or DENSE_RANK along with aggregate function

I have a table with the following data:
SCORE ROW_ID NAME
0.4 1011 ABC
0.95 1011 DEF
0.4 501 GHI
0.95 501 XYZ
At any point of time, i only need single row of data with maximum score, if there has more than 1 records, take the one with minimum row_id.
Is it possible to achieve by using RANK or DENSE_RANK function? How about partition by?
MAX(score) keep(dense_rank first order by row_id)
You are looking for max score, one row, so use row_number():
select score, row_id, name
from (select t.*, row_number() over (order by score desc, row_id) rn from t)
where rn = 1
demo
You can use rank and dense_rank in your example, but they can return more than one row, for instance when you add row (0.95, 501, 'PQR') to your data.
keep dense_rank is typically used when searched value is other than search criteria, for instance if we look for salary of employee who works the longest:
max(salary) keep (dense_rank first order by sysdate - hiredate desc)
max in this case means that if there are two or more employees who works longest, but exactly the same number of days than we take highest salary.
max(salary)
keep (dense_rank first order by sysdate - hiredate desc)
over (partition by deptno)
This is the same as above, but salary of longest working employees is shown for each department separately. You can even use empty over() to show salary of longest working employee in separate column except other data like name, salary, hire_date.
You dont need to use dense_rank. This would help
SELECT * FROM (
SELECT
SCORE,
ROW_ID
NAME
FROM T
ORDER BY SCORE DESC, ROW_ID DESC
)
WHERE ROWNUM = 1;

Oracle SQL join query to find highest salary

So I have two tables salary and emp whose definition is shown as below
[
I am tring to create a query that Find the employee who draws the maximum salary and Display the employee details along with the nationality.
I created this query
select empcode,
max(basic) as "Highest Sal"
from salary
join emp on empcode;
Please help with this
Your query uses a simple aggregate max(basic) which would find the highest salary. Except you need to join to the EMP table to display other details. This means you can't use aggregation, because we need to GROUP BY the non-aggregated columns, which would make a nonsense of the query.
Fortunately we can solve the problem with an analytic function. The subquery selects all the relevant information and ranks each employee by salary, with a rank of 1 being the highest paid. We use rank() here because that will handle ties: two employees with the same basic will be in the same rank.
select empcode
, empname
, nationality
, "Highest Sal"
from (
select emp.empcode
, emp.empname
, emp.nationality
, salary.basic as "Highest Sal"
, rank() over (order by salary.basic desc ) as rnk
from salary join emp on emp.empcode = salary.empcode
)
where rnk = 1;
Find the employee who draws the maximum salary
An employee can have multiple salaries in your datamodel. An employee's (total) salary hence is the sum of these. You want to find the maximum salary per employee and show the employee(s) earning that much.
You can use MAX OVER to find the maximum sum:
select e.*, s.total_salary
from emp e
join
(
select
empcode,
sum(basic) as total_salary,
max(sum(basic)) over () as max_total_salary
from salary
) s on s.empcode = e.empcode and s.total_salary = s.max_total_salary
order by e.empcode;
Try this:
SELECT * FROM
(SELECT E.EmpCode, E.EmpName, E.DOB, E.DOJ, E.DeptCode, E.DesgCode, E.PhNo,
E.Qualification, E.Nationality, S.Basic, S.HRA, S.TA, S.UTA, S.OTRate
FROM EMP AS E JOIN SALARY AS S ON (E.EmpCode = S.EmpCode) order by S.Basic desc)
WHERE rownum = 1

Oracle DB query to get records count and calculate average afterward

Could anyone please help me regarding that Oracle query.
Here is my required output.
Thanks in advanced.from below query return some record i want to Calculate
Average means SUM(COST+DURATION)/No.of row return form that query but below query doing partition based on SITE_ID i dont want any partition,What is want is whatever query return (Sum of all Duration+sum of all Cost/No. of row returns)but without any partition is that any way i can do that
select * from (
SELECT ROW_NUMBER() OVER (ORDER BY 1) RN,
SITE_ID,
CASEID,
WOID,
DURATION,
COST,
(SUM(COST+DURATION) OVER(PARTITION BY SITE_ID))/COUNT as TotalCost from
(
SELECT COUNT(*) OVER () as COUNT,
SITE_ID,
CASEID,
WOID,
ROUND(table1.duration/3600,2) AS DURATION,
ROUND(table2.duration/3600),2) AS COST
FROM table1 ) AVG
WHERE (1=1)
)
where rn between 0 and 1000
On the basis of your revised question what I think you want is the total of COST and DURATION for each SITE, plus an average of these figures derived from the number of entries in table1 and table2. This is what this query does:
select site_id
, duration
, cost
, (duration+cost)/no_of as avg_total_cost
from (
select
table1.site_id
, count(*) as no_of
, sum(round(table1.duration/3600,2)) as duration
, sum(round((table1.labor_rate * table2.duration)/3600,2)) as cost
from table1
inner join table2
on table1.id = table2.id
group by table1.site_id
)
/
Here is a SQL Fiddle demo to prove that this query runs successfully.
Your question appears to reference other tables, site and emp, which I have ignored because you don't provide join conditions for them. Likewise I have ignored the top-n filter; you can add it back it if you want.

ORA-00937 error during select subquery

I am attempting to write a query that returns the the number of employees, the average salary, and the number of employees paid below the average.
The query I have so far is:
select trunc(avg(salary)) "Average Pay",
count(salary) "Total Employees",
(
select count(salary)
from employees
where salary < (select avg(salary) from employees)
) UnderPaid
from employees;
But when I run this I get the ora-00937 error in the subquery.
I had thought that maybe the "count" function is what is causing the issue, but even running a simpler sub query such as:
select trunc(avg(salary)) "Average Pay",
count(salary) "Total Employees",
(
select avg(salary) from employees
) UnderPaid
from employees;
still returns the same error. As both AVG and COUNT seem to be aggregate functions, I'm not sure why I'm getting the error?
Thanks
When you use scala subquery, which is a subquery in the select list, it should return only one row.
In general, subquery can return multiple rows. So when you use it in the select list with aggregation function, you should wrap it with aggregation function that has no side effect.
select count(*), (select count(*) from emp) from emp
-- ERROR. Oracle doesn't know that the subquery returns only 1 row.
select count(*), max((select count(*) from emp)) from emp
-- You know that the subquery returns 1 row, applying max() results the same.
Or you can rewrite the query like this:
select avg(salary), count(*), count(case when salary < sal_avg then 1 end)
from (select salary, avg(salary) over () sal_avg from emp);
ntalbs' answer works (thanks, ntalbs!), but see question "ORA-00937: Not a single-group group function - Query error" for a more complete explanation if you want one.

Resources