Make cumulative sum in view oracle - oracle

I want to make a view in oracle, how can I get value of 'saldo' that I supposed like in this screenshoot with oracle? can someone give me the script?
The result that I want:
dateacct period_id startdate enddate debit credit saldo
7/10/2015 1 1/10/2015 30/10/2015 25 0 25
8/10/2015 1 1/10/2015 30/10/2015 0 5 20
9/10/2015 1 1/10/2015 30/10/2015 0 3 17
10/10/2015 1 1/10/2015 30/10/2015 4 0 21
Notes: dateacct,period_id,startdate,enddate,credit,debit are the column that I take from 'akun' table.

Try this:
SELECT DATEACCT, PERIOD_ID, STARTDATE, ENDDATE, CREDIT, DEBIT,
SUM (DEBIT-CREDIT) OVER (ORDER BY DATEACCT,PERIOD_ID,STARTDATE,ENDDATE) saldo
from AKUN
ORDER BY DATEACCT, PERIOD_ID, STARTDATE, ENDDATE;

There's a bunch of things you can do with Oracle Analytics functions, especially made for that purpose.
Oracle Analytic functions
You can also use the PARTITION BY to have cumul for the day or the period:
SELECT DATEACCT, PERIOD_ID, STARTDATE, ENDDATE, CREDIT, DEBIT,
SUM (DEBIT-CREDIT) OVER (PARTITION BY DATEACCT ORDER BY DATEACCT,PERIOD_ID,STARTDATE,ENDDATE) CUMULDAY,
SUM (DEBIT-CREDIT) OVER (PARTITION BY PERIOD_ID ORDER BY DATEACCT,PERIOD_ID,STARTDATE,ENDDATE) CUMULPERIOD
from AKUN
ORDER BY DATEACCT, PERIOD_ID, STARTDATE, ENDDATE;
DATEACCT PERIOD_ID STARTDATE ENDDATE CREDIT DEBIT CUMULDAY CUMULPERIOD
-------- ---------- --------- -------- ---------- ---------- ---------- -----------
07/10/15 1 01/10/15 30/10/15 0 25 25 25
08/10/15 1 01/10/15 30/10/15 5 0 -5 20
09/10/15 1 01/10/15 30/10/15 3 0 -3 17
10/10/15 1 01/10/15 30/10/15 0 4 4 21

Related

PL SQL function that includes multiple tables

I'm new to PL SQL and have to write a function, which has customer_id as an input and has to output a product_name of the best selling product for that customer_id.
The schema looks like this:
I found a lot of simple examples where it includes two tables, but I can't seem to find one where you have to do multiple joins and use a function, while selecting only the best selling product.
I could paste a lot of very bad code here and how I tried to approach this, but this seems to be a bit over my head for current knowledge, since I've been learning PL SQL for less than 3 days now and got this task.
With some sample data (minimal column set):
SQL> select * from products order by product_id;
PRODUCT_ID PRODUCT_NAME
---------- ----------------
1 BMW
2 Audi
SQL> select * From order_items;
PRODUCT_ID CUSTOM QUANTITY UNIT_PRICE
---------- ------ ---------- ----------
1 Little 100 1
1 Little 200 2
2 Foot 300 3
If we check some totals:
SQL> select o.product_id,
2 o.customer_id,
3 sum(o.quantity * o.unit_price) total
4 from order_items o
5 group by o.product_id, o.customer_id;
PRODUCT_ID CUSTOM TOTAL
---------- ------ ----------
2 Little 400
1 Little 100
2 Foot 900
SQL>
It says that
for customer Little, product 2 was sold with total = 400 - that's our choice for Little
for customer Little, product 1 was sold with total = 100
for customer Foot, product 2 was sold with total = 900 - that's our choice for Foot
Query might then look like this:
temp CTE calculates totals per each customer
rank_them CTE ranks them in descending order per each customer; row_number so that you get only one product, even if there are ties
finally, select the one that ranks as the highest
SQL> with
2 temp as
3 (select o.product_id,
4 o.customer_id,
5 sum(o.quantity * o.unit_price) total
6 from order_items o
7 group by o.product_id, o.customer_id
8 ),
9 rank_them as
10 (select t.customer_id,
11 t.product_id,
12 row_number() over (partition by t.customer_id order by t.total desc) rn
13 from temp t
14 )
15 select * From rank_them;
CUSTOM PRODUCT_ID RN
------ ---------- ----------
Foot 2 1 --> for Foot, product 2 ranks as the highest
Little 2 1 --> for Little, product 1 ranks as the highest
Little 1 2
SQL>
Moved to a function:
SQL> create or replace function f_product (par_customer_id in order_items.customer_id%type)
2 return products.product_name%type
3 is
4 retval products.product_name%type;
5 begin
6 with
7 temp as
8 (select o.product_id,
9 o.customer_id,
10 sum(o.quantity * o.unit_price) total
11 from order_items o
12 group by o.product_id, o.customer_id
13 ),
14 rank_them as
15 (select t.customer_id,
16 t.product_id,
17 row_number() over (partition by t.customer_id order by t.total desc) rn
18 from temp t
19 )
20 select p.product_name
21 into retval
22 from rank_them r join products p on p.product_id = r.product_id
23 where r.customer_id = par_customer_id
24 and r.rn = 1;
25
26 return retval;
27 end;
28 /
Function created.
SQL>
Testing:
SQL> select f_product ('Little') result from dual;
RESULT
--------------------------------------------------------------------------------
Audi
SQL> select f_product ('Foot') result from dual;
RESULT
--------------------------------------------------------------------------------
Audi
SQL>
Now, you can improve it so that you'd care about no data found issue (when customer didn't buy anything), ties (but you'd then return a collection or a refcursor instead of a scalar value) etc.
[EDIT] I'm sorry, ORDERS table has to be included into the temp CTE; your data model is correct, you don't have to do anything about it - my query was wrong (small screen + late hours issue; not a real excuse, just saying).
So:
with
temp as
(select i.product_id,
o.customer_id,
sum(i.quantity * i.unit_price) total
from order_items i join orders o on o.order_id = i.order_id
group by i.product_id, o.customer_id
),
The rest of my code is - otherwise - unmodified.

rownum in Oracle sql with group by

I need to build a query to retrieve information group by Members and an expiration Date but I need to have a sequence number for every Member..
So for example:
If Member "A" has 3 records to expire, "B" has only 1 and "C" has 2, I need a result like this:
Number Member ExpDate
1 A 01/01/2020
2 A 02/01/2020
3 A 03/01/2020
1 B 01/01/2020
1 C 01/01/2020
2 C 02/01/2020
My query now is:
SELECT ROW_NUMBER() OVER(ORDER BY TRUNC(EXPIRATION_DT) ASC) AS SEQUENCE, MEMBER_ID AS MEMBER, SUM(ACCRUALED_VALUE) - SUM(USED_VALUE) AS POINTS, trunc(EXPIRATION_DT) AS EXPDATE
FROM TABLE1
WHERE EXPIRATION_DT > SYSDATE AND EXPIRATION_DT < SYSDATE + 90
GROUP BY MEMBER_ID, TRUNC(EXPIRATION_DT)
HAVING SUM(ACCRUALED_VALUE) - SUM(USED_VALUE) > 0
ORDER BY 4 ASC;
But I cant' "group" the sequence number.... The result now is:
Seq Mem Points Date
1 1-O 188 2018-03-01 00:00:00
2 1-C 472 2018-03-01 00:00:00
3 1-A 485 2018-03-01 00:00:00
4 1-1 267 2018-03-01 00:00:00
5 1-E 500 2018-03-01 00:00:00
6 1-P 55 2018-03-01 00:00:00
7 1-E 14 2018-03-01 00:00:00
I think you need a DENSE_RANK window function. try this -
SELECT DENSE_RANK() OVER (PARTITION BY MEMBER ORDER BY TRUNC(EXPIRATION_DT) ASC) AS SEQUENCE
,MEMBER_ID AS MEMBER
,SUM(ACCRUALED_VALUE) - SUM(USED_VALUE) AS POINTS
,trunc(EXPIRATION_DT) AS EXPDATE
FROM TABLE1
WHERE EXPIRATION_DT > SYSDATE AND EXPIRATION_DT < SYSDATE + 90
GROUP BY MEMBER_ID
,TRUNC(EXPIRATION_DT)
HAVING SUM(ACCRUALED_VALUE) - SUM(USED_VALUE) > 0
ORDER BY 4 ASC;
with g as (
select *
From TABLE1 g
group by MEMBER_ID
,TRUNC(EXPIRATION_DT)
HAVING SUM(ACCRUALED_VALUE) - SUM(USED_VALUE) > 0 ---- etc
)
select rownum, g.* From g
this select return first column with sequence number

get particular day between a data range from giving table

I read many articles which introduce the using of connect by, but all of them get the particular days from a giving parameter(almost the begin date and end date)
What I want to know is how could I get split the rows from a certain table?
Example
Table T1
StartDate EndDate T_ID
2017-06-01 2017-06-15 01
2017-06-05 2017-06-06 02
The result I want is
TargetDate T_ID
2017-06-01 01
2017-06-02 01
2017-06-03 01
2017-06-04 01
2017-06-05 01
.
.
.
.
2017-06-15 01
2017-06-06 01
2017-06-06 02
I tried
SELECT T_ID, T1.StartDate+ LEVEL - 1 DD, LEVEL
FROM T1
WHERE T1.T_ID in = '01'
CONNECT BY LEVEL <= (TO_DATE(TRUNC(T1.EndDate)) - T1.StartDate + 1 ) ;
Waiting for your solution. Thanks.
Test Data:
CREATE TABLE t1 ( t_id, startdate, enddate ) AS
SELECT 1, DATE '2017-06-01', DATE '2017-06-15' FROM DUAL UNION ALL
SELECT 2, DATE '2017-06-05', DATE '2017-06-06' FROM DUAL;
Query:
SELECT T_ID,
COLUMN_VALUE AS dt,
ROW_NUMBER() OVER ( PARTITION BY t1.ROWID
ORDER BY Column_value ) AS lvl
FROM T1
CROSS JOIN
TABLE(
CAST(
MUTLTSET(
SELECT t1.Startdate + LEVEL - 1
FROM DUAL
CONNECT BY t1.Startdate + LEVEL - 1 <= t1.EndDate
) AS SYS.ODCIDATELIST
)
);
Output:
T_ID DT LVL
---- ---------- ---
1 2017-06-01 1
1 2017-06-02 2
1 2017-06-03 3
1 2017-06-04 4
1 2017-06-05 5
1 2017-06-06 6
1 2017-06-07 7
1 2017-06-08 8
1 2017-06-09 9
1 2017-06-10 10
1 2017-06-11 11
1 2017-06-12 12
1 2017-06-13 13
1 2017-06-14 14
1 2017-06-15 15
2 2017-06-05 1
2 2017-06-06 2
Here is the query in standard SQL (with a recursive cte) which also works in Oracle:
with all_dates(targetdate, t_id, enddate) as
(
select startdate as targetdate, t_id, enddate from t1
union all
select targetdate + 1, t_id, enddate from all_dates where targetdate < enddate
)
select targetdate, t_id
from all_dates
order by t_id, targetdate;
SELECT DISTINCT T_ID
, T1.StartDate+ LEVEL - 1 DD
, LEVEL
FROM T1
WHERE T1.T_ID IN( 1,2)
CONNECT BY LEVEL <= T1.EndDate - T1.StartDate + 1
But I'm not sure about performances (At moment I didn't find a way to limit without DISTINCT but using CONNECT BY clauses).
As an alternative you can use a CTE like this (you can remove RN column, I left it as a check):
with all_dates(targetdate, t_id, enddate, RN) as
(
select startdate as targetdate, t_id, enddate, 1 AS RN from t1
union all
select T1.startdate + all_dates.RN, T1.t_id, T1.enddate, all_dates.RN+1 AS RN
from t1
inner JOIN all_dates ON T1.startdate+all_dates.RN<=all_dates.enddate
AND T1.T_ID = all_dates.T_ID
)
select targetdate, t_id , RN
from all_dates
order by t_id, targetdate;
Sample data:
CREATE TABLE T1 (StartDate DATE, EndDate DATE, T_ID NUMBER(10,0));
INSERT INTO T1 VALUES ('20170601','20170615', 1);
INSERT INTO T1 VALUES ('20170605','20170606', 2);
INSERT INTO T1 VALUES ('20170701','20170703', 3);
Output:
20170601 1 1
20170602 1 2
20170603 1 3
20170604 1 4
20170605 1 5
20170606 1 6
20170607 1 7
20170608 1 8
20170609 1 9
20170610 1 10
20170611 1 11
20170612 1 12
20170613 1 13
20170614 1 14
20170615 1 15
20170605 2 1
20170606 2 2
20170701 3 1
20170702 3 2
20170703 3 3
If you're wanting to use connect by to achieve this, you will need to add a couple of additional clauses in order to get it to work with multiple rows:
WITH t1 AS (SELECT to_date('01/06/2017', 'dd/mm/yyyy') startdate, to_date('15/06/2017', 'dd/mm/yyyy') enddate, 1 t_id FROM dual UNION ALL
SELECT to_date('05/06/2017', 'dd/mm/yyyy') startdate, to_date('06/06/2017', 'dd/mm/yyyy') enddate, 2 t_id FROM dual)
SELECT t_id,
startdate + LEVEL -1 dd
FROM t1
CONNECT BY LEVEL <= enddate - startdate + 1
AND PRIOR t_id = t_id
AND PRIOR sys_guid() IS NOT NULL
ORDER BY t_id, dd;
T_ID DD
---------- -----------
1 01/06/2017
1 02/06/2017
1 03/06/2017
1 04/06/2017
1 05/06/2017
1 06/06/2017
1 07/06/2017
1 08/06/2017
1 09/06/2017
1 10/06/2017
1 11/06/2017
1 12/06/2017
1 13/06/2017
1 14/06/2017
1 15/06/2017
2 05/06/2017
2 06/06/2017

oracle procedure that update values based on amount

I have the following table (SEMINARS):
ID AMOUNT
6936120606 320.51
6978332460 261.05
6940456367 0
6973328053 438.68
6951690568 533.56
sum(1690.47)
I have the value 980 that I need to credit one by one the seminars amount
start from biggest amount to lowest.
for example the 980 value must will credit as following:
6951690568 533,56 (-533.56)=0 446.44 (980.00-533.56=446.44)
6973328053 438,68 (-438.68)=0 7.76 (446.44-438.68= 7.76)
6936120606 320,51 (- 7.76)=312.75 0.00 (0)
any idea?
In Oracle 11g you can use recursive query, this gave me desired output:
with
s as (select row_number() over (order by amount desc) rn, id, amount from seminars),
t(rn, id, amount, left1, left2) as (
select rn, id, amount, greatest(amount-980,0), 980-amount from s where rn = 1
union all
select s.rn, s.id, s.amount,
s.amount-least(left2, s.amount), greatest(left2-s.amount, 0)
from s join t on s.rn = t.rn+1 and left2>0)
select * from t
Output and SQLFiddle:
RN ID AMOUNT LEFT1 LEFT2
---------- --------------- ---------- ---------- ----------
1 6951690568 533.56 0 446.44
2 6973328053 438.68 0 7.76
3 6936120606 320.51 312.75 0

Oracle PIVOT a select statement

I would like to pivot a select statement.
Columns "Country", "Store" and "Sales" are given.
Now I would like to have an output like:
Store1 Store2 Store3
Country1 2342 2342 5675
Country2 5753 3274 7326
Country3 1543 4367 3367
So basically I need the salescount for every Store, for every Country.
The Input comes from (example):
Country: StoreNr: ProductSold:
Belgium 23 Car
Belgium 23 House
Netherland 23 Car
Output would be:
Store23
Belgium 2
Netherlands 1
If the number of stores is finite, you could use one of these approaches:
Using count() aggregate function combined with case expression:
-- sample of data. just for the sake of demonstration
SQL> with t1(Country, StoreNr, ProductSold) as(
2 select 'Belgium' , 23, 'Car' from dual union all
3 select 'Belgium' , 23, 'House' from dual union all
4 select 'Netherland', 23, 'Car' from dual union all
5 select 'Belgium' , 25, 'House' from dual
6 )
7 select country
8 , count(case
9 when StoreNr = 23
10 then 1
11 end) as storeNr_23
12 , count(case
13 when StoreNr = 25
14 then 1
15 end) as storeNr_25
16 from t1
17 group by country
18 ;
Result:
COUNTRY STORENR_23 STORENR_25
---------- ---------- ----------
Belgium 2 1
Netherland 1 0
Starting from Oracle 11g and up, the pivot operator as follows:
select *
from (Select country as country
, country as country_cnt
, StoreNr
from t1)
pivot( -- list all store numbers here
count(country_cnt) for storenr in ( 23 as StoreNr_23
, 25 as StoreNr_25)
)
Result:
COUNTRY STORENR_23 STORENR_25
---------- ---------- ----------
Belgium 2 1
Netherland 1 0

Resources