Home > OS >  OracleSQL - calculate difference (and return in %growth) in data between sysdate and sysdate-7 days
OracleSQL - calculate difference (and return in %growth) in data between sysdate and sysdate-7 days

Time:04-21

(OracleSQL) We have our own created application that tracks storage usage in MB. I'd like a query that, without manual changes (as we will integrate this query in our application, which will automatically periodically run this query) can calculate the difference of storage for the past week since sysdate. Bonuspoints if we can calculate this difference in %growth too!

The table we use has automatically collected information on storage. It checks every X minutes (I honestly am not sure how often, the data seems rather random, but it's at least hourly and often more often than that).

Client_Name Check_Timestamp Total_MB Used_MB Remaining_MB
Client1 03/28/2022 15:04:37 52280 20533 34747
Client1 03/28/2022 14:01:20 52280 20421 31859
Client1 03/21/2022 14:01:20 51090 20300 30790
Client1 03/21/2022 13:49:20 51090 20121 30969
Client2 03/28/2022 15:34:17 52280 20533 34747
Client2 03/28/2022 14:20:22 52280 20421 31859
Client2 03/21/2022 14:09:54 51090 20300 30790
Client2 03/21/2022 13:32:03 51090 20121 30969

My attempts have failed miserably this far.

Any help would be greatly appreciated.

CodePudding user response:

Here is something to get you started. This query will find the most recent entry for each client (comparing to SYSDATE), and the most recent entry as of exactly seven days before SYSDATE. You might need to wrap SYSDATE within TRUNC if you want to compare to TRUNC(SYSDATE) instead.

You can make this into a subquery and do any additional computations you need, such as increase in MB and in percentage points. That part is trivial; if you can't add it yourself, perhaps you shouldn't be working on such problems in the first place.

Since I am running this on 04/20/2022 at 10:26 AM my time, your sample data isn't relevant. I used this instead (only relevant columns):

create table storage_tracking
( client_name     varchar2(15)
, check_timestamp timestamp 
, used_mb number
);

alter session set nls_timestamp_format = 'mm/dd/yyyy hh24:mi:ss';

insert into storage_tracking
  select 'Client1', '04/20/2022 10:04:37',   20533 from dual union all
  select 'Client1', '04/19/2022 04:01:20',   20421 from dual union all
  select 'Client1', '04/13/2022 14:01:20',   20300 from dual union all
  select 'Client1', '04/13/2022 08:49:20',   20121 from dual union all
  select 'Client2', '04/20/2022 10:24:17',   20533 from dual union all
  select 'Client2', '04/20/2022 08:20:22',   20421 from dual union all
  select 'Client2', '04/14/2022 14:09:54',   20300 from dual union all
  select 'Client2', '04/12/2022 07:32:03',   20121 from dual
;

commit;

Query and output:

select client_name,
       max(case when check_timestamp <= sysdate - 7 
                then check_timestamp end) as week_earlier,
       max(used_mb) keep (dense_rank last order by case 
                when check_timestamp <= sysdate - 7
                then check_timestamp end nulls first) as used_mb_week_earlier,
       max(case when check_timestamp <= sysdate
                then check_timestamp end) as now,
       max(used_mb) keep (dense_rank last order by case
                when check_timestamp <= sysdate
                then check_timestamp end nulls first) used_mb_now
from   storage_tracking
group  by client_name
;

CLIENT_NAME     WEEK_EARLIER        USED_MB_WEEK_EARLIER NOW                 USED_MB_NOW
--------------- ------------------- -------------------- ------------------- -----------
Client1         04/13/2022 08:49:20                20121 04/20/2022 10:04:37       20533
Client2         04/12/2022 07:32:03                20121 04/20/2022 10:24:17       20533

CodePudding user response:

If you just want the values for a day and want the maximum total and used and the minimum remaining then:

SELECT t.*,
       ROUND((total_mb / total_mb_last_week) * 100 - 100, 2) AS percent_total_growth,
       ROUND((used_mb / used_mb_last_week) * 100 - 100, 2) AS percent_used_growth,
       ROUND((remaining_mb / remaining_mb_last_week) * 100 - 100, 2) AS percent_remaining_growth
FROM   (
  SELECT client_name,
         TRUNC(Check_timestamp) AS check_day,
         MAX(total_mb) AS total_mb,
         MAX(used_mb) AS used_mb,
         MIN(remaining_mb) AS remaining_mb,
         MAX(MAX(total_mb)) OVER (
           PARTITION BY Client_Name
           ORDER BY TRUNC(Check_Timestamp)
           RANGE BETWEEN INTERVAL '7' DAY PRECEDING AND INTERVAL '7' DAY PRECEDING
         ) AS total_mb_last_week,
         MAX(MAX(used_mb)) OVER (
           PARTITION BY Client_Name
           ORDER BY TRUNC(Check_Timestamp)
           RANGE BETWEEN INTERVAL '7' DAY PRECEDING AND INTERVAL '7' DAY PRECEDING
         ) AS used_mb_last_week,
         MAX(MIN(remaining_mb)) OVER (
           PARTITION BY Client_Name
           ORDER BY TRUNC(Check_Timestamp)
           RANGE BETWEEN INTERVAL '7' DAY PRECEDING AND INTERVAL '7' DAY PRECEDING
         ) AS remaining_mb_last_week
  FROM   table_name
  GROUP BY client_name, TRUNC(Check_Timestamp)
) t
WHERE check_day = DATE '2022-03-28';

Will, for your sample data:

CREATE TABLE table_name (Client_Name, Check_Timestamp, Total_MB, Used_MB, Remaining_MB) AS
SELECT 'Client1', DATE '2022-03-28'   INTERVAL '15:04:37' HOUR TO SECOND, 52280, 20533, 34747 FROM DUAL UNION ALL
SELECT 'Client1', DATE '2022-03-28'   INTERVAL '14:01:20' HOUR TO SECOND, 52280, 20421, 31859 FROM DUAL UNION ALL
SELECT 'Client1', DATE '2022-03-21'   INTERVAL '14:01:20' HOUR TO SECOND, 51090, 20300, 30790 FROM DUAL UNION ALL
SELECT 'Client1', DATE '2022-03-21'   INTERVAL '13:49:20' HOUR TO SECOND, 51090, 20121, 30969 FROM DUAL UNION ALL
SELECT 'Client2', DATE '2022-03-28'   INTERVAL '15:34:17' HOUR TO SECOND, 52280, 20533, 34747 FROM DUAL UNION ALL
SELECT 'Client2', DATE '2022-03-28'   INTERVAL '14:20:22' HOUR TO SECOND, 52280, 20421, 31859 FROM DUAL UNION ALL
SELECT 'Client2', DATE '2022-03-21'   INTERVAL '14:09:54' HOUR TO SECOND, 51090, 20300, 30790 FROM DUAL UNION ALL
SELECT 'Client2', DATE '2022-03-21'   INTERVAL '13:32:03' HOUR TO SECOND, 51090, 20121, 30969 FROM DUAL;

Output:

CLIENT_NAME CHECK_DAY TOTAL_MB USED_MB REMAINING_MB TOTAL_MB_LAST_WEEK USED_MB_LAST_WEEK REMAINING_MB_LAST_WEEK PERCENT_TOTAL_GROWTH PERCENT_USED_GROWTH PERCENT_REMAINING_GROWTH
Client1 28-MAR-22 52280 20533 31859 51090 20300 30790 2.33 1.15 3.47
Client2 28-MAR-22 52280 20533 31859 51090 20300 30790 2.33 1.15 3.47

If you want the values for the current day (which you have not provided data for) then change the last line to:

WHERE  check_day = TRUNC(SYSDATE);

Or, if you want to get the values for all days in the database then remove that WHERE clause.

db<>fiddle here

  • Related