SQL Report - query multiple tables - sql

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)

Related

Flag 2 actual vs benchmark readings every rolling 12 hours in SQL Developer Query

Looking for some help with code in SQL Developer query to flag any 2 temperature readings - every rolling 12 hours - if they are greater than the acceptable benchmark of 101 deg F.
The given data fields are:
Temp Recorded (DT/TM data type ; down to seconds)
Reading Value (number data type)
Patient ID
There are multiple readings taken throughout a patients stay, at random times.
Logically, we can check if two adjacent times total 12 hrs or more & EACH of their temp readings are >101 but not sure how to put it into a CASE statement (unless there's a better SQL syntax).
Will really appreciate if a SQL only solution can be recommended.
Many Thanks
Giving the code below including the CASE statement as provided by #Gordon Linoff underneath. The below sample code is part of a bigger query joining multiple tables:
SELECT CE.PatientID, CE.ReadingValue, CE.TempRecorded_DT_TM,
(case when sum(case when readingvalue > 101 then 1 else 0 end) over (
partition by patientid
order by dt
range between '12' hour preceding and current row
) >= 2
then 'Y' else 'N'
end) as temp_flag
FROM
edw.se_clinical_event CE
WHERE
CE.PatientID = '176660214'
AND
CE.TempRecorded_DT_TM >= '01-JAN-20'
ORDER BY
TempRecorded_DT_TM
If you want two readings in 12 hours that are greater than 101, then you can use a rolling sum with a window frame:
select t.*,
(case when sum(case when readingvalue > 101 then 1 else 0 end) over (
partition by patientid
order by dt
range between interval '12' hour preceding and current row
) >= 2
then 'Y' else 'N'
end) as temp_flag
from t;

How to flag an id which has repeated more than once in the past

I am trying to flag an employee id with specific conditions which has repeated in the last 6 months. The way I want to do this is create a new column which will flag these employees with the conditions I have specified in the query
Eeid Receiveddt Status Reason Past 6 months?
123 May19 closed OHM 1
234 Jan19 closed xyz 0
123 July19 closed OHM 1
567 Aug19 open ZZZ 0
123 Sept19 open OHM 0
Now as per the above example eeid 123 is flagged under column 'Past 6 months?' because the received date is in the last 6 months and the status was closed (these are my 2 conditions for flagging)
I was thinking of using a case when statement but not sure how to check the repeated 'Eeid'
Is this what you want?
select t.*,
( received_date > current_date - interval '6 month' and status = 'closed' )::int
from t;
I read your requirement somewhat differently. I read it meaning you wanted to update the 'closed' rows having another of the same id also closed within a 6 month period closed within a 6 month period. The following does that, and I tried to structure it to show the step-by-step development (it could likely be reduced somewhat - but that wouldn't show its development). Hope this helps now and gives you technique to build upon.
update t
set last_6mo_flag = 1
where last_6mo_flag = 0
and (id, received_date) in
(select id,received_date
from (select id, received_date, last_6mo
from ( select id, received_date
, ( coalesce((extract (days from (received_date + interval '6 months') - received_date)::integer) > abs(next_date - received_date),false)
or coalesce((extract (days from (received_date + interval '6 months') - received_date)::integer) > abs(prior_date- received_date),false)
) last_6mo
from (select id, received_date, status
, lag(t.received_date) over(partition by t.id order by t.id, t.received_date) next_date
, lead(t.received_date) over(partition by t.id order by t.id, t.received_date) prior_date
from t
where status = 'closed'
) s1
) s2
where last_6mo
) s3
);

Time difference between two date fields

I have a date field names "dts" in string format. I want to find out all the records based on their time differences (in hours). The run event time should be greater than or equal to eat event.
The output should be:
Convert timestamps to seconds, then subtract, divide result by 3600 to get hours, use case+count to count by ranges, something like this:
select count(case when diff_hrs >24 then 1 end) as more_24,
count(case when diff_hrs <=24 then 1 end) as less_than_24,
...
count(case when diff_hrs >=2 and diff_hrs <=3 then 1 end) as hrs_2_to_3,
...
from
(
select
abs(unix_timestamp(dts) - unix_timestamp(dts-eat)))/60/60 as diff_hrs
from table
)s;

How to sum previous month values and current month values in SQL query

Please help me to solve this issue. I have to write a query to get the sum of previous month values and current month values but not able to get the solution.
Here is an image for your reference sum of actual count column values I need the result.
In Oracle you would use the following, could be not completely correct on the case statement:
Select
Site,
sum(value * (case (date_transaction > trunc(sysdate, 'MM') then 1 else 0 end))
volume_current_month
from myTable
where date_transaction between add_months(trunc(sysdate,'MM'),-1) and sysdate
group by site
The previous month is more or less the same using another case statement and ADD_MONTHS.
There are two ways to do this in Tableau :
First Way .
Under Analysis --> Total --> Add All SubTotals .
Second Way :
Create a calc field Total_event as :
WINDOW_SUM(SUM([Event Count]))
Compute the Window_sum as COMPUTE USING EVENT NAME .
If you CAN USE sql AND YOU actually need the previous month and not just all months summed by EventName then you could use LAG. I made the assumption that you are summing by EventName and you only want the current EventName and previous EventNAme
WITH Summed
AS
(
SELECT * ,
LAG(EventCount) OVER (Partition BY EventName ORDER BY trialmonth) as PrevEvent
FROM dbo.Table1
)
SELECT *,
SUM(PrevEvent+EventCount) AS SummedCount
FROM summed
GROUP BY Site
,TrialMonth
,EventName
,EventCount
, PrevEvent
This will produce output like this
Site TrialMonth EventName EventCount PrevEvent SummedCount
12101 2001-10-15 Actual Count 4 NULL NULL
12101 2001-10-15 Projected Count 8 NULL NULL
12101 2001-11-15 Actual Count 6 4 10
12101 2001-11-15 Projected Count 9 8 17
12101 2001-12-15 Actual Count 0 6 6
12101 2001-12-15 Projected Count 10 9 19
http://sqlfiddle.com/#!6/8253f0/2
In tableau -> Create a calculated field ->
// This is for previous month
if [date] >= DATE("01" +"-" + (Month(TODAY())-1) +"-" + YEAR(TODAY()))
and [date] <= DATE("31" +"-" + (Month(TODAY())-1) +"-" + YEAR(TODAY()))
//if the month has 30 or 28 days it will check that there is no 31 and return null
then
sum([Value])
end
//For current month ->
if Month([date]) = Month(TODAY()) then
sum([Value])
end
You will 2 fields returning previous month's sum and current month's sum, You can then use them as you wish.
On sql end you may use
For Previous Month ->
select sum(value) from table_name t where DATE_PART('month',t.date)=DATE_PART('month',TIMESTAMPADD(mm,-1,GETDATE()))
For current Month ->
select sum(value) from table_name t where DATE_PART('month',t.date)=DATE_PART('month',GETDATE()))

MySQL AVG function for recent 15 records by date (order date desc) in every symbol

I am trying to create a statement in SQL (for a table which holds stock symbols and price on specified date) with avg of 5 day price and avg of 15 days price for each symbol.
Table columns:
symbol
open
high
close
date
The average price is calculated from last 5 days and last 15 days. I tried this for getting 1 symbol:
SELECT avg(close),
avg(`trd_qty`)
FROM (SELECT *
FROM cashmarket
WHERE symbol = 'hdil'
ORDER BY `M_day` desc
LIMIT 0,15 ) s
but I couldn't get the desired list for showing avg values for all symbols.
You can either do it with row numbers as suggested by astander, or you can do it with dates.
This solution will also take the last 15 days if you don't have rows for every day while the row number solution takes the last 15 rows. You have to decide which one works better for you.
EDIT: Replaced AVG, use CASE to avoid division by 0 in case no records are found within the period.
SELECT
CASE WHEN SUM(c.is_5) > 0 THEN SUM( c.close * c.is_5 ) / SUM( c.is_5 )
ELSE 0 END AS close_5,
CASE WHEN SUM(c.is_5) > 0 THEN SUM( c.trd_qty * c.is_5 ) / SUM( c.is_5 )
ELSE 0 END AS trd_qty_5,
CASE WHEN SUM(c.is_15) > 0 THEN SUM( c.close * c.is_15 ) / SUM( c.is_15 )
ELSE 0 END AS close_15,
CASE WHEN SUM(c.is_15) > 0 THEN SUM( c.trd_qty * c.is_15 ) / SUM( c.is_15 )
ELSE 0 END AS trd_qty_15
FROM
(
SELECT
cashmarket.*,
IF( TO_DAYS(NOW()) - TO_DAYS(m_day) < 15, 1, 0) AS is_15,
IF( TO_DAYS(NOW()) - TO_DAYS(m_day) < 5, 1, 0) AS is_5
FROM cashmarket
) c
The query returns the averages of close and trd_qty for the last 5 and the last 15 days. Current date is included, so it's actually today plus the last 4 days (replace < by <= to get current day plus 5 days).
Use:
SELECT DISTINCT
t.symbol,
x.avg_5_close,
y.avg_15_close
FROM CASHMARKET t
LEFT JOIN (SELECT cm_5.symbol,
AVG(cm_5.close) 'avg_5_close',
AVG(cm_5.trd_qty) 'avg_5_qty'
FROM CASHMARKET cm_5
WHERE cm_5.m_date BETWEEN DATE_SUB(NOW(), INTERVAL 5 DAY) AND NOW()
GROUP BY cm_5.symbol) x ON x.symbol = t.symbol
LEFT JOIN (SELECT cm_15.symbol,
AVG(cm_15.close) 'avg_15_close',
AVG(cm_15.trd_qty) 'avg_15_qty'
FROM CASHMARKET cm_15
WHERE cm_15.m_date BETWEEN DATE_SUB(NOW(), INTERVAL 15 DAY) AND NOW()
GROUP BY cm_15.symbol) y ON y.symbol = t.symbol
I'm unclear on what trd_qty is, or how it factors into your equation considering it isn't in your list of columns.
If you want to be able to specify a date rather than the current time, replace the NOW() with #your_date, an applicable variable. And you can change the interval values to suit, in case they should really be 7 and 21.
Have a look at How to number rows in MySQL
You can create the row number per item for the date desc.
What you can do is to retrieve the Rows where the rownumber is between 1 and 15 and then apply the group by avg for the selected data you wish.
trdqty is the quantity traded on particular day.
the days are not in order coz the market operates only on weekdays and there are holidays too so date may not be continuous