Hide column from Query - oracle

I don't want rank column but still, want the data in the same format by applying dense rank.
select ename,position,deptno,dense_rank() over(partition by deptno order by ename asc) as rank from emp where deptno in ('10','30');

Why do you need the rank just order by deptno first then ename.
SELECT ename,position,deptno,
FROM emp
WHERE deptno in ('10','30')
ORDER BY DeptNo, Ename
Using the analytical function two options derived table or CTE
Derived table/inline view.
SELECT ename,position,deptno
FROM (select ename,position,deptno,dense_rank() over(partition by deptno order by ename asc) as rank
from emp
where deptno in ('10','30')) Z
ORDER BY deptNo, rank
Common Table Expression (CTE):
with Z AS (SELECT ename,position,deptno
, dense_rank() over(partition by deptno order by ename asc) as rank
FROM emp
WHERE deptno in ('10','30'))
SELECT ename,position,deptno
FROM z
ORDER BY deptno, rank
Both these last 2 techniques simply avoid exposing the rank function to the outer query in which the results are returned. They are "Tricks" and sub-optimal execution time. unless there's a specific reason to have the rank data; I'd not use it.

Related

Employees with the lower salary than the average in their dept

I got two challenges on my college exam to do.
The first one is to Show data for employees (name, salary, and department) who are paid less than the average salary for their department.
I tried this:
SELECT ename, sal, deptno
FROM Emp
WHERE sal < (SELECT AVG(sal) FROM EMP)
GROUP BY deptno;
But i get the ORA-00979: not a GROUP BY expression error.
The other one i didn't started but is quite the same, Show the data of employees (name, commission and department) who receive commission greater than the average commission in their department.
ps: I'm starting with SQL so the topic isn't very deep yet.
You can use a window function for this
SELECT ename, sal, deptno
FROM (
SELECT *,
AVG(sal) OVER (PARTITION BY deptno) AS AvgPerDept
FROM Emp
) AS Emp
WHERE sal < AvgPerDept
Your attempt to the first one is not correct
The expression SELECT AVG(sal) FROM EMP will give the average salary of everyone, regardless which department they're in.
To get the average for each deparment, you will need to GROUP BY dept_no
SELECT Emp.* , dept_salary.average_sal
FROM Emp
INNER JOIN
-- join with an average salary for each department
(
SELECT deptno, AVG(sal) as average_sal
FROM Emp
GROUP BY deptno
) dept_salary
ON Emp.deptno = dept_salary.deptno
WHERE Emp.dept_no = dept_salary.deptno AND Emp.sal < dept_salary.average_sal;

order by What are 1, 2 in alignment?

select empno , deptno , sal from emp
order by 1,2
What are 1, 2 in alignment?
What is one and two? Why don't you write the column name?
The query language defines this as a shortcut.
select empno , deptno , sal from emp
order by 1,2
select empno , deptno , sal from emp
order by empno, deptno
mean precisely the same thing. The numbers refer to the column numbers in your SELECT, counting from 1.
The shortcut comes in handy if you have stuff like
select CONCAT(surname, ', ', givenname) name, empno , deptno , sal from emp
order by 1
because it saves typing. In standard SQL you'd have to write that query
select CONCAT(surname, ', ', givenname) name, empno , deptno , sal from emp
order by CONCAT(surname, ', ', givenname)
Use whichever one you please. But be careful; it makes your ORDER BY clause dependent on the order of items in your SELECT clause. The next person to work on your code may not be expecting such a dependency. Especially if that next person is you.
It works just the same, whether you use
order by empno, deptno
or
order by 1, 2
Note that - although positional sorting requires less typing, you have to synchronize such an order by clause with every select column list rearrangement. I never use it (except for quick & dirty testing queries).

Not sure how to write connect by clause

I have the following query to find employees who are not managers in the EMP table of oracle
select * from emp e1
where not exists (select null from emp e2
where e2.mgr=e1.empno)
I need output using start with connect by clause , thus avoiding self join
There is a function, CONNECT_BY_ISLEAF(), which indicates whether a given row in a hierarchical query is a leaf node. In the EMP table, employees who are not managers will be leaf nodes.
So, we can use this function in a nested hierarchical query to filter the non-managers:
select empno, mgr, ename
from (
select empno, mgr, ename, CONNECT_BY_ISLEAF cbi
from emp
start with mgr is null
connect by prior empno = mgr
) where cbi = 1
/
Oracle has several neat functions for interrogating hierarchies. Find out more.

How can i select just one row in Oracle by row id?

I have used mysql database in my application, but I want to migrate to Oracle.
The problem is in that query:
select * from users limit ?,1;"
That query returns every row one by one depending on ?.
How can i do that in oracle?
On Oracle 12c, you could use the row limiting feature using FETCH FIRST clause.
SQL> SELECT empno, sal, deptno FROM emp ORDER BY empno DESC
2 FETCH FIRST 1 ROWS ONLY;
EMPNO SAL DEPTNO
---------- ---------- ----------
7934 1300 10
SQL>
Prior 12c solution is ROWNUM, however, if you want the row to be first sorted, then you need to do it in a sub-query -
SQL> SELECT empno, sal, deptno FROM
2 ( SELECT * FROM emp ORDER BY empno DESC
3 ) WHERE ROWNUM = 1;
EMPNO SAL DEPTNO
---------- ---------- ----------
7934 1300 10
SQL>
If the order doesn't matter to you, if you just want any random row, simply use ROWNUM.
Depending on your requirement, you could also use ANALYTIC functions such as ROW_NUMBER, RANK, DENSE_RANK.
select * from (select rownum r, u.* from users u ) where r=1;
or if you want it sorted (replace x by columnnumber or columnname):
select * from (select rownum r, u.* from users u order by x) where r=1;

Display Data in own order by using union

I want to display
all emps who are seniors to king and who are juniors to smith as in following order.
who are seniors to king are under king header and juniors to smith are under smith header
I tried this one,
select ename from emp where hiredate<(select hiredate from emp where ename='king')
union
select ename from emp where hiredate>(select hiredate from emp where ename='smith');
OUTPUT is only one header(ENAME)
How can i get my desired output(Two Headers KING SMITH)
Can any one help me
Addding another artificial column is a little trick:
select 1 status, ename from emp
where hiredate<(select hiredate from emp where ename='king')
union all
select 2 status, ename from emp
where hiredate>(select hiredate from emp where ename='smith')
order by 1;
You can even use a union all instead of union since all lines will be different. Or, if you really need headers/separators:
select 0 status, 'KING''S SENIORS' from dual
union all
select 1 status, ename from emp where hiredate<(select hiredate from emp where ename='king')
union all
select 2 status, 'SMITH''S JUNIORS' from dual
union all
select 3 status, ename from emp where hiredate>(select hiredate from emp where ename='smith')
order by 1;
may be this one will be useful
select e.ename,
case when e.hiredate < khd.hiredate then 1 else 0 end king_header,
case when e.hiredate > shd.hiredate then 1 else 0 end smith_juniour
from emp e,
(select hiredate from emp where ename='king') khd,
(select hiredate from emp where ename='smith') shd
where e.hiredate < khd.hiredate or e.hiredate > shd.hiredate

Resources