Window functions: How to partition over nothing? - oracle

I am extracting a table, but I would also like the sum of a column.
I can say SUM(column) over (partition by other_column)
to get a new column with a sum over the column for every grouping given by other_column.
But I don't want a grouping! Basically sum(column) is meant to give me a column with a constant row equal to the sum of the entire column with no partitioning.
So how do I partition over nothing?

Exactly like you said; over "nothing". For example:
SQL> select deptno, ename, sal, sum(sal) over () sumsal
2 from emp;
DEPTNO ENAME SAL SUMSAL
---------- ---------- ---------- ----------
20 SMITH 920 34145
30 ALLEN 1600 34145
30 WARD 1250 34145
20 JONES 2975 34145
30 MARTIN 1250 34145
30 BLAKE 2850 34145
10 CLARK 2450 34145
20 SCOTT 3000 34145
10 KING 10000 34145
30 TURNER 1500 34145
20 ADAMS 1100 34145
30 JAMES 950 34145
20 FORD 3000 34145
10 MILLER 1300 34145
14 rows selected.
SQL>

Let's see the table orders created as follows:
Schema (MySQL v8.0)
CREATE TABLE orders (
`trade_date` DATETIME,
`ticker` VARCHAR(4),
`trans_type` VARCHAR(4),
`quantity` INTEGER
);
INSERT INTO orders
(`trade_date`, `ticker`, `trans_type`, `quantity`)
VALUES
('2020-12-10', 'FB', 'BUY', '100'),
('2020-12-28', 'FB', 'BUY', '50'),
('2020-12-29', 'FB', 'SELL', '80'),
('2020-12-30', 'FB', 'SELL', '30'),
('2020-12-31', 'FB', 'BUY', '40'),
('2020-11-16', 'AAPL', 'BUY', '30'),
('2020-11-17', 'AAPL', 'SELL', '70'),
('2020-11-20', 'AAPL', 'BUY', '50'),
('2020-11-24', 'AAPL', 'BUY', '40');
And we want to sum over the quantity by the trans_type:
Query #1
SELECT
trade_date,
ticker,
trans_type,
quantity,
SUM(CASE WHEN trans_type='SELL' THEN -quantity ELSE quantity END) OVER () AS net_quantity
FROM
orders;
We will get this table:
trade_date
ticker
trans_type
quantity
net_quantity
2020-12-10 00:00:00
FB
BUY
100
130
2020-12-28 00:00:00
FB
BUY
50
130
2020-12-29 00:00:00
FB
SELL
80
130
2020-12-30 00:00:00
FB
SELL
30
130
2020-12-31 00:00:00
FB
BUY
40
130
2020-11-16 00:00:00
AAPL
BUY
30
130
2020-11-17 00:00:00
AAPL
SELL
70
130
2020-11-20 00:00:00
AAPL
BUY
50
130
2020-11-24 00:00:00
AAPL
BUY
40
130
View on DB Fiddle
This article would be helpful for you to learn window functions: An Intro to SQL Window Functions.
Reference:
mysql window function with case

Related

Row Number In 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>

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

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

Oracle Cursor Issue

Anyone have any idea how to do the following?
declare cursor
open cursor
fetch cursor
<< Start reading the cursor in a LOOP >>
Lets say the cursor have 10 records.
Read until 5th record then go to the 6th record and do some checking.
Now, is it possible to go back to 5th record from 6th record ?
Depends on the requirements.
You can use the LAG() and LEAD() analytic functions to get information for the next and prior rows, i.e.
SQL> ed
Wrote file afiedt.buf
1 select ename,
2 sal,
3 lead(sal) over (order by ename) next_sal,
4 lag(sal) over (order by ename) prior_sal
5 from emp
6* order by ename
SQL> /
ENAME SAL NEXT_SAL PRIOR_SAL
---------- ---------- ---------- ----------
ADAMS 1100 1600
ALLEN 1600 2850 1100
BLAKE 2850 2450 1600
CLARK 2450 3000 2850
FORD 3000 950 2450
JAMES 950 2975 3000
JONES 2975 5000 950
KING 5000 1250 2975
MARTIN 1250 1300 5000
MILLER 1300 3000 1250
SCOTT 3000 800 1300
ENAME SAL NEXT_SAL PRIOR_SAL
---------- ---------- ---------- ----------
SMITH 800 1500 3000
TURNER 1500 1250 800
WARD 1250 1500
14 rows selected.
If you don't want to use analytic functions, you can use PL/SQL collections, BULK COLLECT the data into those collections (using the LIMIT clause if you have more data than you want to store in your PGA) and then move forward and backward through your collections.
How far do you need to go back? If you only need a look-ahead of one row, you could buffer just the previous row in your loop (application-side).

Resources