Dropping Data into Aggregate Buckets by Date and Category - sql

I'm trying to display aggregate counts of open and closed IT tickets by date and category.
Parent table consists of the following columns:
Alert_ID Alert_Open_Date Alert_Closed_Date
I'd like my end result to resemble the following, where I have
A. A date within any specified date range,
B. total number of alerts that still showed open as of that date (Outstanding_Alerts),
C. total number of alerts that were opened on that date (New_Alerts),
D. total number of the new alerts that were closed on that date (Closed_New_Alerts), and
E. combined number of alerts, both new and old, that were closed on that date (Closed_Total):
Date Outstanding_Alerts New_Alerts Closed_New_Alerts Closed_Total
6/1/2018 20 10 5 7
6/2/2018 23 20 8 10
6/3/2018 33 13 10 15
etc. # # # #
I was thinking of something like the following conceptual query to accomplish this, but I'm stumbling over the logic to get the results I'd like. Regardless of wording I can't seem to get the buckets correct. Some columns remain blank when they should be populated, for example. Any help is appreciated.
SELECT DISTINCT
alert_date
, SUM(OOA) AS Outstanding_Open_Alerts
, SUM(NOA) AS New_Open_Alerts
, SUM(CNA*NOA) AS Closed_New_Alerts
, SUM(CT) AS Total_Closed_Alerts
, SUM(CNA+NOA-CT) AS Remaining_Alerts --optional column
FROM
(SELECT
TRUNC(open_date) AS Alert_Date
, CASE WHEN alert_date < trunc(SYSDATE)-1 AND closed_date IS NULL THEN 1 ELSE 0
END AS OOA --old open alerts
, CASE WHEN alert_date > trunc(SYSDATE)-1 THEN 1 ELSE 0
END AS NOA --new open alerts
, CASE WHEN closed_date >= trunc(SYSDATE)-1 THEN 1 ELSE 0
END AS CNA --closed new alerts
, CASE WHEN closed_date < trunc(SYSDATE)-1 THEN 1 ELSE 0
END AS CT --closed total
FROM sys_alerts)
GROUP BY alert_date;

If I'm following your description, to get the numbers for an arbitrary date supplied as some_date you want something like:
SELECT
some_date AS Alert_Date
, COUNT(CASE WHEN open_date < some_date
AND (closed_date IS NULL OR closed_date > some_date
THEN alert_id END) AS Current_Open_Alerts
, COUNT(CASE WHEN open_date >= some_date
AND open_date < some_date + 1
THEN alert_id END AS New_Alerts
, COUNT(CASE WHEN open_date >= some_date
AND open_date < some_date + 1
AND closed_date < some_date + 1
THEN alert_id END AS Closed_New_Alerts
, COUNT(CASE WHEN closed_date >= some_date
AND closed_date < some_date + 1
THEN alert_id END AS Closed_Total
, COUNT(CASE WHEN open_date < some_date + 1
AND (closed_date IS NULL OR closed_date >= some_date + 1
THEN alert_id END) AS Alerts_Remaining
FROM sys_alerts
GROUP BY some_date
I've used a count rather than a sum, which relies on the fact that count ignores nulls - and left the default from the case expresssions as null. It doesn't need a sunquery really so I've removed that.
It seems like you want all data for a range of dates, so you can generate that in an inline view or CTE and then join to your real table, so some_date becomes wach individual date from that generated range. E.g. to get the last 30 days something like:
FROM (
SELECT sysdate - level AS some_date
FROM dual
CONNECT BY level <= 30
LEFT JOIN sys_alerts
ON closed_date IS NULL OR closed_date >= some_date
GROUP BY some_date
ORDER BY some_date
or you could do something with cross apply or a partitioned outer join. Hopefully this gives you a starting point though.

Related

It is required to write a query that returns, for two dates, the total balances of active deals on these dates in SQL

The following table is given:
Columns
Deal start date
Deal end date
Amount
There is a table:
Start Date
End Date
Amount
01.01.2020
01.01.2021
1
15.04.2020
15.04.2021
3
It is required to write a query that returns, for two dates, the total balances of active deals on these dates.
It is required to display balances on date 1 = 02.05.2020 and date 2 = 02.02.2021
I know how to get them one by one
SELECT SUM(Amount)
FROM Table
WHERE 02.05.2020 >= End Date
Or
SELECT SUM(Amount)
FROM Table
WHERE 02.05.2020 >= End Date OR 02.02.2021 >= End Date
But I have no idea how to create a separate table for dates:
date 1 || amount 1
date 2 || amount 2
Can you give me some advice or direction for a solution?
You could use conditional aggregation or filtered sum function as the following:
SELECT
SUM(CASE WHEN '02.05.2020' >= End_Date THEN Amount ELSE 0 END) AS Date1,
SUM(CASE WHEN '02.05.2020' >= End_Date OR '02.02.2021' >= End_Date THEN Amount ELSE 0 END) AS Date2
FROM table_name
Or:
SELECT
SUM(Amount) FILTER (WHERE '02.05.2020' >= End_Date) AS Date1,
SUM(Amount) FILTER (WHERE '02.05.2020' >= End_Date OR '02.02.2021' >= End_Date) AS Date2
FROM table_name
See a demo.

Count on a column with a conditional statement in SQL?

I want a table as this:
cycle_end_date | count(user_id) | count(repaid_user_id) | event_ts | repayment_ts
15th Jan | 20 | 15 | 23rd Jan | 25th Jan
15th Jan | 30 | 30 | 24th Jan | 24th Jan
I am using the following query:
select
date(cycle_end_date) as cycle_end_date
,date(payload_event_timestamp + interval '5 hours 30 minutes') as event_date
,d.payment_date
,count( s.user_id) as count
,count(case s.user_id when d.payment_date < event_date then 1 else 0 end) as repaid_users
d.payment_date is taken from another query -
case when ots_created_at is null then
current_date else date(ots_created_at + interval ' 5 hours 30 minutes')
end as payment_date
this is giving me an error : ProgrammingError: operator does not exist: character varying = boolean
Basically I want the count of users who repaid on the day event was triggered. which is basically if event date is equal to repayment date we can find out that these many users paid on that event date. How to find this?
You have attempted to use a "short form" of case expression which starts like this:
case s.user_id when d.payment_date
For this format to be valid the syntax after when assumes an equal comparison (e.g. when d.payment_date assumes =) and in this short form it can only be an equality operator that is assumed. For more complex comparisons (less than, not equal, etc.) it is necessary to use the longer format of a case expression, like this:
case when d.payment_date < event_date then user_id end
Note that the aggregate function COUNT() increments for every non-null value, so to avoid counting any unwanted conditions do NOT return any value using else. e.g.
count(case when d.payment_date < event_date then user_id end) as col1
or, if you prefer to be more explicit in the query code, use else to return NULL which will not be counted, e.g.
count(case when d.payment_date < event_date then user_id else NULL end) as col1
nb: Instead of returning a column value you could use a constant like this:
count(case when d.payment_date < event_date then 1 end) as col1

SQL Report - query multiple tables

I have been working on a Stats page in APEX and currently have the following report query:
select to_char(DATELOGGED,'Month - YYYY') as Month,
COUNT(*) as "Total Calls",
SUM(case when CLOSED is null then 1 else null end) as "Open",
COUNT(case CLOSED when 'Y' then 1 else null end) as "Closed",
SUM(case when EXTREF is null then 0 else 1 end) as "Referred",
round((COUNT(case SLA_MET when 'Y' then 1 else null end)/COUNT(case CLOSED when 'Y' then 1 else null end)*100),2) as "SLA Met %"
from IT_SUPPORT_CALLS
GROUP BY to_char(DATELOGGED,'Month - YYYY')
order by MIN (DATELOGGED) desc
I wish to add the sum of DURATION from a different table:
select
"START_TIME",
DECODE(DURATION,null,'Open',((select extract( minute from DURATION )
+ extract( hour from DURATION ) * 60
+ extract( day from DURATION ) * 60 * 24
from dual)||' minutes')) DURATION
from "IT_DOWNTIME"
The IT_DOWNTIME table uses START_TIME (varchar2) as the date identifier, the IT_SUPPORT_CALLS uses DATELOGGED (DATE) as date identifier.
The current output for IT_DOWNTIME is for example:
08-FEB-2019 - 30 Minutes
20-FEB-2019 - 15 Minutes
I would like the report SUM and group IT_DOWNTIME and add this into the existing report.
Hope this makes sense.
Please let me know if I missed any information that would help to resolve this.
Many thanks
Thanks for that, much appreciated. Unfortunately it doesn't return any data from IT_DOWNTIME.
I'm guessing the different date formats doesn't help, hope this clears things up a bit:
These are the columns in IT_DOWNTIME that are of interest:
START_TIME ( VARCHAR2(30) )
DURATION ( INTERVAL DAY(2) TO SECOND(6) )
Example of current IT_DOWNTIME output without formatting:
START_TIME
06-JUL-2016 11:05
DURATION
+00 00:35:00.000000
Example of current IT_SUPPORT_CALLS output without formatting:
DATELOGGED
06/07/2016
Something like this will probably do it, but there has been some guesswork as to your column names etc:
SELECT *
FROM
(
SELECT
to_char(DATELOGGED,'MON-YYYY') as Month,
COUNT(*) as Total_Calls,
SUM(case when CLOSED is null then 1 else null end) as case_Open,
COUNT(case CLOSED when 'Y' then 1 else null end) as case_Closed,
SUM(case when EXTREF is null then 0 else 1 end) as case_Referred,
round((COUNT(case SLA_MET when 'Y' then 1 else null end)/COUNT(case CLOSED when 'Y' then 1 else null end)*100),2) as percent_SLA_met
FROM IT_SUPPORT_CALLS
GROUP BY to_char(DATELOGGED,'MON-YYYY')
) calls
LEFT JOIN
(
SELECT
SUBSTR(START_TIME, 4) as down_month,
SUM(extract(minute from DURATION) +
extract(hour from DURATION) * 60 +
extract(day from DURATION) * 60 * 24
) || 'minutes' as total_down_mins
FROM IT_DOWNTIME
WHERE duration is not null
GROUP BY SUBSTR(START_TIME, 4)
) downs
ON calls.month = downs.down_month
Changed your date formatting of the first query to be MON-YYYY to make it align with what you claim is the formatting of the varchar2 date of the second query (dd-mon-yyy), and substringed the date to remove the day, leaving just the month
Edit:
Ok, so since you've posted some different example data from IT_DOWNTIME I see the problem: there's a time on the date also. Your first sample data didn't contain this time, it was just a date (as a string) so I was doing...
SUBSTR('01-JAN-1970', 4)
...to reduce the day date to a month date ('JAN-1970') and this was intended to align with the stuff going on in the other table ( to_date() with a format of 'non-yyyy' )
Now we know that there is a time in there too, of course it won't align because...
SUBSTR('01-JAN-1970 12:34', 4)
...produces 'JAN-1970 12:34' and this will then not match to anything from the other table (which will be just 'JAN-1970' without the time), so the left join means that nulls will be output
The solution is to change the SUBSTR call so it cuts 8 characters, starting at position 4:
SUBSTR(start_time, 4, 8)
This will remove the day and the time, leaving just the month-year that we need. You'll need to make the change in two places in the query above..
Apologies for the delay on replying to this. However, that is working perfectly Caius, thanks very much! So to be complete, had to change your above code to:
SUBSTR(START_TIME, 4, 8) as down_month,
and
GROUP BY SUBSTR(START_TIME, 4, 8)

Grouping by Date without Time component

So i have a table that looks like this:
As you can see some of the dates are the same however the time of day is different.
i have the following select statement (i have deleted and altered some data since this is rather sensitive):
SELECT LAST_UPD AS PERIOD,
COUNT(CASE WHEN SOLVED_SECONDS /60 /60 <= 2 THEN 1 END) as completed_within_2hours,
COUNT(CASE WHEN STATUS ='Færdig' THEN +1 END) as completed_callbacks,
COUNT(CASE WHEN SOLVED_SECONDS /60 /60 <= 2 THEN 1 END) / COUNT(CASE WHEN STATUS ='Færdig' THEN 1 END) * 100 as Percentage
FROM TABLE
WHERE LAST_UPD BETWEEN '2013-07-01' AND '2013-07-03'
AND STATUS ='Færdig'
AND AGENTGROUP IN ('Hovednumre','Forsikring','Hotline','Kunder')
GROUP BY LAST_UPD
Now i want my query to count when the DATES are the same so forexample if the date is 2013-07-01 i want it to count x amount for that day regardless of time of day.
I have tried the following:
SELECT DISTINCT TO_CHAR(LAST_UPD, 'YYYY/MM/DD') AS PERIOD,
Sadly this returns each of days but doesnt count them correctly (it returns 1 for each day).
Perhaps what you want is to select and group by "Trunc(last_upd)"

How do I use Group By based on a Case statement in Oracle?

I have an SQL-query where I use Oracle CASE to compare if a date column is less than or greater than current date. But how do I use that CASE-statement in a GROUP BY-statement? I would like to count the records in each case.
E.g.
select
(case
when exp_date > sysdate then 1
when exp_date <= sysdate then 2
else 3
end) expired, count(*)
from mytable
group by expired
But I get an error when trying this: ORA-00904. Any suggestions?
select
(case
when exp_date > sysdate then 1
when exp_date <= sysdate then 2
else 3
end) expired, count(*)
from mytable
group by (case
when exp_date > sysdate then 1
when exp_date <= sysdate then 2
else 3
end)
Use an inline view:
SELECT expired,
count(*)
FROM (SELECT (CASE
WHEN exp_date > SYSDATE THEN 1
WHEN exp_date <= SYSDATE THEN 2
ELSE 3
END) AS expired
FROM mytable)
GROUP BY expired;
Hope it helps...
As an additional input, although it does not relate to this thread, you can also aggregate your case statements without mentioning it in the group by clause.
SELECT
WORK_ORDER_NUMBER,
SUM(CASE WHEN STOCK_CODE LIKE 'xxxx' THEN STOCK_QUANTITY ELSE 0 END) AS TOTAL
FROM Table
GROUP BY WORK_ORDER_NUMBER;