Query to get the data at particular time - oracle

I need to write a query for attendance, which gives the attendance for current time and previous 10 minutes. In total 20 records.
I have the below query. it gives me the correct data for current time. But the previous attendance goes on reducing.
For example : at 12:30 PM the attendance is 2000. But when the time reaches 1:30 PM the attendnce corresponding to 12:30 shows very less.
SELECT TO_CHAR (each_sec, 'HH24:MI') p_time, COUNT (each_sec) cnt
FROM (SELECT *
FROM (SELECT d.userid, d.logdate
FROM atendance_data d
JOIN
(SELECT userid, MAX (logdate) logdate
FROM atendance_data
WHERE TRUNC (logdate) = TRUNC (SYSDATE)
GROUP BY userid) m
ON d.userid = m.userid AND d.logdate = m.logdate
WHERE d.c1 NOT IN ('Check-Out', 'Break-Out')) c
JOIN
(SELECT SYSDATE + (1 - LEVEL) / 24 / 3600 * 600 each_sec
FROM DUAL
CONNECT BY LEVEL <= 20) s ON c.logdate <= s.each_sec
)
GROUP BY each_sec
ORDER BY each_sec
I have to write a where condition to take maximum of logdate, saying less than each_sec. I guess that is where i am going wrong. But I dont know how to do it.
Can anyone help me doing this?
Thanks in advance.

I have got it done like this. Thank you all for your responses
SELECT COUNT (*), each_sec
FROM (SELECT a.userid, a.logdate, a.c1, each_sec
FROM atendance_data a,
(SELECT userid, MAX (logdate) logdate, each_sec
FROM (SELECT userid, logdate, c1, each_sec
FROM (SELECT a.USERID,A.LOGDATE,A.C1, b.each_sec
FROM atendance_data a
JOIN
(SELECT SYSDATE
+ (1 - LEVEL)
/ 24
/ 3600
* 600 each_sec
FROM DUAL
CONNECT BY LEVEL <= 20) b
ON a.logdate <= b.each_sec
AND trunc(a.logdate) =trunc(sysdate)
))
GROUP BY userid, each_sec) b
WHERE a.userid = b.userid
AND a.logdate = b.logdate
AND Trunc (a.logdate) = trunc(sysdate))
WHERE c1 NOT IN ('Check-Out', 'Break-Out')
GROUP BY each_sec
ORDER BY each_sec;

Related

How to avoid Internal_Function by TO_CHAR IN Oracle

I'm using Oracle 12C and I have the following code:
SELECT *
FROM
(
SELECT
*
FROM
t.tablea
WHERE
name = 'FIS'
) A
LEFT JOIN (
SELECT
*
FROM
t.tableb
WHERE
enabled = 1
) B ON b.id = a.id
AND TO_CHAR(b.createdate, 'dd-mm-yy hh24:mi') = TO_CHAR(a.createdate, 'dd-mm-yy hh24:mi')
Both a and b createdate are timestamp datatype.
Optimizer return an internal_function at TO_CHAR(b.createdate, 'dd-mm-yy hh24:mi') = TO_CHAR(a.createdate, 'dd-mm-yy hh24:mi') in Execution Plan
If I compare like this: 'AND b.createdate = a.createdate', It will lost 1000 rows that look like this '11-JUN-18 04.48.34.269928000 PM'. And If I change 269928000 to 269000000 It will work
Now, I don't want to using to_char to avoid internal_function(must create Function-based-Index)
Anyone can help me?
If I compare like this: AND b.createdate = a.createdate, It will lost 1000 rows that look like this 11-JUN-18 04.48.34.269928000 PM. And If I change 269928000 to 269000000 It will work
Your values appear to have a fractional seconds component and would have the TIMESTAMP data type. If so, you can use TRUNC( timestamp_value, 'MI' ) to truncate to the nearest minute.
SELECT *
FROM t.tablea a
LEFT OUTER JOIN t.tableb b
ON ( a.createdate >= TRUNC( b.createdate, 'MI' )
AND a.createdate < TRUNC( b.createdate, 'MI' ) + INTERVAL '1' MINUTE
AND a.id = b.id
AND b.enabled = 1
)
WHERE a.name = 'FIS'
This will remove the need to apply a function to one of the two tables (a.createdate in this case but you could swap them).
I don't even see the need for the subqueries:
SELECT a.*, b.*
FROM t.tablea a
LEFT JOIN t.tableb b
ON a.id = b.id AND
TRUNC(b.createdate, 'MI') = TRUNC(a.createdate, 'MI') AND
b.enabled = 1
WHERE
a.name = 'FIS'

get data in 'n' hours interval between two dates oracle

i am trying to get data between two dates in an 'n' hour intervals ... the problem is that i am not getting desired result after 8 hour intervals ... my value in 'n' interval can range up to any number between 1 to 120.
Following is the Pseudo code of what i am tying to do:
-- i first select number of hours between two dates
SELECT
24 * (SYSDATE - to_date('2018-04-16 15:20', 'YYYY-MM-DD hh24:mi')) AS diff_hours
FROM dual;
-- Then i use the above value in CONNECT BY ROWNUM <= ROUND((hours between two dates/n),0) to get data in n intervals
SELECT TRUNC(sysdate - (rownum/ROUND((24/n),0)),'HH24') as the_hour
FROM dual
CONNECT BY ROWNUM <= ROUND((hours between two dates/n),0) ;
Sample query
SELECT
24 * (SYSDATE - to_date('2018-04-16 15:20', 'YYYY-MM-DD hh24:mi')) AS diff_hours
FROM dual;
SELECT TRUNC(sysdate - (rownum/ROUND((24/8),0)),'HH24') as the_hour
FROM dual
CONNECT BY ROWNUM <= ROUND((724/8),0) ;
How can i change above query to get data in 'n' hour intervals between two dates, with n being any number of hours?
Remove the first ROUND() in your SELECT
SELECT TRUNC (SYSDATE - (ROWNUM / (24 / 9)), 'HH24') AS the_hour
FROM DUAL
CONNECT BY ROWNUM <= ROUND ( (724 / 9), 0);
try this,
SELECT TO_DATE(:p_date1, 'MM/DD/YYYY') + (FLOOR((rownum*:p_interval)/24) + (MOD((rownum*:p_interval), 24)/24)) dt
FROM DUAL
CONNECT BY (rownum*:p_interval) <= ((TO_DATE(:p_date2, 'MM/DD/YYYY')+.5) - TO_DATE(:p_date1, 'MM/DD/YYYY') + (MOD((rownum*:p_interval), 24)/24)) * 24
ORDER BY 1;

Ora-00932 - expected NUMBER got -

I have been running the below query without issue:
with Nums (NN) as
(
select 0 as NN
from dual
union all
select NN+1 -- (1)
from Nums
where NN < 30
)
select null as errormsg, trunc(sysdate)-NN as the_date, count(id) as the_count
from Nums
left join
(
SELECT c1.id, trunc(c1.c_date) as c_date
FROM table1 c1
where c1.c_date > trunc(sysdate) - 30
UNION
SELECT c2.id, trunc(c2.c_date)
FROM table2 c2
where c2.c_date > trunc(sysdate) -30
) x1
on x1.c_date = trunc(sysdate)-Nums.NN
group by trunc(sysdate)-Nums.NN
However, when I try to pop this in a proc for SSRS use:
procedure pr_do_the_thing (RefCur out sys_refcursor)
is
oops varchar2(100);
begin
open RefCur for
-- see above query --
;
end pr_do_the_thing;
I get
Error(): PL/SQL: ORA-00932: inconsistent datatypes: expected NUMBER got -
Any thoughts? Like I said above, as a query, there is no issue. As a proc, the error appears at note (1) int eh query.
This seems to be bug 18139621 (see MOS Doc ID 2003626.1). There is a patch available, but if this is the only place you encounter this, it might be simpler to switch to a hierarchical query:
with Nums (NN) as
(
select level - 1
from dual
connect by level <= 31
)
...
You could also calculate the dates inside the CTE (which also fails with a recursive CTE):
with Dates (DD) as
(
select trunc(sysdate) - level + 1
from dual
connect by level <= 31
)
select null as errormsg, DD as the_date, count(id) as the_count
from Dates
left join
(
SELECT c1.id, trunc(c1.c_date) as c_date
FROM table1 c1
where c1.c_date > trunc(sysdate) - 30
UNION
SELECT c2.id, trunc(c2.c_date)
FROM table2 c2
where c2.c_date > trunc(sysdate) -30
) x1
on x1.c_date = DD
group by DD;
I'd probably organise it slightly differently, so the subquery doesn't limit the date range directly:
with dates (dd) as
(
select trunc(sysdate) - level + 1
from dual
connect by level <= 31
)
select errormsg, the_date, count(id) as the_count
from (
select null as errormsg, d.dd as the_date, c1.id
from dates d
left join table1 c1 on c1.c_date >= d.dd and c1.c_date < d.dd + 1
union all
select null as errormsg, d.dd as the_date, c2.id
from dates d
left join table2 c2 on c2.c_date >= d.dd and c2.c_date < d.dd + 1
)
group by errormsg, the_date;
but as always with these things, check the performance of each approach...
Also notice that I've switched from union to union all. If an ID could appear more than once on the same day, in the same table or across both tables, then the counts will be different - you need to decide whether you want to count them once or as many times as they appear. That applies to your original query too.

oracle olap query execution is taking too long

I have these following tables:
1) date_table_dim
2) clock_table_dim
3) onlinegpspoint : which contains our main information for olap reports
And also there is a sql query like this:
SELECT
date_table_dim.day_id day_id,
clock_table_dim.hour_id hour_id
FROM onlinegpspoint olgps
INNER JOIN date_table_dim
ON (
olgps.occurance_time >= to_date('2014-03-01 00:00:00', 'yyyy-mm-dd hh24:mi:ss')
AND olgps.occurance_time >= date_table_dim.day_id
AND olgps.occurance_time < date_table_dim.day_id + 1
)
INNER JOIN clock_table_dim
ON ( clock_table_dim.hour_id <= TO_NUMBER(TO_CHAR(occurance_time, 'HH24'))
AND clock_table_dim.hour_id > TO_NUMBER(TO_CHAR((occurance_time - 1/24), 'HH24') ))
GROUP BY
date_table_dim.day_id,
clock_table_dim.hour_id ;
My problem is that this query is taking too long to execute.
What actions can be taken to improve the performance of this query execution?
EDIT
There is an index for occurance_time on onlinegpspoint. By this query I want to get some Olap information for 1 hour period of time. (This query is a kind of summary of my fact_table query.)
You can try following query.
SQLFiddle
with t as (select d.day_id, o.occurance_time ot
from onlinegpspoint o
join date_table_dim d on ( o.occurance_time >= date '2014-03-01'
and d.day_id <= o.occurance_time and o.occurance_time < d.day_id + 1) )
select day_id, c.hour_id
from t join clock_table_dim c on (
c.hour_id <= to_char(t.ot, 'HH24') and to_char((t.ot - 1/24), 'HH24') < c.hour_id )
group by day_id, c.hour_id order by day_id, c.hour_id;
In your original query you compare to_char(occurance_time, 'HH24') with hour_id, here index probably does not work.
So idea is to firstly filter data to interesting period and then work only with these filtered data.
There is one more query worth trying, which gave me promising results:
select distinct trunc(occurance_time) day_id, to_char(occurance_time, 'hh24')+0 hour_id
from onlinegpspoint o join (
select to_date(to_char(day_id, 'yyyy-mm-dd ')||' '
||lpad(hour_id, 2, 0), 'yyyy-mm-dd hh24') dt
from date_table_dim, clock_table_dim) d
on (o.occurance_time >= date '2014-03-01'
and d.dt-1/24 <= o.occurance_time and o.occurance_time < d.dt)

Finding a count of rows in an arbitrary date range using Oracle

The question I need to answer is this "What is the maximum number of page requests we have ever received in a 60 minute period?"
I have a table that looks similar to this:
date_page_requested date;
page varchar(80);
I'm looking for the MAX count of rows in any 60 minute timeslice.
I thought analytic functions might get me there but so far I'm drawing a blank.
I would love a pointer in the right direction.
You have some options in the answer that will work, here is one that uses Oracle's "Windowing Functions with Logical Offset" feature instead of joins or correlated subqueries.
First the test table:
Wrote file afiedt.buf
1 create table t pctfree 0 nologging as
2 select date '2011-09-15' + level / (24 * 4) as date_page_requested
3 from dual
4* connect by level <= (24 * 4)
SQL> /
Table created.
SQL> insert into t values (to_date('2011-09-15 11:11:11', 'YYYY-MM-DD HH24:Mi:SS'));
1 row created.
SQL> commit;
Commit complete.
T now contains a row every quarter hour for a day with one additional row at 11:11:11 AM. The query preceeds in three steps. Step 1 is to, for every row, get the number of rows that come within the next hour after the time of the row:
1 with x as (select date_page_requested
2 , count(*) over (order by date_page_requested
3 range between current row
4 and interval '1' hour following) as hour_count
5 from t)
Then assign the ordering by hour_count:
6 , y as (select date_page_requested
7 , hour_count
8 , row_number() over (order by hour_count desc, date_page_requested asc) as rn
9 from x)
And finally select the earliest row that has the greatest number of following rows.
10 select to_char(date_page_requested, 'YYYY-MM-DD HH24:Mi:SS')
11 , hour_count
12 from y
13* where rn = 1
If multiple 60 minute windows tie in hour count, the above will only give you the first window.
This should give you what you need, the first row returned should have
the hour with the highest number of pages.
select number_of_pages
,hour_requested
from (select to_char(date_page_requested,'dd/mm/yyyy hh') hour_requested
,count(*) number_of_pages
from pages
group by to_char(date_page_requested,'dd/mm/yyyy hh')) p
order by number_of_pages
How about something like this?
SELECT TOP 1
ranges.date_start,
COUNT(data.page) AS Tally
FROM (SELECT DISTINCT
date_page_requested AS date_start,
DATEADD(HOUR,1,date_page_requested) AS date_end
FROM #Table) ranges
JOIN #Table data
ON data.date_page_requested >= ranges.date_start
AND data.date_page_requested < ranges.date_end
GROUP BY ranges.date_start
ORDER BY Tally DESC
For PostgreSQL, I'd first probably write something like this for a "window" aligned on the minute. You don't need OLAP windowing functions for this.
select w.ts,
date_trunc('minute', w.ts) as hour_start,
date_trunc('minute', w.ts) + interval '1' hour as hour_end,
(select count(*)
from weblog
where ts between date_trunc('minute', w.ts) and
(date_trunc('minute', w.ts) + interval '1' hour) ) as num_pages
from weblog w
group by ts, hour_start, hour_end
order by num_pages desc
Oracle also has a trunc() function, but I'm not sure of the format. I'll either look it up in a minute, or leave to see a friend's burlesque show.
WITH ranges AS
( SELECT
date_page_requested AS StartDate,
date_page_requested + (1/24) AS EndDate,
ROWNUMBER() OVER(ORDER BY date_page_requested) AS RowNo
FROM
#Table
)
SELECT
a.StartDate AS StartDate,
MAX(b.RowNo) - a.RowNo + 1 AS Tally
FROM
ranges a
JOIN
ranges b
ON a.StartDate <= b.StartDate
AND b.StartDate < a.EndDate
GROUP BY a.StartDate
, a.RowNo
ORDER BY Tally DESC
or:
WITH ranges AS
( SELECT
date_page_requested AS StartDate,
date_page_requested + (1/24) AS EndDate,
ROWNUMBER() OVER(ORDER BY date_page_requested) AS RowNo
FROM
#Table
)
SELECT
a.StartDate AS StartDate,
( SELECT MIN(b.RowNo) - a.RowNo
FROM ranges b
WHERE b.StartDate > a.EndDate
) AS Tally
FROM
ranges a
ORDER BY Tally DESC

Resources