Stuck with my query - sql

Would really appreciate if someone could help me with my query.
I need it to show the difference between max salary and salary of others.
select e.ename, f.sal, e.sal
from emp e , (select max(sal) as "sal" from emp) f
where 1=1 ;
Where am I making the mistake?
Thanks in advance!

select
name
, sal
, max_sal - sal as diff
from (
select
e.ename
, e.sal
, max(salary) over() as max_sal
from emp e
) d
I suggest using max(salary) over() analytic (or window) function which places the maximum salary on every row of the subquery, then it is a simple subtraction to calculate the difference. No group by clause is required in this form of aggregate.

select e.ename,
e.sal sal_of_others,
(select max(sal) from emp where ename = e.ename ) - e.sal sal_diff
from emp e

No need to use where condition unnecessarily and also you can use subquery as shown below
SELECT e.ename, e.sal, (select max(sal) from emp) - E.Sal as difference
FROM emp e

You can use an analytic function to find the maximum salary - this negates the need to query the table twice:
select ename,
sal,
max(sal) over () max_overall_sal,
max(sal) over (partition by deptno) max_sal_per_dept
from emp;
I've given you two ways to find the max salary - one across the whole data set (over ()) and one per department (over (partition by deptno)) since I wasn't sure which you wanted.
If you are unfamiliar with analytic functions, I highly recommend you look them up. In their basic form, they're similar to how aggregate functions work, except instead of collapsing the rows, they report the value for each row. The partition by clause groups the data in the same way as the group by does in an aggregate query.

use the below query
select ename,sal,((select max(sal) from emp)-sal) as saldiff from emp

Related

GROUP BY - inline view and unjoined tables

I have the following ORACLE query where I attempt to find the department with the highest average salary. I would like to use in-line view (i.e. retain the b dataset) for this implementation, but struggle to get the right part at the WHERE and GROUP BY components. I know the below GROUP BY and WHERE (which is non-existant) is wrong. But how do i correct them?
select a.deptno from emp a,
(select max(avg_sal) max_avg_sal from (select
avg(sal) avg_sal from emp group by deptno) ) b
group by a.deptno, b.max_avg_sal
having avg(a.sal) = b.max_avg_sal
Expected Result
deptno
10
Emp Structure
deptno staff sal
10 A 1000
10 B 1500
11 C 1100
12 D 1000
12 E 900
12 F 1000
Is this what you want?
select e.*
from (select e.*, avg(e.salary) over (partition by e.deptno) as avg_salary
from emp e
) e
order by avg_salary desc
fetch first 1 row only;
fetch first is available in Oracle 12c+. You can do similar things with an additional subquery in earlier versions.
You can use subquery
select deptno from tablename
group by deptno
having avg(sal)= (select max(asal) from (select avg(sal) as asal from tablename group by deptdno)A)
The straight-forward way is:
select deptno
from emp
group by deptno
order by avg(salary) desc
fetch first row with ties;
FETCH FIRST is available as of Oracle 12c.
In Oracle 11g we could use this instead:
select deptno
from
(
select deptno, avg(salary) as avg_salary, max(avg(salary)) over () as max_avg_salary
from emp
group by deptno
)
where avg_salary = max_avg_salary;
But you want an inline view, another word for a derived table (a subquery in the from clause). That looks way more clumsy. One example without FETCH FIRST and without window functions:
with d as
(
select deptno, avg(salary) as avg_salary
from emp
group by deptno
)
, dmax as
(
select max(avg_salary) as max_avg_salary
from d
)
select d.*
from d
join dmax on dmax.max_avg_salary = d.avg_salary;
I find this very obfuscated and don't recommend it at all. You can do the same without WITH clauses of course. Then it is even less readable.
I don't know why you'd want to write it this way, but if you really want only inline views and no windowing clauses, you can write it this way:
select b.deptno
from (SELECT deptno, avg(sal) avgsal from emp group by deptno ) b
cross join (SELECT max(avgsal) maxavgsal FROM (SELECT avg(sal) avgsal FROM emp group by deptno )) c
where b.avgsal = c.maxavgsal;
This the same thing, if you don't like CROSS JOIN for some reason:
select b.deptno
from (SELECT deptno, avg(sal) avgsal from emp group by deptno ) b
inner join ( SELECT max(avgsal) maxavgsal FROM
( SELECT avg(sal) avgsal FROM emp group by deptno ) ) c
on b.avgsal = c.maxavgsal;

I want to display some columns of a table based on a subquery

I want to Display department number, names and salaries of employees who are earning max salary in their departments which are in the same table.
I am using oracle sql. The Table structure I am using is
Emp(Empno,Ename,Job,Salary,Deptno)
I have read about this and I think that this can be done by the use of correlated sub-queries. The query I fired was
select E1.Ename,E1.Ename,E1.Salary
from Emp E1
where E1.Empno=(
select Empno
from Emp E2
where Salary=(
select max(Salary)
from Emp
where Deptno=E1.Deptno
)
);
This gives an error saying "single-row subquery returns more than one row".
What am I doing wrong? What should do to correct it?
SELECT EmpNo, Ename,Job,Salary,Deptno
FROM
(
SELECT EmpNo, Ename,Job,Salary,Deptno,
DENSE_RANK() OVER (PARTITION BY DeptNo
ORDER BY Salary DESC ) rn
FROM Emp
) a
WHERE a.rn = 1
DENSE_RANK
or by using MAX
SELECT a.*
FROM Emp a
INNER JOIN
(
SELECT DeptNo, MAX(Salary) Max_sal
FROM Emp
GROUP BY DeptNo
) b ON a.DeptNo = b.DeptNo AND
a.Salary = b.Max_SAL

What is wrong in the below query?

I have written this query on oracle database.
Select salary from emp having salary < (SELECT MAX(SALARY) FROM EMP ));
http://sqlfiddle.com/#!4/47961/13
But it is not working .
Where am i wrong?
things to do,
use WHERE (use having when comparing with aggregated condition)
remove extra parenthesis,
eg.
Select salary
FROM emp
WHERE salary < (SELECT MAX(SALARY) FROM EMP );
SQLFiddle Demo
Two errors:
Use WHERE instead of HAVING.
Remove the extra parenthesis.
Try this:
SELECT salary
FROM emp
WHERE salary < (SELECT MAX(salary) FROM emp);
http://sqlfiddle.com/#!4/47961/17
Select salary from emp where salary < (SELECT MAX(SALARY) FROM EMP ));
you can apply having clause where you used group by , here there is no group by than just use where
The HAVING Clause
The HAVING clause was added to SQL because the WHERE keyword could not be used with aggregate functions.
Select salary from emp WHERE SALARY < (SELECT MAX(SALARY) FROM EMP );
Correct way using Having in Oracle:
SELECT sal FROM scott.emp
GROUP BY sal
HAVING sal < (SELECT MAX(SAL) FROM scott.EMP)
/
SELECT deptno, COUNT(*) total_emps_by_dept
FROM scott.emp
GROUP BY deptno
HAVING COUNT(*) > 1
/

SQL Subquery and joins

There are 2 tables available
EMP(empname PK, empno, sal, comm, deptno, hiredate, job, mgr)
DEPT(depnto, dname, loc)
The queries are
a)Display the ename and dname who is earning the first highest salary
They have given a statement this has to be done using only subqueries and Join
So i started like this:
select A.ename, B.dname from emp A, dept B where A.deptno=B.deptno
and i tried various perm&comb, but i couldn't get the join statement...
This is not a homework problem, i am just trying to solve the exercise problem given in my textbook..Thanks in advance
To get the record with the max salary I've Ordered by e.sal DESC. This will order the records in terms of salary, with the highest at the top (DESC = Descending, and therefore highest-lowest).
Then I've used TOP 1 to only return 1 record.
To get the dname I've joined the tables relating the 2 deptno columns.
SELECT TOP 1 e.empname
,d.dname
FROM [EMP] e
JOIN [DEPT] d ON d.deptno=e.deptno
ORDER BY e.sal DESC
I hope this helps
You can try following query:
SELECT emp.ename,dept.dname FROM emp
JOIN dept ON emp.deptno=dept.deptno
WHERE emp.sal=(SELECT MAX(sal) FROM emp)

Combining columns

I am trying to do the following:
I have a table with ename, job, deptno, and sal. I am trying to initiate a query that returns the top earners of each department. I have done this with grouping and a subquery. However, I also want to display the average sal by deptno. So the following would be the result:
"ename" "dept" "sal" "average of dept"
sal 20 1000 500
kelly 30 2000 800
mika 40 3000 400
this might be impossible since the average does not associate with the other rows.
any suggestion would be appreciated. Thanks. I am using Oracle 10g to run my queries.
You could use analytic functions:
WITH RankedAndAveraged AS (
SELECT
ename,
dept,
sal,
RANK() OVER (PARTITION BY dept ORDER BY sal DESC) AS rnk,
AVG(sal) OVER (PARTITION BY dept) AS "average of dept"
FROM atable
)
SELECT
ename,
dept,
sal,
"average of dept"
FROM RankedAndAveraged
WHERE rnk = 1
This may return more than one employee per department if all of them have the same maximum value of sal. You can replace RANK() with ROW_NUMBER() if you only want one person per department (in which case you could also further extend ORDER BY by specifying additional sorting criteria to pick the top item, otherwise it will be picked randomly from among those with the maximum salary).
This should work. The only trick is that if you have several employees with the maximum salary in a department, it will show all of them.
SELECT t.ename, t.deptno, mx.sal as sal, mx.avg_sal as avg_sal
FROM tbl t,
(SELECT MAX(sal) AS sal, AVG(sal) AS avg_sal, deptno
FROM tbl
GROUP BY deptno) mx
WHERE t.deptno = mx.deptno AND t.sal = mx.sal
Not sure about Oracle, haven't used it in about 10 years, but something like this should be possible:
SELECT
ename, deptno, sal,
(SELECT AVG(T2.sal)
FROM tbl T2
WHERE T2.deptno = T.deptno
) AS average_of_dept
FROM tbl T
GROUP BY deptno
HAVING sal = MAX(sal)