I have a table on employees like this:
ENAME JOB
Jack Clerk
Adam Manager
Raphael President
And my idea is to get the names that have the maximum length and the minimum length (1 per each) and in case the length is the same, take the one that goes first alphabetically (for example in the case of Jack and Adam, both with 4 characters, it would take Adam):
ENAME LENGTH
Adam 4
Raphael 7
I couldn't find the way of doing it in one unique query so I tried to join two queries but it keeps giving me error ('ORA-00933: SQL command not properly ended') and I don't know why:
SELECT ENAME, LENGTH(ENAME) FROM EMP
GROUP BY ENAME
HAVING LENGTH(ENAME) = (SELECT MAX(LENGTH(ENAME)) FROM emp)
ORDER BY ENAME
FETCH FIRST 1 ROW ONLY
union all
SELECT ENAME, LENGTH(ENAME) FROM EMP
GROUP BY ENAME
HAVING LENGTH(ENAME) = (SELECT MIN(LENGTH(ENAME)) FROM emp)
ORDER BY ENAME
FETCH FIRST 1 ROW ONLY;
SELECT ENAME, LENGTH(ENAME) FROM EMP
GROUP BY ENAME
HAVING LENGTH(ENAME) = (SELECT MAX(LENGTH(ENAME)) FROM emp)
--ORDER BY 1
--FETCH FIRST 1 ROW ONLY
union
SELECT ENAME, LENGTH(ENAME) FROM EMP
GROUP BY ENAME
HAVING LENGTH(ENAME) = (SELECT MIN(LENGTH(ENAME)) FROM emp)
ORDER BY 1
FETCH FIRST 1 ROW ONLY;
One method uses aggregation and union all:
select min(name) keep (dense_rank first order by len(name) asc), min(len(name))
from emp
union all
select min(name) keep (dense_rank first order by len(name) desc, max(len(name))
from emp;
The keep syntax is Oracle's rather verbose way of having a "first" aggregation function.
Related
I need to show the names of the oldest employee and of the last one so i need to compare datetime of the hire_date, here's how i tried and its not working:
SELECT ename
from emp
where hiredate = 'MIN(hiredate)' AND hiredate = 'MAX(hiredate)';
what should i do?
For example:
SQL> with minimax as
2 (select min(hiredate) min_hiredate,
3 max(hiredate) max_hiredate
4 from emp
5 )
6 select e.ename, e.hiredate
7 from emp e join minimax m on e.hiredate in (m.min_hiredate, m.max_hiredate);
ENAME HIREDATE
---------- ----------
SMITH 17.12.1980
ADAMS 12.01.1983
SQL>
I would recommend window functions:
select *
from (
select e.*,
rank() over(order by hire_date) rn_asc,
rank() over(order by hire_date desc) rn_desc
from emp e
) e
where 1 in (rn_asc, rn_desc)
The subquery ranks employees by increasing and decreasing hire date, the the outer query selects the top and bottom records. This allows ties, if any.
The same logic can be implemented with a window min and a window max:
select *
from (
select e.*,
min(hire_date) over() min_hire_date,
max(hire_date) over() max_hire_date
from emp e
) e
where hire_date in (min_hire_date, max_hire_date)
In Oracle, you can do this with a "simple" aggregation query:
select max(ename) keep (dense_rank first order by hiredate asc) as oldest_employee,
max(ename) keep (dense_rank first order by hiredate desc) as newest_employee
from emp;
You can also use union all if you want this information on different rows:
(select name, hiredate
from emp
order by hiredate
fetch first 1 row only
) union all
(select name, hiredate
from emp
order by hiredate desc
fetch first 1 row only
);
This question already has answers here:
How to find the employee with the second highest salary?
(5 answers)
Closed 3 years ago.
I have to get the name of employee with the second highest salary the table name from where I am fetching is emp. I know the query for second highest salary which is
select max(sal)
from emp
where sal < (select max(sal) from emp)
it works and it returns the right answer.
But I have to get the name of the employee as well. I simply tried
select name, max(sal)
from emp
where sal < (select max(sal) from emp)
I get this error:
ORA-00937: not a single-group group function
how can i remove the error in order to get the name and salary both.
thank you to anyone who helps.
You can use
select name,sal from emp where sal = (select max(sal) from emp where sal < (select max(sal) from emp));
use this :
with cte (
select ROW_NUMBER() over(order by sal desc)rnum ,name,sal from emp )
select * from cte where rnum = 2
You can get this easily with a window function. Try something like this:
SELECT name, sal
FROM emp
QUALIFY RANK OVER(ORDER BY sal DESC) = 2
This will order your rows by Salary and then give each row a ranking. Then it will return the rows with ranking = 2.
If you want to ensure you only get one row back, change RANK to ROW_NUMBER.
Practicing some SQL, we have to get the name of the employees whose salary is the greatest of his department. But if in any department there were more than one employer with the greatest salary, we would not have to consider that department.
We got the first part but not the second one (because there are two employees with the same greatest salary (3,000) in the same department (20)).
This is what we did:
SQL> SELECT ename, sal, deptno FROM emp a
WHERE sal >= ALL (SELECT sal FROM emp WHERE deptno=a.deptno)
ORDER BY sal;
And this is what we got:
ENAME SAL DEPTNO
---------- ------- ------
BLAKE 2,850 30
FORD 3,000 20
SCOTT 3,000 20
KING 5,000 10
4 filas seleccionadas.
Any help will be useful, thank you!
SELECT ename, sal, deptno
FROM emp a
WHERE not exists (
SELECT *
FROM emp
WHERE deptno=a.deptno
and sal >= a.sal
and ename != a.ename)
ORDER BY sal;
with cte as
( SELECT ename, sal, deptno
, row_number() over (partition by deptno order by sal desc) as rn
FROM emp
)
select ename, sal, deptno from cte where rn = 1
except
select ename, sal, deptno from cte where rn = 2
order by sal
if this does not work in oracle - it used to be also tagged mssql
You can have what you need with some analytic functions:
select ename,
deptno,
sal
from (
select ename,
deptno,
sal,
row_number() over(partition by deptno order by sal desc) AS num,
count(1) over(partition by deptno, sal) AS count
from emp
)
where num = 1
and count = 1
The inner query orders by salary and counts the number of employees with the same salary in the same department; the outer one simply filters for employees with the maximum salary, where only one employee has that salary in the department.
With a different approach, simply modifying your query, you can try:
SELECT ename, sal, deptno FROM emp a
WHERE sal >= ALL (SELECT sal FROM emp WHERE deptno=a.deptno)
and (select count(1) from emp b where a.deptno = b.deptno and a.sal = b.sal) = 1
The first way gives better performance, with a single table scan, while the second one needs a nested query, thus being less efficient
try to use GROUP BY column_name`, it will show the record without duplicate.
Say there's a table
Name Salary
Joe 4000
Steve 6000
I could just do this
select name from emp where salary = (select max(salary) from emp);
but is there a way to do this without using a subquery?? Please help.
EDIT: Sorry I forgot to mention that I'm using Oracle 10g and LIMIT doesn't work on it
You didn't mention the version of Oracle.
On Oracle 12 there is a new low limiting clause that can be used:
SELECT name
FROM emp
ORDER BY salary desc
FETCH FIRST 1 ROWS ONLY;
There are examples in documentation: https://docs.oracle.com/database/121/SQLRF/statements_10002.htm#BABEAACC
On earlier versions it can't be done without using a subquery, but if you must then create a view:
CREATE VIEW emp_ordered AS
SELECT *
FROM emp
ORDER BY salary desc;
and then query this view in this way:
SELECT * FROM emp_ordered
WHERE rownum <=1
ANSI SQL answer (no dbms specified):
select Name, Salary
from emp
order by Salary desc
fetch first 1 row only
Edit: Will work with newer Oracle versions.
In Oracle 12c, the top-n row limiting feature is introduced. Which allows to ORDER the rows without an additional subquery. So, no more dependency on ROWNUM and explicit sorting in a subquery.
For example,
SQL> SELECT ename, sal FROM emp ORDER BY sal DESC
2 FETCH FIRST 1 row only;
ENAME SAL
---------- ----------
KING 5000
SQL>
Update Regarding duplicate rows
There is an option WITH TIES which will include the duplicate rows.
For example,
SQL> insert into emp(empno, ename, sal) values(1234, 'LALIT', 5000);
1 row created.
SQL> SELECT ename, sal FROM emp ORDER BY sal DESC FETCH FIRST 1 ROWS WITH TIES;
ENAME SAL
---------- ----------
KING 5000
LALIT 5000
SQL>
Try this
SELECT name FROM emp
ORDER BY salary DESC
LIMIT 1
Try
select * from emp order by salary DESC limit 1
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)