Oracle: Optimizing twice self-join query - oracle

I've trying to get this query work efficiently over the past two days. I've learn more about Oracle Index behavior that I think I'm confused at this point what is supposed to work and what doesn't.
Basically, the query is summing up values and comparing to values from yesterday and last week.
I've played around with breaking it down, I've toyed in my mind analytic queries and changing order of indices but nothing seems to work. All my test has been on a table with 500K rows, as soon as I run it on a table with 20 Million rows it just takes forever.
Any help is greatly appreciated.
I modified the original post to help you help me. :)
CREATE TABLE TABLE_1
(ORDER_LINE_ID NUMBER, OFFSET NUMBER, BREAK_ID NUMBER, ZONE NUMBER, NETWORK NUMBER, HOUR_OF_DAY NUMBER, START_TIME DATE, END_TIME DATE, SUCCESS NUMBER
CONSTRAINT "TABLE_1_PK" PRIMARY KEY (ORDER_LINE_ID, OFFSET, BREAK_ID, ZONE, HOUR_OF_DAY))
-- SUCCESS is already aggregated during the insert
-- These are last week's records
INSERT INTO TABLE_1 (ORDER_LINE_ID, OFFSET, BREAK_ID, ZONE, NETWORK, HOUR_OF_DAY, START_TIME, END_TIME, SUCCESS)
VALUES (1,0,1, 1, 1, 2016042001,'04/20/2016 00:00:00', '04/20/2016 02:00:00', 1);
INSERT INTO TABLE_1 (ORDER_LINE_ID, OFFSET, BREAK_ID, ZONE, NETWORK, HOUR_OF_DAY, START_TIME, END_TIME, SUCCESS)
VALUES (1,30,1, 1, 1, 2016042001,'04/20/2016 00:00:00', '04/20/2016 02:00:00', 2);
INSERT INTO TABLE_1 (ORDER_LINE_ID, OFFSET, BREAK_ID, ZONE, NETWORK, HOUR_OF_DAY, START_TIME, END_TIME, SUCCESS)
VALUES (2,0,1, 1, 1, 2016042001,'04/20/2016 00:00:00', '04/20/2016 02:00:00', 1);
INSERT INTO TABLE_1 (ORDER_LINE_ID, OFFSET, BREAK_ID, ZONE, NETWORK, HOUR_OF_DAY, START_TIME, END_TIME, SUCCESS)
VALUES (2,30,1, 1, 1, 2016042001,'04/20/2016 00:00:00', '04/20/2016 02:00:00', 1);
-- These are yesterday's records
INSERT INTO TABLE_1 (ORDER_LINE_ID, OFFSET, BREAK_ID, ZONE, NETWORK, HOUR_OF_DAY, START_TIME, END_TIME, SUCCESS)
VALUES (3,0,1, 1, 1, 2016042601,'04/26/2016 00:00:00', '04/26/2016 02:00:00', 1);
INSERT INTO TABLE_1 (ORDER_LINE_ID, OFFSET, BREAK_ID, ZONE, NETWORK, HOUR_OF_DAY, START_TIME, END_TIME, SUCCESS)
VALUES (3,30,1, 1, 1, 2016042601,'04/26/2016 00:00:00', '04/26/2016 02:00:00', 2);
INSERT INTO TABLE_1 (ORDER_LINE_ID, OFFSET, BREAK_ID, ZONE, NETWORK, HOUR_OF_DAY, START_TIME, END_TIME, SUCCESS)
VALUES (4,0,1, 1, 1, 2016042601,'04/26/2016 00:00:00', '04/26/2016 02:00:00', 1);
INSERT INTO TABLE_1 (ORDER_LINE_ID, OFFSET, BREAK_ID, ZONE, NETWORK, HOUR_OF_DAY, START_TIME, END_TIME, SUCCESS)
VALUES (4,30,1, 1, 1, 2016042601,'04/26/2016 00:00:00', '04/26/2016 02:00:00', 1);
-- This is today's records
INSERT INTO TABLE_1 (ORDER_LINE_ID, OFFSET, BREAK_ID, ZONE, NETWORK, HOUR_OF_DAY, START_TIME, END_TIME, SUCCESS)
VALUES (5,0,1, 1, 1, 2016042701,'04/27/2016 00:00:00', '04/27/2016 02:00:00', 1);
INSERT INTO TABLE_1 (ORDER_LINE_ID, OFFSET, BREAK_ID, ZONE, NETWORK, HOUR_OF_DAY, START_TIME, END_TIME, SUCCESS)
VALUES (5,30,1, 1, 1, 2016042701,'04/27/2016 00:00:00', '04/27/2016 02:00:00', 1);
-- Original twice join query
SELECT BREAK_ID, ORDER_LINE_ID, HOUR_OF_DAY, OFFSET, ZONE, NETWORK, START_TIME, END_TIME, SUM(SUCCESS), SUM(YESTERDAY_SUCCESS), SUM(LAST_WEEK_SUCCESS)
FROM TABLE_1 CURRENT_DAY
LEFT OUTER JOIN (
SELECT SUM(SUCCESS) YESTERDAY_SUCCESS, ZONE, NETWORK, HOUR_OF_DAY, START_TIME, END_TIME FROM TABLE_1
GROUP BY ZONE, NETWORK, HOUR_OF_DAY, START_TIME, END_TIME
) YESTERDAY
ON YESTERDAY.START_TIME + 1 = CURRENT_DAY.START_TIME
AND YESTERDAY.END_TIME + 1 = CURRENT_DAY.END_TIME
AND YESTERDAY.HOUR_OF_DAY = CURRENT_DAY.HOUR_OF_DAY
AND YESTERDAY.NETWORK = CURRENT_DAY.NETWORK
AND YESTERDAY.ZONE = CURRENT_DAY.ZONE
LEFT OUTER JOIN (
SELECT SUM(SUCCESS) LAST_WEEK_SUCCESS, ZONE, NETWORK, HOUR_OF_DAY, START_TIME, END_TIME FROM TABLE_1
GROUP BY ZONE, NETWORK, HOUR_OF_DAY, START_TIME, END_TIME
) LAST_WEEK
ON YESTERDAY.START_TIME + 7 = CURRENT_DAY.START_TIME
AND YESTERDAY.END_TIME + 7 = CURRENT_DAY.END_TIME
AND YESTERDAY.HOUR_OF_DAY = CURRENT_DAY.HOUR_OF_DAY
AND YESTERDAY.NETWORK = CURRENT_DAY.NETWORK
AND YESTERDAY.ZONE = CURRENT_DAY.ZONE
GROUP BY BREAK_ID, ORDER_LINE_ID, HOUR_OF_DAY, OFFSET, ZONE, NETWORK, START_TIME, END_TIME;
-- Using Analytic Query (thank you to MT0)
SELECT BREAK_ID, ORDER_LINE_ID, HOUR_OF_DAY, OFFSET, ZONE, NETWORK, START_TIME, END_TIME, SUM(SUCCESS), SUM(YESTERDAY_SUCCESS), SUM(LAST_WEEK_SUCCESS)
FROM (
SUM( SUCCESS )
OVER ( PARTITION BY ZONE, NETWORK, HOUR_OF_DAY, TO_CHAR(START_TIME, 'HH24:MI:SS'), TO_CHAR(END_TIME, 'HH24:MI:SS')
ORDER BY START_TIME
RANGE BETWEEN INTERVAL '1' DAY PRECDEDING AND INTERVAL '1' DAY PRECEDING
) AS YESTERDAY_SUCCESS,
SUM ( SUCCESS )
OVER ( PARTITION BY ZONE, NETWORK, HOUR_OF_DAY, TO_CHAR(START_TIME, 'HH24:MI:SS'), TO_CHAR(END_TIME, 'HH24:MI:SS')
ORDER BY START_TIME
RANGE BETWEEN INTERVAL '7' DAY PRECDEDING AND INTERVAL '7' DAY PRECEDING
) AS LAST_WEEK_SUCCESS
FROM TABLE_1
) T1
WHERE SYSDATE - INTERVAL '12' HOUR <= START_TIME
AND START_TIME < SYSDATE - INTERVAL '1' HOUR
GROUP BY BREAK_ID, ORDER_LINE_ID, HOUR_OF_DAY, OFFSET, ZONE, NETWORK, START_TIME, END_TIME;
I must say thank you for the help to bring this question up to something I hope is more understandable. Everything works as expected but performance could use some tuning.
1.8 seconds on a table with 500K rows
400 seconds on a table with 20 million rows
i also want to add some Execution Plans provided by Oracle. I'm having trouble tuning performance.
-- using twice self join
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads | Writes | OMem | 1Mem | O/1/M |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 50 |00:00:00.84 | 99875 | 217 | 1705 | | | |
| 1 | HASH GROUP BY | | 1 | 6711 | 50 |00:00:00.84 | 99875 | 217 | 1705 | 1616K| 995K| |
|* 2 | FILTER | | 1 | | 119K|00:00:00.65 | 99875 | 0 | 0 | | | |
| 3 | NESTED LOOPS OUTER | | 1 | 54M| 119K|00:00:00.64 | 99875 | 0 | 0 | | | |
|* 4 | HASH JOIN OUTER | | 1 | 109 | 119K|00:00:00.52 | 99875 | 0 | 0 | 13M| 2093K| 1/0/0|
| 5 | TABLE ACCESS BY INDEX ROWID| TABLE_1_IDX | 1 | 109 | 119K|00:00:00.14 | 85908 | 0 | 0 | | | |
|* 6 | INDEX RANGE SCAN | START_TIME_IDX | 1 | 109 | 119K|00:00:00.02 | 320 | 0 | 0 | | | |
| 7 | VIEW | | 1 | 1250 | 29311 |00:00:00.23 | 13967 | 0 | 0 | | | |
| 8 | HASH GROUP BY | | 1 | 1250 | 29311 |00:00:00.22 | 13967 | 0 | 0 | 3008K| 1094K| 1/0/0|
|* 9 | FILTER | | 1 | | 88627 |00:00:00.20 | 13967 | 0 | 0 | | | |
|* 10 | TABLE ACCESS FULL | TABLE_1 | 1 | 1250 | 88627 |00:00:00.19 | 13967 | 0 | 0 | | | |
| 11 | VIEW | | 119K| 499K| 0 |00:00:00.10 | 0 | 0 | 0 | | | |
| 12 | SORT GROUP BY | | 119K| 499K| 0 |00:00:00.08 | 0 | 0 | 0 | 1024 | 1024 | 1/0/0|
|* 13 | FILTER | | 119K| | 0 |00:00:00.02 | 0 | 0 | 0 | | | |
| 14 | TABLE ACCESS FULL | TABLE_1 | 0 | 499K| 0 |00:00:00.01 | 0 | 0 | 0 | | | |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter(SYSDATE#!-17<SYSDATE#!-16)
4 - access("YESTERDAY"."ZONE"="T1"."ZONE" AND "YESTERDAY"."NETWORK"="T1"."NETWORK" AND "YESTERDAY"."HOUR_OF_DAY"="T1"."HOUR_OF_DAY"
AND "T1"."END_TIME"=INTERNAL_FUNCTION("YESTERDAY"."END_TIME")+1 AND
"T1"."START_TIME"=INTERNAL_FUNCTION("YESTERDAY"."START_TIME")+1)
6 - access("T1"."START_TIME">=SYSDATE#!-17 AND "T1"."START_TIME"<SYSDATE#!-16)
9 - filter(SYSDATE#!-17<SYSDATE#!-16)
10 - filter((INTERNAL_FUNCTION("START_TIME")+1>=SYSDATE#!-17 AND INTERNAL_FUNCTION("START_TIME")+1<SYSDATE#!-16))
13 - filter(("YESTERDAY"."ZONE"="T1"."ZONE" AND "YESTERDAY"."NETWORK"="T1"."NETWORK" AND "YESTERDAY"."HOUR_OF_DAY"="T1"."HOUR_OF_DAY"
AND "T1"."END_TIME"=INTERNAL_FUNCTION("YESTERDAY"."END_TIME")+7 AND
"T1"."START_TIME"=INTERNAL_FUNCTION("YESTERDAY"."START_TIME")+7))
Another execution plan using Analytic Query (thank again to MT0)
-- using analytic query
-------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | O/1/M |
-------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 50 |00:00:01.51 | 13967 | | | |
| 1 | HASH GROUP BY | | 1 | 499K| 50 |00:00:01.51 | 13967 | 98M| 7788K| |
|* 2 | VIEW | | 1 | 499K| 119K|00:00:01.15 | 13967 | | | |
| 3 | WINDOW SORT | | 1 | 499K| 499K|00:00:01.43 | 13967 | 66M| 2823K| 1/0/0|
|* 4 | FILTER | | 1 | | 499K|00:00:00.16 | 13967 | | | |
| 5 | TABLE ACCESS FULL| TABLE_1 | 1 | 499K| 499K|00:00:00.12 | 13967 | | | |
-------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter(("T1"."START_TIME">=SYSDATE#!-INTERVAL'+17 00:00:00' DAY(2) TO SECOND(0) AND
"T1"."START_TIME"<SYSDATE#!-INTERVAL'+16 00:00:00' DAY(2) TO SECOND(0)))
4 - filter(SYSDATE#!-INTERVAL'+17 00:00:00' DAY(2) TO SECOND(0)<SYSDATE#!-INTERVAL'+16 00:00:00' DAY(2) TO
SECOND(0))
As you can see, I added an index on start_time which self-Join query benefits from but estimates vs actuals are off. Where Analytic Query just decides it wants nothing to do with the index. Any ideas, points of references or help is greatly appreciated. Thanks in advance, everyone.

It is unclear why you are joining only when there are rows with exactly the same time today and yesterday (or last week) but if you just want the rows that are between certain times then you can eliminate all the self-joins and do:
SELECT order_line,
zone,
network,
sum(
CASE WHEN SYSDATE - INTERVAL '12' HOUR <= start_time
AND start_time < SYSDATE - INTERVAL '1' HOUR
THEN success
END
) AS total_successes_today,
sum(
CASE WHEN SYSDATE - INTERVAL '12' HOUR <= start_time
AND start_time < SYSDATE - INTERVAL '1' HOUR
THEN error
END
) AS total_errors_today,
sum(
CASE WHEN SYSDATE - INTERVAL '36' HOUR <= start_time
AND start_time < SYSDATE - INTERVAL '25' HOUR
THEN success
END
) AS total_successes_yesterday,
sum(
CASE WHEN SYSDATE - INTERVAL '180' HOUR <= start_time
AND start_time < SYSDATE - INTERVAL '169' HOUR
THEN success
END
) AS total_successes_last_week
FROM table_1
WHERE ( SYSDATE - INTERVAL '12' HOUR <= start_time
AND start_time < SYSDATE - INTERVAL '1' HOUR ) -- today
OR ( SYSDATE - INTERVAL '36' HOUR <= start_time
AND start_time < SYSDATE - INTERVAL '25' HOUR ) -- yesterday = today + 24 hours
OR ( SYSDATE - INTERVAL '180' HOUR <= start_time
AND start_time < SYSDATE - INTERVAL '169' HOUR ) -- last week = today + 7*24 hours
However, if you do want to keep the join on the start- and end-times then you could use an analytic query:
SELECT order_line,
zone,
network,
SUM( success ),
SUM( error ),
SUM( yesterday_success ),
SUM( last_week_success )
FROM (
SELECT t.*,
SUM( success )
OVER ( PARTITION BY id,
TO_CHAR( start_time, 'HH24:MI:SS' ),
TO_CHAR( end_time, 'HH24:MI:SS' )
ORDER BY start_time
RANGE BETWEEN INTERVAL '1' DAY PRECEDING AND INTERVAL '1' DAY PRECEDING
) AS yesterday_success,
SUM( success )
OVER ( PARTITION BY id,
TO_CHAR( start_time, 'HH24:MI:SS' ),
TO_CHAR( end_time, 'HH24:MI:SS' )
ORDER BY start_time
RANGE BETWEEN INTERVAL '7' DAY PRECEDING AND INTERVAL '7' DAY PRECEDING
) AS last_week_success
FROM TABLE_1 t
)
WHERE SYSDATE - INTERVAL '12' HOUR <= start_time
AND start_time < SYSDATE - INTERVAL '1' HOUR
GROUP BY
order_line,
zone,
network
ORDER BY
order_line,
zone,
network
You can look at whether you would get speed improvements by using function-based indexes on TO_CHAR( start_time, 'HH24:MI:SS' ) and TO_CHAR( end_time, 'HH24:MI:SS' ).

Related

Oracle sql set end date based on previous start date

I have one table where I need to add new column endDate for future implementation but since we have currently only start date for all records I need to set endDate which should be equal to start date from previous record that are connected by userId and if it is only one record for that user than end date will have some value in future.
For example:
Table structure:
ID | USER_ID | START_DATE | END_DATE
-------------------------------------
1 | 1 | 01.01.2015 |
2 | 1 | 01.01.2016 |
3 | 1 | 01.07.2018 |
4 | 1 | 01.08.2021 |
5 | 2 | 01.01.2015 |
6 | 3 | 01.01.2016 |
7 | 3 | 01.07.2018 |
8 | 4 | 01.08.2021 |
Expected result should be like this
ID | USER_ID | START_DATE | END_DATE
-------------------------------------
1 | 1 | 01.01.2015 | 01.01.2016
2 | 1 | 01.01.2016 | 01.07.2018
3 | 1 | 01.07.2018 | 01.08.2021
4 | 1 | 01.08.2021 | 01.01.2050
5 | 2 | 01.01.2015 | 01.01.2050
6 | 3 | 01.01.2016 | 01.07.2018
7 | 3 | 01.07.2018 | 01.01.2050
8 | 4 | 01.08.2021 | 01.01.2050
Can someone help me with how query in oracle databse should look to update it like this?
I've tried something with for loop but not sure how to continue from this step
DECLARE
CURSOR c_contract
IS
SELECT
USER_ID
FROM
CONTRACT
ORDER_BY START_DATE

BEGIN
FOR r_contract IN c_contract
LOOP

dbms_output.put_line( r_contract.USER_ID );
END LOOP;

END;
Use the LEAD analytic function with the default date as the third argument:
SELECT t.*,
LEAD( start_date, 1, DATE '2050-01-01') OVER (
PARTITION BY user_id
ORDER BY start_date
) AS end_date
FROM table_name t
Which, for the sample data:
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';
CREATE TABLE table_name ( ID, USER_ID, START_DATE ) AS
SELECT 1, 1, DATE '2015-01-01' FROM DUAL UNION ALL
SELECT 2, 1, DATE '2016-01-01' FROM DUAL UNION ALL
SELECT 3, 1, DATE '2018-07-01' FROM DUAL UNION ALL
SELECT 4, 1, DATE '2021-08-01' FROM DUAL UNION ALL
SELECT 5, 2, DATE '2015-01-01' FROM DUAL UNION ALL
SELECT 6, 3, DATE '2016-01-01' FROM DUAL UNION ALL
SELECT 7, 3, DATE '2018-07-01' FROM DUAL UNION ALL
SELECT 8, 4, DATE '2021-08-01' FROM DUAL;
Outputs:
ID
USER_ID
START_DATE
END_DATE
1
1
2015-01-01 00:00:00
2016-01-01 00:00:00
2
1
2016-01-01 00:00:00
2018-07-01 00:00:00
3
1
2018-07-01 00:00:00
2021-08-01 00:00:00
4
1
2021-08-01 00:00:00
2050-01-01 00:00:00
5
2
2015-01-01 00:00:00
2050-01-01 00:00:00
6
3
2016-01-01 00:00:00
2018-07-01 00:00:00
7
3
2018-07-01 00:00:00
2050-01-01 00:00:00
8
4
2021-08-01 00:00:00
2050-01-01 00:00:00
If you want to add a column then:
ALTER TABLE table_name ADD (end_date DATE);
MERGE INTO table_name dst
USING (
SELECT ROWID AS rid,
LEAD( start_date, 1, DATE '2050-01-01') OVER (
PARTITION BY user_id
ORDER BY start_date
) AS end_date
FROM table_name
) src
ON (dst.ROWID = src.rid)
WHEN MATCHED THEN
UPDATE SET end_date = src.end_date;
fiddle

Oracle: Taking columns from rows and putting them side by side

I'm having trouble figuring out how to do something in Oracle. I have this table:
| id | rownum | code | gift |
|-----|--------|-----------|------|
|2000 | 1 | Ganymede | 437 |
|2000 | 2 | Alpha | 50 |
|2000 | 3 | Ambergris | 600 |
And the client wants it to look like this:
| id | code_1 | gift_1 | code_2 | gift_2 | code_3 | gift_3 |
|----|--------|--------|--------|--------|-----------|--------|
|2000|Ganymede| 437 | Alpha | 50 | Ambergris | 600 |
I'm not quite sure how to go about doing this using PIVOT.
You can use PIVOT:
SELECT id,
"1_CODE" AS code_1,
"1_GIFT" AS gift_1,
"2_CODE" AS code_2,
"2_GIFT" AS gift_2,
"3_CODE" AS code_3,
"3_GIFT" AS gift_3
FROM table_name
PIVOT (
MAX(code) AS code,
MAX(gift) AS gift
FOR "ROWNUM" IN (1, 2, 3)
)
or conditional aggregation:
SELECT id,
MAX(CASE "ROWNUM" WHEN 1 THEN code END) AS code_1,
MAX(CASE "ROWNUM" WHEN 1 THEN gift END) AS gift_1,
MAX(CASE "ROWNUM" WHEN 2 THEN code END) AS code_2,
MAX(CASE "ROWNUM" WHEN 2 THEN gift END) AS gift_2,
MAX(CASE "ROWNUM" WHEN 3 THEN code END) AS code_3,
MAX(CASE "ROWNUM" WHEN 3 THEN gift END) AS gift_3
FROM table_name
GROUP BY id
Which, for the sample data:
CREATE TABLE table_name (id, "ROWNUM", code, gift ) AS
SELECT 2000, 1, 'Ganymede', 437 FROM DUAL UNION ALL
SELECT 2000, 2, 'Alpha', 50 FROM DUAL UNION ALL
SELECT 2000, 3, 'Ambergris', 600 FROM DUAL;
Both output:
ID
CODE_1
GIFT_1
CODE_2
GIFT_2
CODE_3
GIFT_3
2000
Ganymede
437
Alpha
50
Ambergris
600
db<>fiddle here

Select SUM with GROUP BY by day interval

I have this table in Oracle Database:
date | column1 | column2
01/05/2020 00:00 | 50 | 20
01/05/2020 00:15 | 60 | 30
01/05/2020 00:30 | 70 | 40
... | |
01/05/2020 23:45 | 80 | 50
02/05/2020 00:00 | 100 | 40
02/05/2020 00:15 | 110 | 35
And I have this SELECT script:
SELECT
period,
liquid
FROM
(
SELECT
( nvl(SUM(m.column1), 0) - nvl(SUM(m.column2), 0) ) liquid,
TO_CHAR(trunc(m.date), 'dd/MM/YYYY') period
FROM
table m
WHERE
m.id_register IN (id_register)
AND m.date BETWEEN TO_DATE('01/05/2020 00:15:00', 'dd/mm/yyyy hh24:mi:ss') AND TO_DATE('01/06/2020 00:00:00', 'dd/mm/yyyy hh24:mi:ss')
GROUP BY
TO_CHAR(trunc(m.date), 'dd/MM/YYYY')
) vrdm
I need to sum (difference column1 and column2) all day registers in interval between 00:15 and 00:00 (next day) per day. How to make it?
The result that I need is like this:
period | liquid
01/05/2020 | 3000 -> SUM(all differences between column1 and column2 between 01/05 00:15 and 02/05 00:00)
02/05/2020 | 4000 -> SUM(all differences between column1 and column2 between 02/05 00:15 and 03/05 00:00)
03/05/2020 | 3500 -> SUM(all differences between column1 and column2 between 03/05 00:15 and 04/05 00:00)
Just check time part in where clause:
SELECT to_char(trunc(m.dt), 'dd/MM/YYYY') period,
nvl(SUM(m.column1), 0) - nvl(SUM(m.column2), 0) liquid
FROM m
WHERE to_char(m.dt, 'hh24:mi') >= '00:15'
GROUP BY trunc(m.dt)
dbfiddle

Count Max number of day continious overdraff

Date | Account | Amount | Count max number of day continuous < 0 |
1 | 1001 | 100 | 0 |
2 | 1001 | -100 | 1 |
3 | 1001 | -100 | 2 |
4 | 1001 | 100 | 2 |
5 | 1001 | -100 | 2 |
6 | 1001 | -100 | 2 |
7 | 1001 | -100 | 3 |
8 | 1001 | -100 | 4 |
9 | 1001 | 100 | 4 |
I have sample data. I want have column "Count max number of day continuous < 0". How i can select it in database oracle
In order to find continuous periods you can use Tabibitosian method. Then use analytical count and finally max:
select date_, account, amount,
max(cnt) over (partition by account order by date_) max_overdraft_period
from (
select date_, account, amount,
count(case when amount <= 0 then 1 end)
over (partition by account, grp order by date_) cnt
from (
select date_, account, amount,
date_ - sum(case when amount <= 0 then 1 else 0 end)
over (partition by account order by date_) grp
from t ))
demo
I assumed that dates are continuous, if not then use row numbering at first.

Edit Total line in Oracle report 1

The data structure:
sql-> desc t1
- List item
p_code number,
acc_date date ,
debit number,
credit number
Data inside my table:
sql-> select * from t1;
| p_code | acc_date | debit | credit |
| 001 | 01-01-15 | 100 | 25 |
| 001 | 02-01-15 | 0 | 125 |
| 001 | 03-01-15 | 415 | 85 |
I would like to do something like this:
select * from t1
where acc_date between :fromdate and :todate
union all
select p_code, (sum(nvl(debit,0))- sum(nvl(credit,0))) open_balance
from t1
where acc_date < :fromdate
;
But, I can't figure out what are my mistakes.
Type and number of columns in union must be the same -
select p_code, acc_date, debit, credit, null as open_balance
from t1
where acc_date between :fromdate and :todate
union all
select p_code, null as acc_date, null as debit, null as credit,
(sum(nvl(debit, 0)) - sum(nvl(credit, 0))) open_balance
from t1
where acc_date < :fromdate

Resources