Row Number In oracle - oracle

I want to fetch data based on the last row number.
I want the records highlighted in yellow color. Please guide.

Should be a simple MAX function.
select max(row_number) rn,
account_no
from your_table
group by account_no
order by account_no;
If "row_number" represents the result of analytic function (which isn't clear from what you posted so far), then include ORDER BY clause into the function (I don't know which column you're sorting data on) in descending order so that your "max" actually becomes "min" whose RN = 1 and then it is easy to select it as a final result.
with temp as
(select columnb,
columnc,
row_number() over (partition by accountno order by SOMETHING desc) rn
^^^^^^^^^^^^^^^^^^^^^^^
add this
from some_table
)
select columnb,
columnc
from temp
where rn = 1
As I don't have your tables, here's Scott's EMP:
SQL> select * from emp;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- -------- ---------- ---------- ----------
7369 SMITH CLERK 7902 17.12.80 920 20
7499 ALLEN SALESMAN 7698 20.02.81 1600 300 30
7521 WARD SALESMAN 7698 22.02.81 1250 500 30
7566 JONES MANAGER 7839 02.04.81 2975 20
7654 MARTIN SALESMAN 7698 28.09.81 1250 1400 30
7698 BLAKE MANAGER 7839 01.05.81 2850 30
7782 CLARK MANAGER 7839 09.06.81 2450 10
7788 SCOTT ANALYST 7566 09.12.82 3000 20
7839 KING PRESIDENT 17.11.81 10000 10
7844 TURNER SALESMAN 7698 08.09.81 1500 0 30
7876 ADAMS CLERK 7788 12.01.83 1100 20
7900 JAMES CLERK 7698 03.12.81 950 30
7902 FORD ANALYST 7566 03.12.81 3000 20
7934 MILLER CLERK 7782 23.01.82 1300 10
14 rows selected.
Your code would then be like this; note line #6 which calculates row number as I suggested; you'll use it in the final WHERE clause (line #11), while presenting that "max" row number value you desperately wanted.
SQL> with temp as
2 (select deptno,
3 sal,
4 row_number() over (partition by deptno order by sal) rn,
5 --
6 row_number() over (partition by deptno order by sal desc) rnd
7 from emp
8 )
9 select deptno, sal, rn
10 from temp
11 where rnd = 1
12 /
DEPTNO SAL RN
---------- ---------- ----------
10 10000 3
20 3000 4
30 2850 6
SQL>

Related

Rank the managers based on how many employees they have

[Update]
Thanks for all the comments!
Appreciated!
I solved this by the code below, after referring to all of your posts.
I did not put into account of hierarchy but I will for the later works.
Thank you!
SELECT m.first_name
, m.last_name
, RANK() over (partition by (select COUNT(e.employee_id)
from DB1_employee e)
ORDER BY e.employee_id DESC) AS RANK from DB2_manager m LEFT JOIN RITDB_employee e ON m.employee_id=e.manager
I am having difficulty to troubleshoot my code to rank the managers
based on the number of employees they have. The error is "missing right parenthesis". Any helps would be highly appreciated. Thanks!
SELECT m.first_name
, m.last_name
, RANK() over (partition by (select COUNT(e.employee_id)
from DB1_employee e) AS NUM_EMP
ORDER BY NUM_EMP DESC) AS RANK
from DB2_manager m
, DB1_employee e
group by m.first_name, m.last_name
ORDER BY RANK
An example based on Scott's schema (as I don't have your tables nor data), so that it is easier to see what should be returned.
SQL> select mgr, empno, ename, job from emp order by mgr;
MGR EMPNO ENAME JOB
---------- ---------- ---------- ---------
7566 7902 FORD ANALYST --> MGR 7566 (Jones) has 2 employees
7566 7788 SCOTT ANALYST
7698 7900 JAMES CLERK --> 7698 (Blake) has 5 employees
7698 7499 ALLEN SALESMAN
7698 7521 WARD SALESMAN
7698 7844 TURNER SALESMAN
7698 7654 MARTIN SALESMAN
7782 7934 MILLER CLERK --> 7782 (Clark) has 1 employee
7788 7876 ADAMS CLERK --> 7788 (Scott) has 1 employee
7839 7698 BLAKE MANAGER --> 7839 (King) has 3 employees
7839 7566 JONES MANAGER
7839 7782 CLARK MANAGER
7902 7369 SMITH CLERK --> 7902 (Ford) has 1 employee
7839 KING PRESIDENT
14 rows selected.
SQL>
So:
SQL> select m.ename mgrname,
2 count(*) cnt,
3 dense_rank() over (order by count(*) desc) rnk
4 from emp e join emp m on e.mgr = m.empno
5 where e.mgr is not null
6 group by e.mgr, m.ename
7 order by rnk;
MGRNAME CNT RNK
---------- ---------- ----------
BLAKE 5 1
KING 3 2
JONES 2 3
SCOTT 1 4
FORD 1 4
CLARK 1 4
6 rows selected.
SQL>
However:
That's not correct. Why? Because of hierarchy. Not all managers share the same level. Have a look: King is the president; he is the big boss and manages them all; how can he be ranked as #2? The same goes for the rest of them.
SQL> select lpad(' ', 2 * level - 2) || e.empno ||' '|| e.ename val
2 from emp e
3 connect by prior e.empno = e.mgr
4 start with e.mgr is null;
VAL
--------------------------------------------------------------------
7839 KING
7566 JONES --> Jones has 4 employees!!! (Scott, Adams, Ford and Smith)
7788 SCOTT --> Scott has 1 employee (that's Adams)
7876 ADAMS
7902 FORD --> Ford has 1 employee (that's Smith)
7369 SMITH
7698 BLAKE
7499 ALLEN
7521 WARD
7654 MARTIN
7844 TURNER
7900 JAMES
7782 CLARK
7934 MILLER
14 rows selected.
SQL>
Therefore, we need another approach. Begin with a simple query which, basically, returns "root" for each of them:
SQL> select connect_by_root(ename) manager
2 from emp
3 connect by prior empno = mgr;
MANAGER
----------
SCOTT
SCOTT
FORD
FORD
ALLEN
JAMES
<snip>
KING
KING
39 rows selected.
SQL>
It is further used as a source for
SQL> select x.mgrname,
2 count(*) - 1 cnt
3 from (select connect_by_root(e.ename) mgrname
4 from emp e
5 connect by prior e.empno = e.mgr
6 ) x
7 group by x.mgrname;
MGRNAME CNT
---------- ----------
ALLEN 0
JONES 4
FORD 1
MILLER 0
CLARK 1
WARD 0
SMITH 0
SCOTT 1
TURNER 0
MARTIN 0
ADAMS 0
JAMES 0
BLAKE 5
KING 13
14 rows selected.
SQL>
Finally, remove those that don't have any employees and rank them; the result is quite different than the first (the most obvious, but probably wrong) approach:
SQL> select r.mgrname,
2 r.cnt,
3 dense_rank() over (order by cnt desc) rnk
4 from (select x.mgrname,
5 count(*) - 1 cnt
6 from (select connect_by_root(e.ename) mgrname
7 from emp e
8 connect by prior e.empno = e.mgr
9 ) x
10 group by x.mgrname
11 ) r
12 where r.cnt > 0
13 order by rnk;
MGRNAME CNT RNK
---------- ---------- ----------
KING 13 1
BLAKE 5 2
JONES 4 3
SCOTT 1 4
FORD 1 4
CLARK 1 4
6 rows selected.
SQL>
First of all
Your query is doing a cross join, and then grouping by manager name. There can be an issue if two managers have the same name. Better to use PK and FK.
Don't use old join syntax
NUM_EMP. has no reference in your query and still used in the ORDER BY.
I am considering that DB1_employee table must have some column referring to its manager(let's say, MANAGER_FK ). So writing the query accordingly as following:
SELECT
M.FIRST_NAME,
M.LAST_NAME,
RANK() OVER(
ORDER BY
E.EMPLOYEE_CNT DESC
) AS RNK
FROM
DB2_MANAGER M
JOIN (
SELECT
MANAGER_FK,
COUNT(1) AS EMPLOYEE_CNT
FROM
DB1_EMPLOYEE
GROUP BY
MANAGER_FK
) E ON ( M.MANAGER_ID = E.MANAGER_FK )
ORDER BY
RNK;
Cheers!!
try:
SELECT m.first_name
, m.last_name
, RANK() over (partition by (select COUNT(e.employee_id)
from DB1_employee e)
ORDER BY NUM_EMP DESC) AS RANK
from DB2_manager m
, DB1_employee e
group by m.first_name, m.last_name
ORDER BY RANK
;

display the empno,ename of employees who where count of job>1

EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
7839 KING PRESIDENT - 11/17/1981 5000 - 10
7698 BLAKE MANAGER 7839 05/01/1981 2850 - 30
7782 CLARK MANAGER 7839 06/09/1981 2450 - 10
7566 JONES MANAGER 7839 04/02/1981 2975 - 20
7788 SCOTT ANALYST 7566 12/09/1982 3000 - 20
7902 FORD ANALYST 7566 12/03/1981 3000 - 20
7369 SMITH CLERK 7902 12/17/1980 800 - 20
7499 ALLEN SALESMAN 7698 02/20/1981 1600 300 30
7521 WARD SALESMAN 7698 02/22/1981 1250 500 30
In the default table emp, in oracle 11g, i need to display the names of those employees with their emp no , where the job is greater than 1.
Like in the table there are 3 managers and 2 salesman which should be printed with ename,emp. Job like president which is just 1 should be omitted.
Like below, you will need a subquery to select job from emp where you can calculate the count and check if its greater than 1. Before the subquery you will select EMPNO and ENAME to display from emp.
SELECT EMPNO, ENAME
FROM emp
WHERE JOB IN (SELECT JOB
FROM emp
GROUP BY JOB
HAVING COUNT(*) > 1)
You can try GROUP BY :
SELECT empno,ename, count(job)
FROM emp
GROUP BY job
HAVING count(job)>1
COUNT(JOB) : the number of employees with the same job
GROUP BY Job : group the results by job eg. 3 managers and 2 salesman....
The subselect will just return JOBs which occur more than 1 time.
The outer select will now just return EMPNOs which are in this subset of JOBs
SELECT EMPNO,ENAME
FROM emp
WHERE JOB IN (SELECT JOB
FROM emp
GROUP BY JOB
HAVING COUNT(*) > 1
)

how to make ranking of value in oracle

I have table : tb_user.
id|name|value
10|boy|500
20|Ony|200
10|boy|500
When I execute the following query:
Select id,name,sum(value) as grant_total from tb_user group by id,name
the result is:
id|name|grant_total
10|boy |1000
20|Ony |200
I want to add 1 column --> ranking
id|name|grant_total|ranking
10|boy |1000 |1
20|Ony |200 |2
how to make ranking?
Try:
Select id,name,sum(value) as grant_total, rownum as ranking from tb_user group by id,name
You could use ROWNUM and a subquery to first order the rows:
SQL> WITH DATA AS(
2 SELECT 10 ID, 'boy' NAME, 1000 grand_total FROM dual UNION ALL
3 SELECT 20, 'Ony', 200 grand_total from dual
4 )
5 SELECT t.*,
6 rownum ranking
7 FROM
8 ( SELECT * FROM DATA ORDER BY grand_total DESC
9 ) t
10 /
ID NAM GRAND_TOTAL RANKING
---------- --- ----------- ----------
10 boy 1000 1
20 Ony 200 2
SQL>
Or,
You could use the Analytic functions:
For example,
SQL> WITH DATA AS(
2 SELECT 10 ID, 'boy' NAME, 1000 grand_total FROM dual UNION ALL
3 SELECT 20, 'Ony', 200 grand_total from dual
4 )
5 SELECT t.*, row_number() OVER(ORDER BY grand_total DESC) ranking FROM DATA t;
ID NAM GRAND_TOTAL RANKING
---------- --- ----------- ----------
10 boy 1000 1
20 Ony 200 2
SQL>
Depending on the requirement, you need to use:
ROW_NUMBER
RANK
DENSE_RANK
Let's see an example,
SQL> SELECT empno, ename, sal FROM emp;
EMPNO ENAME SAL
---------- ---------- ----------
7369 SMITH 800
7499 ALLEN 1600
7521 WARD 1250
7566 JONES 2975
7654 MARTIN 1250
7698 BLAKE 2850
7782 CLARK 2450
7788 SCOTT 3000
7839 KING 5000
7844 TURNER 1500
7876 ADAMS 1100
7900 JAMES 950
7902 FORD 3000
7934 MILLER 1300
14 rows selected.
SQL> SELECT empno,
2 ename,
3 sal,
4 ROW_NUMBER() OVER(ORDER BY sal) rnum,
5 RANK() OVER(ORDER BY sal DESC) rank,
6 DENSE_RANK() OVER(ORDER BY sal) drank
7 FROM emp
8 ORDER BY empno
9 /
EMPNO ENAME SAL RNUM RANK DRANK
---------- ---------- ---------- ---------- ---------- ----------
7369 SMITH 800 1 14 1
7499 ALLEN 1600 8 7 7
7521 WARD 1250 4 10 4
7566 JONES 2975 11 4 10
7654 MARTIN 1250 5 10 4
7698 BLAKE 2850 10 5 9
7782 CLARK 2450 9 6 8
7788 SCOTT 3000 12 2 11
7839 KING 5000 14 1 12
7844 TURNER 1500 7 8 6
7876 ADAMS 1100 3 12 3
7900 JAMES 950 2 13 2
7902 FORD 3000 13 2 11
7934 MILLER 1300 6 9 5
14 rows selected.
SQL>
The easiest way is to use the ROW_NUMBER() analytic function (or RANK() if you want to rank ties equally):
SELECT id, name, SUM(value) AS grand_total
, ROW_NUMBER() OVER ( ORDER BY SUM(value) DESC ) AS ranking
FROM tb_user
GROUP BY id, name
ORDER BY ranking;

Show the column required of a table using group by function in oracle

I want to display the column c.officeID along with the column "Amount". This is my query:
select c.officeID,max(Sum(p.amount)) as “Amount” from payment p, client c where c.clientid in (select clientid from client) and p.clientID=c.clientID group by c.officeID;
I tried using:
select c.officeID,max(Sum(p.amount)) as “Amount” from payment p, client c where c.clientid in (select clientid from client) and p.clientID=c.clientID group by c.officeID,p.amount;
But I am getting a error saying 'ORA00937-Not a single group-group funtion'. Could anyone please tell me where I am going wrong?
You can't add a max and group by, use this following query to get the result, if you want max then use the second query,
SCOTT#research 17-APR-15> select c.empno,Sum(p.sal) as "Amount"
2 from empp p, emp c
3 where c.empno in (select empno from emp)
4 and p.empno = c.empno
5 group by c.empno
6 ;
EMPNO Amount
---------- ----------
7782 2450
7839 5000
7844 1500
7698 2850
7521 1250
7902 3000
7566 2975
7654 1250
7788 3000
7934 1300
7499 1600
7876 1100
234 800
7900 950
14 rows selected.
select max("Amount") from (
select c.empno,Sum(p.sal) as "Amount"
from empp p, emp c
where c.empno in (select empno from emp)
and p.empno = c.empno
group by c.empno)
MAX("AMOUNT")
-------------
5000

Difference b/w 'HAVING NOT EXISTS' and 'MINUS'

HAVING acts like a where clause and EXISTS checks for the rows that exist for the given row or not. So, when we use HAVING NOT EXISTS it should have the same functionality as MINUS which eliminates the common rows from first table.
But in an example when I substituted HAVING NOT EXISTS for MINUS the result sets are not same. the query is
(
select empno,ename,job,mgr,hiredate,sal,comm,deptno
,
count(*) as cnt
from v
group by empno,ename,job,mgr,hiredate,sal,comm,deptno
minus
( select empno,ename,job,mgr,hiredate,sal,comm,deptno
,
count(*) as cnt
from emp
group by empno,ename,job,mgr,hiredate,sal,comm,deptno)
)
union all
(
select empno,ename,job,mgr,hiredate,sal,comm,deptno
,
count(*) as cnt
from emp
group by empno,ename,job,mgr,hiredate,sal,comm,deptno
minus
( select empno,ename,job,mgr,hiredate,sal,comm,deptno
,
count(*) as cnt
from v
group by empno,ename,job,mgr,hiredate,sal,comm,deptno)
)
V is a view, which is formed like
create or replace view v
as
select * from emp where deptno != 10
union all
select * from emp where ename = 'WARD';
The query is an example to determine whether the tables have same data or not
this is my emp table:
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
7369 SMITH CLERK 7902 17-DEC-80 800 20
7499 ALLEN SALESMAN 7698 20-FEB-81 1600 300 30
7521 WARD SALESMAN 7698 22-FEB-81 1250 500 30
7566 JONES MANAGER 7839 02-APR-81 2975 20
7654 MARTIN SALESMAN 7698 28-SEP-81 1250 1400 30
7698 BLAKE MANAGER 7839 01-MAY-81 2850 30
7782 CLARK MANAGER 7839 09-JUN-81 2450 10
7788 SCOTT ANALYST 7566 09-DEC-82 3000 20
7839 KING PRESIDENT 17-NOV-81 5000 10
7844 TURNER SALESMAN 7698 08-SEP-81 1500 0 30
7876 ADAMS CLERK 7788 12-JAN-83 1100 20
7900 JAMES CLERK 7698 03-DEC-81 950 30
7902 FORD ANALYST 7566 03-DEC-81 3000 20
7934 MILLER CLERK 7782 23-JAN-82 1300 10
This is the view V
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
------- ---------- --------- ---------- --------- ---------- ---------- ----------
7369 SMITH CLERK 7902 17-DEC-80 800 20
7499 ALLEN SALESMAN 7698 20-FEB-81 1600 300 30
7521 WARD SALESMAN 7698 22-FEB-81 1250 500 30
7566 JONES MANAGER 7839 02-APR-81 2975 20
7654 MARTIN SALESMAN 7698 28-SEP-81 1250 1400 30
7698 BLAKE MANAGER 7839 01-MAY-81 2850 30
7788 SCOTT ANALYST 7566 09-DEC-82 3000 20
7844 TURNER SALESMAN 7698 08-SEP-81 1500 0 30
7876 ADAMS CLERK 7788 12-JAN-83 1100 20
7900 JAMES CLERK 7698 03-DEC-81 950 30
7902 FORD ANALYST 7566 03-DEC-81 3000 20
7521 WARD SALESMAN 7698 22-FEB-81 1250 500 30
When minus is used the query results in
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO CNT
------ ---------- --------- ---------- --------- ---------- ---------- ---------- ----------
7521 WARD SALESMAN 7698 22-FEB-81 1250 500 30 2
7521 WARD SALESMAN 7698 22-FEB-81 1250 500 30 1
7782 CLARK MANAGER 7839 09-JUN-81 2450 10 1
7839 KING PRESIDENT 17-NOV-81 5000 10 1
7934 MILLER CLERK 7782 23-JAN-82 1300 10 1
When HAVING NOT EXISTS is used no rows are resulted from the query
Is there any wrong in my understanding of the terms. Please explain why the result sets are different with a short example.Thanks
This is always going to produce no rows:
select empno,ename,job,mgr,hiredate,sal,comm,deptno,count(*) as cnt
from v
group by empno,ename,job,mgr,hiredate,sal,comm,deptno
having not exists (
select empno,ename,job,mgr,hiredate,sal,comm,deptno, count(*) as cnt
from emp
group by empno,ename,job,mgr,hiredate,sal,comm,deptno
);
There is no correlation between the main select and the data returned by the query in the having clause. Unless emp is empty there will always be something in that clause, so the having not exists has to be false, so all rows in the main query are discarded.
It's nothing to do with your data really; a simplified version does the same:
select * from dual
where not exists (select * from dual);
It's logically impossible for there to be rows in dual, and no rows in dual, at the same time. (OK, if there are no rows in dual you're in big trouble, so maybe this wasn't a great example).
In your case v and emp are related; if emp is empty then v must be too.
The main select in this part finds 11 rows from v. The select inside the having clause finds 14 rows from emp, but it wouldn't matter if it found 1 or 100 rows, as there is no relation between the two sets of rows. The having clause just acts as a filter; having not exists (<query that finds anything>) filters out everything. If you break it down, exists (select ...) is true because there are rows in emp; not exists (select ...) is just the negation of that, so it's false; so you're effectively saying 'give me the rows where true = false'.
The second part of the union finds 14 rows from emp, but the same 'true = false' filter applies, so all rows are still discarded, even though the inner select only finds 11 rows from v. You are not making any connection between the data in v and emp.
But any subquery that always produces rows would have the same effect; because there is no correlation you could be querying a different table which has nothing to do with emp at all, and as long is that subquery returned one or more rows your having clause would filter out everything you expect to see. This behaves the same, for example:
select empno,ename,job,mgr,hiredate,sal,comm,deptno,count(*) as cnt
from v
group by empno,ename,job,mgr,hiredate,sal,comm,deptno
having not exists (
select * from dual
);
For the second part of the union you can get some of the data back by adding the correlation:
select empno,ename,job,mgr,hiredate,sal,comm,deptno,count(*) as cnt
from v
group by empno,ename,job,mgr,hiredate,sal,comm,deptno
having not exists (
select empno,ename,job,mgr,hiredate,sal,comm,deptno, count(*) as cnt
from emp
where emp.empno = v.empno
and emp.ename = v.ename
and emp.job = v.job
and emp.mgr = v.mgr
and emp.hiredate = v.hiredate
and emp.sal = v.sal
and emp.deptno = v.deptno
group by empno,ename,job,mgr,hiredate,sal,comm,deptno
);
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO CNT
---------- ---------- --------- ---------- --------- ---------- ---------- ---------- ----------
7782 CLARK MANAGER 7839 09-JUN-81 2450 10 1
7934 MILLER CLERK 7782 23-JAN-82 1300 10 1
7839 KING PRESIDENT 17-NOV-81 5000 10 1
Just using empno might work if it's a primary key or unique, unless you'd really be looking for discrepancies between other fields. But correlating the counts between v and emp is tricky, and possibly not worth the effort. Looking at the count at all seems a bit odd; they only differ for WARD because of the contrived way you've built the view. Using minus here is simpler and possibly quicker.

Resources