SQL Top 5 and the rest as others - oracle

I want to select the top 5 rows and additional a 6th row named Others with rest aggregated.
with
Anzahl as
(SELECT
NVL (parse_listener_log_line (connect_string, 'HOST'), 'n/a') host, COUNT(*) cnt
FROM listener_log
WHERE ID_MANDANT = :P100_MANDANT
AND ID_SERVER = :P100_SERVER
GROUP BY parse_listener_log_line (connect_string, 'HOST')
ORDER BY cnt DESC),
client as
(select
case
when rownum > 4 then 'Others'
else host
end as client, cnt
FROM Anzahl)
SELECT client, cnt
FROM client;
CLIENT CNT
jdbc 118553
server2 106170
server1 101710
server4 13370
Others 8734
Others 1760
Others 1365
Others 1058

A little bit of analytic functions (row_number) along with set operations (union) might do what you're looking for.
Scott's EMP table contains these data:
SQL> select ename, sal from emp order by sal desc;
ENAME SAL
---------- ----------
KING 5000
FORD 3000
SCOTT 3000
JONES 2975
BLAKE 2850
CLARK 2450
ALLEN 1600
TURNER 1500
MILLER 1300
WARD 1250
MARTIN 1250
ADAMS 1100
JAMES 950
SMITH 800
14 rows selected.
Now: find each row's ordinal number (using row_number) and union the first 5 rows (take each of them as is) with the sixth one that contains aggregated salaries:
SQL> with temp as
2 (select ename,
3 sal,
4 row_number() over (order by sal desc) rn
5 from emp
6 )
7 select rn,
8 ename,
9 sal
10 from temp
11 where rn <= 5
12 union all
13 select 6,
14 'Other',
15 sum(sal)
16 from temp
17 where rn > 5
18 order by rn;
RN ENAME SAL
---------- ---------- ----------
1 KING 5000
2 SCOTT 3000
3 FORD 3000
4 JONES 2975
5 BLAKE 2850
6 Other 12200
6 rows selected.
SQL>

Related

HOW can I add a sort row column?

Im using view on oracle plsql. In the table showing my sales,
I want to have a column showing the sequence number next to the sales of top 50 products.
The best selling products should be listed and followed by the sequence number in the row.
how can i do that?
Thanks.
This is my related query
NVL (
(SELECT ROUND (
SUM (
CASE DOCUMENT_TYPE
WHEN 2
THEN
(CASE TRANSACTION_TYPE
WHEN 0 THEN 0 - AMOUNT
ELSE AMOUNT
END)
ELSE
(CASE TRANSACTION_TYPE
WHEN 1 THEN 0 - AMOUNT
ELSE AMOUNT
END)
END),
8)
FROM TBL_TRANSACTION_LINES
WHERE STORE_NO = tbl_location.locationno
AND (TRANSACTION_TYPE NOT IN (10, 30))
AND TRANSACTION_DATE >
TO_DATE ('2020-09-27 0:0:0',
'yyyy-mm-dd HH24:MI:SS')
AND TRANSACTION_DATE <=
TO_DATE ('2020-10-04 0:0:0',
'yyyy-mm-dd HH24:MI:SS')
AND (URUNID = TBL_URUNLER.URUNID)),
0)
AS RESULTS
I don't have your tables nor data, so - here's an example based on Scott's sample schema; ranking employees by salary within their departments. Apply that to your case.
SQL> with temp as
2 (select deptno, ename, sal,
3 rank() over (partition by deptno order by sal desc) rnk
4 from emp
5 )
6 select deptno, ename, sal, rnk
7 from temp
8 order by deptno, rnk;
DEPTNO ENAME SAL RNK
---------- ---------- ---------- ----------
10 KING 10000 1
10 CLARK 2450 2
10 MILLER 1300 3
20 SCOTT 3000 1
20 FORD 3000 1
20 JONES 2975 3
20 ADAMS 1100 4
20 SMITH 920 5
30 BLAKE 2850 1
30 ALLEN 1600 2
30 TURNER 1500 3
30 MARTIN 1250 4
30 WARD 1250 4
30 JAMES 950 6
14 rows selected.
SQL>

how to do cumulative sums in oracle

I am new to SQL and I wanted to do a report which shows the daily number of tickets per shift and also the to-date total.
Here's the query I have which shows the first 5 columns below:
SELECT
TO_CHAR(DTTM,'YYYY-MM-DD') as "DATE"
,COUNT(CASE WHEN TO_CHAR(DTTM, 'HH24:MI') BETWEEN '14:00' AND '22:00' THEN TKTNUM ELSE NULL END) AS "DAYS"
,COUNT(CASE WHEN TO_CHAR(DTTM, 'HH24:MI') BETWEEN '06:00' AND '14:00' THEN TKTNUM ELSE NULL END) AS "MIDS"
,COUNT(CASE WHEN TO_CHAR(DTTM, 'HH24:MI') NOT BETWEEN '06:00' AND '22:00' THEN TKTNUM ELSE NULL END) AS "SWINGS"
,COUNT(TKTNUM) AS "TOTAL"
FROM TKTHISTORY
GROUP BY TO_CHAR(DTTM,'YYYY-MM-DD')
ORDER BY TO_CHAR(DTTM,'YYYY-MM-DD')
DATE DAYS MIDS SWINGS TOTAL
2019-08-01 8 13 1 22 22
2019-08-02 19 5 3 27 49
2019-08-03 23 6 6 35 84
2019-08-04 7 9 13 29 113
2019-08-05 4 17 2 23 136
2019-08-06 10 5 16 31 167
2019-08-07 3 12 11 26 193
The 6th column should be the cumulative sum for the dates. I tried browsing the internet and read about "over" and "partition by" but I still can't figure out how to use it :(
Here's an example based on Scott's EMP table, which counts jobs per department. The last column is the "running total" value.
Sample data shows that there are 3 employees in DEPTNO = 10, 5 of them in dept. 20 and 6 in dept. 30:
SQL> select deptno, empno, ename from emp order by deptno;
DEPTNO EMPNO ENAME
---------- ---------- ----------
10 7782 CLARK
10 7839 KING
10 7934 MILLER
20 7566 JONES
20 7902 FORD
20 7876 ADAMS
20 7369 SMITH
20 7788 SCOTT
30 7521 WARD
30 7844 TURNER
30 7499 ALLEN
30 7900 JAMES
30 7698 BLAKE
30 7654 MARTIN
14 rows selected.
Query then looks like this:
SQL> select
2 deptno,
3 count(empno) emps_per_dept,
4 sum(count(*)) over (order by deptno) total
5 from emp
6 group by deptno;
DEPTNO EMPS_PER_DEPT TOTAL
---------- ------------- ----------
10 3 3
20 5 8
30 6 14
SQL>
Which, in your case, might be like this:
SELECT
...
,sum(COUNT(TKTNUM)) over (order by TO_CHAR(DTTM,'YYYY-MM-DD')) AS "TOTAL"
FROM TKTHISTORY
...
SELECT t.user_id,
t.transactions_,
SUM(t.transactions_) over(ORDER BY t.user_id) cum_sum
FROM FEBRUARY_2023_USER_ACTIVITIES t

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
;

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

Resources