SQL: combine case and number to date conversion - sql

I want to do a sql statement which calculates in one table the substraction of 2 Date values. If the value is negative I just want to show it as the 0 value.
The number value is the number of seconds a payment is in a current state.
I convert it with a trick to a time value(Date type).
My current code is
SELECT
max(CASE WHEN t1.time_event < SYSDATE and t2.time_event > SYSDATE THEN to_char(to_date(max(round(SYSDATE - t1.time_event) * 24 * 60 * 60)),'ssssss'),'hh24:mi:ss') else to_char(to_date(0)) END) as "current_age"
from tbl_dummyfeed t1 join tbl_dummyfeed t2 on t1.payment_Id = t2.payment_id
where t1.event = 'accepted' and t2.event = 'enriched');

You could use a similar date 'trick', which is to add the fractional-days difference to a nominal date which has the time portion as midnight - you could use a fixed date, or trunc(sysdate), as long as the time ends up as midnight - without having to multiply by 24*60*60. (Your to_date() solution does the same thing implicitly, effectively adding the number of seconds to midnight on the first day of the current month; but this might be a little clearer). But you can also move the case clauses into the where filter
select to_char(date '1970-01-01'
+ nvl(max(sysdate - t1.time_event), 0), 'HH24:MI:SS') as "current_age"
from tbl_dummyfeed t1
join tbl_dummyfeed t2
on t1.trax_id = t2.trax_id
where t1.event = 'accepted'
and t1.time_event < sysdate
and t2.event = 'enriched'
and t2.time_event > sysdate;
You could also use an analytic approach so you only have to hit the table once, with a subquery that pairs up each 'enriched' time with the previous 'accepted' time for that ID, and which you then filter against the current time:
select to_char(date '1970-01-01'
+ nvl(max(sysdate - time_accepted), 0), 'HH24:MI:SS') as "current_age"
from (
select last_value(case when event = 'accepted' then time_event end ignore nulls)
over (partition by trax_id order by time_event) as time_accepted,
case when event = 'enriched' then time_event end as time_enriched
from tbl_dummyfeed
)
where time_accepted < sysdate
and time_enriched > sysdate;

This works, only the conversion to time hh24:mi:ss still needs to happen.
SELECT max(CASE WHEN t1.time_event < SYSDATE and t2.time_event > SYSDATE THEN round((SYSDATE - t1.time_event) * 24 * 60 * 60) else 0 END) as "current_age"
from tbl_dummyfeed t1 join tbl_dummyfeed t2 on t1.payment_id= t2.payment_id
where t1.event = 'accepted' and t2.event = 'enriched';
When I add the conversion to hh24:mm:ss The solution looks like this
SELECT to_char(to_date(max(CASE WHEN t1.time_event < SYSDATE and t2.time_event > SYSDATE THEN round((SYSDATE - t1.time_event) * 24 * 60
* 60) else 0 END),'sssss'),'hh24:mi:ss') as "current_age" from tbl_dummyfeed t1 join tbl_dummyfeed t2 on t1.trax_Id = t2.trax_id where t1.event = 'accepted' and t2.event = 'enriched';
This is the only good solution for my question. Hope this helps people.

Related

PostgreSQL showing different time periods in a single query

I have a query that will return the ratio of issuances from (issuances from specific network with specific time period / total issuances). so the issuances from specific network with a specific time period divided to total issuances from all networks. Right now it returns the ratios of issuances only from last year (year-to-date I mean), I want to include several time periods in it such as one month ago, 2 month ago etc. LEFT JOIN usually works but I couldn't figure it out for this one. How do I do it?
Here is the query:
SELECT IR1.network,
count(*) / ((select count(*) FROM issuances_extended
where status = 'completed' and
issued_at >= date_trunc('year',current_date)) * 1.) as issuance_ratio_ytd
FROM issuances_extended as IR1 WHERE status = 'completed' and
(issued_at >= date_trunc('year',current_date))
GROUP BY
IR1.network
order by IR1.network
I would break your query into CTEs something like this:
with periods (period_name, period_range) as (
values
('YTD', daterange(date_trunc('year', current_date), null)),
('LY', daterange(date_trunc('year', current_date - 'interval 1 year'),
date_trunc('year', current_date))),
('MTD', daterange(date_trunc('month', current_date - 'interval 1 month'),
date_trunc('month', current_date));
-- Add whatever other intervals you want to see
), period_totals as ( -- Get period totals
select p.period_name, p.period_range, count(*) as total_issuances
from periods p
join issuances_extended i
on i.status = 'completed'
and i.issued_at <# p.period_range
)
select p.period_name, p.period_range,
i.network, count(*) as network_issuances,
1.0 * count(*) / p.total_issuances as issuance_ratio
from period_totals p
join issuances_extended i
on i.status = 'completed'
and i.issued_at <# p.period_range
group by p.period_name, p.period_range, i.network, p.total_issuances;
The problem with this is that you get rows instead of columns, but you can use a spreadsheet program or reporting tool to pivot if you need to. This method simplifies the calculations and lets you add whatever period ranges you want by adding more values to the periods CTE.
Something like this? Obviously not tested
SELECT
IR1.network,
count(*)/((select count(*) FROM issuances_extended
where status = 'completed' and
issued_at between mon.t and current_date ) * 1.) as issuance_ratio_ytd
FROM
issuances_extended as IR1 ,
(
SELECT
generate_series('2022-01-01'::date,
'2022-07-01'::date, '1 month') AS t)
AS mon
WHERE
status = 'completed' and
(issued_at between mon.t and current_date)
GROUP BY
IR1.network
ORDER BY
IR1.network
I've managed to join these tables, so I am answering my question for those who would need some help. To add more tables all you have to do is put new queries in LEFT JOIN and acknowledge them in the base query (IR3, IR4, blabla etc.)
SELECT
IR1.network,
count(*) / (
(
select
count(*)
FROM
issuances_extended
where
status = 'completed'
and issued_at >= date_trunc('year', current_date)
) * 1./ 100
) as issuances_ratio_ytd,
max(coalesce(IR2.issuances_ratio_m0, 0)) as issuances_ratio_m0
FROM
issuances_extended as IR1
LEFT JOIN (
SELECT
network,
count(*) / (
(
select
count(*)
FROM
issuances_extended
where
status = 'completed'
and issued_at >= date_trunc('month', current_date)
) * 1./ 100
) as issuances_ratio_m0
FROM
issuances_extended
WHERE
status = 'completed'
and (issued_at >= date_trunc('month', current_date))
GROUP BY
network
) AS IR2 ON IR1.network = IR2.network
WHERE
status = 'completed'
and (issued_at >= date_trunc('year', current_date))
GROUP BY
IR1.network,
IR2.issuances_ratio_m0
order by
IR1.network

Oracle sql: filtering "fake" (repeated) rows that only differ by a tiny amount of time

I have posted a similar question some days ago, but unfortunately I misunderstood our customer's statement of the problem I have to solve (most precisely I forgot about a part of it). That's why this post may look as duplicated, but it is not.
This is my original post: Oracle sql: filtering repeated rows that only differ by a tiny amount of time
So lets go again:
I have an Oracle table with event alarms fired by parquimeters. Alarms have a state of Open/Close and when an alarm is Open (PKN_EVENTSTATUS='Open') and Close (PKN_EVENTSTATUS='Close') between a tiny amount of time -RECEIVEDDATE- (let's say for example Open RECEIVEDDATE = x | Close RECEIVEDDATE = x + 30 seconds and -of course- PKN_EVENTNAME is the same) this both event rows (Open/Close) are considered as a "fake" alarm (a "failure alarm" that was fired by the parquimeter by "mistake") so both should be deleted.
I would need to create an Oracle SQL query that will select all that "fake" alarms so I can delete them. Again, alarms that has a tiny difference in time (RECEIVEDDATE) between "Open" and "Close" states.
I started creating a query that might work, but it's extremely slow, so I can not even test it because it takes too long. I'm quite sure it can be optimized, but cannot find how right now so I hope anyone can help me.
My current very slow query:
select t1.ID, t1.PKN_EVENTNAME, t1.PKN_EVENTSTATUS, t1.RECEIVEDDATE
from PARQUIMETERS_ALARMS t1
where
exists
(select 'x'
from PARQUIMETERS_ALARMS t2
where t1.PKN_EVENTNAME = t2.PKN_EVENTNAME
and ((t1.PKN_EVENTSTATUS = 'Open' and t2.PKN_EVENTSTATUS = 'Close'
and abs(t1.RECEIVEDDATE - t2.RECEIVEDDATE) * 24 * 60 * 60 < 30) -- < 30 sec
or (t1.PKN_EVENTSTATUS = 'Close' and t2.PKN_EVENTSTATUS = 'Open'
and abs(t2.RECEIVEDDATE - t1.RECEIVEDDATE) * 24 * 60 * 60 < 30))) -- < 30 sec
It might be faster to use two exists conditions rather than one:
select t1.id, t1.pkn_eventname, t1.pkn_eventstatus, t1.receiveddate
from parquimeters_alarms t1
where
(
t1.pkn_eventstatus = 'Open'
and exists (
select 1
from parquimeters_alarms t2
where
t2.pkn_eventname = t1.pkn_eventname
and t2.pkn_eventstatus = 'Close'
and t2.receiveddate < t1.receiveddate + 30 / 60 / 60 / 24
)
)
or (
t1.pkn_eventstatus = 'Close'
and exists (
select 1
from parquimeters_alarms t2
where
t2.pkn_eventname = t1.pkn_eventname
and t2.pkn_eventstatus = 'Open'
and t1.receiveddate < t2.receiveddate + 30 / 60 / 60 / 24
)
)
This query might take advantage of an index on (pkn_eventname, pkn_eventstatus, receiveddate).
You could also consider union all, which avoids the need for an or condition:
select t1.id, t1.pkn_eventname, t1.pkn_eventstatus, t1.receiveddate
from parquimeters_alarms t1
where
t1.pkn_eventstatus = 'Open'
and exists (
select 1
from parquimeters_alarms t2
where
t2.pkn_eventname = t1.pkn_eventname
and t2.pkn_eventstatus = 'Close'
and t2.receiveddate < t1.receiveddate + 30 / 60 / 60 / 24
)
union all
select t1.id, t1.pkn_eventname, t1.pkn_eventstatus, t1.receiveddate
from parquimeters_alarms t1
where
t1.pkn_eventstatus = 'Close'
and exists (
select 1
from parquimeters_alarms t2
where
t2.pkn_eventname = t1.pkn_eventname
and t2.pkn_eventstatus = 'Open'
and t1.receiveddate < t2.receiveddate + 30 / 60 / 60 / 24
)
You can use pivot() to make it easier - it requires just 1 scan without extra request to the same table:
select
level,
'EVENT'||trunc(dbms_random.value(1,5)),
case when dbms_random.value>0.5 then 'CLOSE' else 'OPEN' end case,
date'2020-01-01' + numtodsinterval( trunc(level*dbms_random.value(1,60)) ,'second')
from dual
connect by level<=50
)
,v_pivot as (
select
ID
, PKN_EVENTNAME
, OPEN
, CLOSE
, lag(open)over(partition by PKN_EVENTNAME order by coalesce(open,close)) last_open
from (
select *
from PARQUIMETERS_ALARMS v
pivot (
max(RECEIVEDDATE)
for (PKN_EVENTSTATUS) in ('OPEN' as open,'CLOSE' as close)
)
)
)
select
*
from v_pivot
where close is null or
abs(close - last_open) * 24 * 60 * 60 > 30;
Full example on randomly generated data (since it's generated, it includes unclosed and unopened events):
with PARQUIMETERS_ALARMS(ID, PKN_EVENTNAME, PKN_EVENTSTATUS, RECEIVEDDATE) as (
select
level,
'EVENT'||trunc(dbms_random.value(1,5)),
case when dbms_random.value>0.5 then 'CLOSE' else 'OPEN' end case,
date'2020-01-01' + numtodsinterval( trunc(level*dbms_random.value(1,60)) ,'second')
from dual
connect by level<=50
)
,v_pivot as (
select
ID
, PKN_EVENTNAME
, OPEN
, CLOSE
, lag(open)over(partition by PKN_EVENTNAME order by coalesce(open,close)) last_open
from (
select *
from PARQUIMETERS_ALARMS v
pivot (
max(RECEIVEDDATE)
for (PKN_EVENTSTATUS) in ('OPEN' as open,'CLOSE' as close)
)
)
)
select
*
from v_pivot
where close is null or
abs(close - last_open) * 24 * 60 * 60 > 30
/

Divide results from two query by another query in SQL

I have this query in Metabase:
with l1 as (SELECT date_trunc ('day', Ticket_Escalated_At) as time_scale, count (Ticket_ID) as chat_per_day
FROM CHAT_TICKETS where SUPPORT_QUEUE = 'transfer_investigations'
and date_trunc('month', TICKET_ESCALATED_AT) > now() - interval '6' Month
GROUP by 1)
with l2 as (SELECT date_trunc('day', created_date) as week, count(*) as TI_watchman_ticket
FROM jira_issues
WHERE issue_type NOT IN ('Transfer - General', 'TI - Advanced')
and date_trunc('month', created_date) > now() - interval '6' Month
and project_key = 'TI2'
GROUP BY 1)
SELECT l1.* from l1
UNION SELECT l2.* from l2
ORDER by 1
and this one:
with hours as (SELECT date_trunc('day', ws.start_time) as date_
,(ifnull(sum((case when ws.shift_position = 'TI - Non-watchman' then (minutes_between(ws.end_time, ws.start_time)/60) end)),0) + ifnull(sum((case when ws.shift_position = 'TI - Watchman' then (minutes_between(ws.end_time, ws.start_time)/60) end)),0) ) as total
from chat_agents a
join wiw_shifts ws on a.email = ws.user_email
left join people_ops.employees h on substr(h.email,1, instr(h.email,'#revolut') - 1) = a.login
where (seniority != 'Lead' or seniority is null)
and date_trunc('month', ws.start_time) > now() - interval '6' Month
GROUP BY 1)
I would like to divide the output of the UNION of the first one, by the result of the second one, any ideas.

How to make Select Statement Faster in Oracle

I wrote query to find sum of money in a given period with one filial and it is working fast:
SELECT FILIAL_CODE,
sum(sum_eqv)/100 AS summa
FROM table
WHERE substr(acc,1,5) = '65434'
and cast(substr(acc,18,3) as integer) >= 600
and cast(substr(account_co,18,3) as integer)<=607
AND o_day >= to_date('01.12.2019', 'DD.MM.YYYY')
and oday < to_date('08.12.2019', 'DD.MM.YYYY')+ INTERVAL '1' DAY
AND FILIAL_CODE = '001234'
Above query is working fine. But when I want to use it multiple filials it is becoming more complex.
Below query is needs to be fixed.
SELECT FILIAL_CODE,
sum(sum_eqv)/100 AS summa
FROM table
WHERE substr(acc,1,5) = '65434'
and cast(substr(acc,18,3) as integer) >= 600
and cast(substr(account_co,18,3) as integer)<=607
AND o_day >= to_date('01.12.2019', 'DD.MM.YYYY')
and oday < to_date('08.12.2019', 'DD.MM.YYYY')+ INTERVAL '1' DAY
AND FILIAL_CODE in (select code from city where region = '26')
group by FILIAL_CODE;
This query runs long. How can I maximize this statement. Any Help is appreciated!
Try JOIN instead of IN, such as:
SELECT t.filial_code, SUM (sum_eqv) / 100 AS summa
FROM your_table t JOIN city c ON c.code = t.filial_code
WHERE SUBSTR (t.acc, 1, 5) = '65434'
AND CAST (SUBSTR (t.acc, 18, 3) AS INTEGER) >= 600
AND CAST (SUBSTR (t.account_co, 18, 3) AS INTEGER) <= 607
AND t.o_day >= TO_DATE ('01.12.2019', 'DD.MM.YYYY')
AND t.oday < TO_DATE ('08.12.2019', 'DD.MM.YYYY') + INTERVAL '1' DAY
AND c.region = '26'
GROUP BY t.filial_code;
It would probably help if city.code and table.filial_code were indexed.

SQL Where Clause Maximo IBM

I want to create a where clause in Maximo.
We have a clause today where we can see the work order about to breach responded SLA.
But i want the clause to calculate to show work order about to breach responded SLA within 1 hour.
Is that possible?
Clause is as follows:
(woclass = 'WORKORDER' or woclass = 'ACTIVITY')
and historyflag = 0
and istask = 0
and workorderid in (select sla.ownerid from slarecords sla where sla.ownertable = 'WORKORDER' and sla.ownerid = workorderid)
and wonum not in (select wos.wonum from wostatus wos where wos.status = 'RESPONDED' and wos.siteid = siteid and wos.wonum = wonum)
and sysdate < targstartdate
and (((targstartdate - reportdate) - (targstartdate - sysdate))/(targstartdate - reportdate)*100)>90
and (siteid in (select defsite from maxuser mu where mu.sdx_siteteam is null and mu.userid = 'ALEXANDER.JEPPSON#SODEXO.COM' )
or (siteid in (select sis.siteid from sdx_integratedsites sis where sis.sdx_property = 'COMMANDCENTER' and sis.sdx_type = (select sdx_siteteam from maxuser mu where mu.userid = 'ALEXANDER.JEPPSON#SODEXO.COM' ))))
It sounds like you should change
and sysdate < targstartdate
to subtract an hour from the target
and sysdate < targstartdate - 1/24
or add an hour to the current time
and sysdate + 1/24 < targstartdate