Oracle query, get count of records by hour - sql

I am trying to get transaction counts for every hour. Normally it is a straight forward query by unfortunately the timestamp column I have to work with is not timestamp but varchar2! No matter what I try I get either "not a valid month" or "invalid number", depending on the format I use.
The timestamp looks like: 2021-08-08 00:00:52:632
I also executed the following to get NLS format:
SELECT * FROM nls_session_parameters WHERE parameter = 'NLS_DATE_FORMAT';
and get
DD-MON-RRRR.
This is the latest I tried among a dozen others (I commented out the "group by" to just get the darn thing to display).
select to_char(reqts,'mm/dd/yyyy hh24') DATE_HR
--, count(*)
from idcreqresplog
where logdate > trunc(SYSDATE -2)
and logtypeid in (2,4)
--group by to_char(reqts,'mm/dd/yyyy hh24');
Also
select to_char(reqts, 'yyyy-mm-dd hh24:mi:ss.fff' )
--, count(*)
FROM
reqresplog
WHERE
logdate > trunc(SYSDATE -2) ;
--group by to_date(reqts, 'yyyy-mm-dd HH4');
At my wits end and need some help.

Assuming that your column is always in the format 2021-08-08 00:00:52:63 then group on the substring up to the 13th character:
SELECT SUBSTR(reqts, 1, 13) AS date_hr,
count(*)
FROM idcreqresplog
WHERE logdate > trunc(SYSDATE -2)
AND logtypeid in (2,4)
GROUP BY
SUBSTR(reqts, 1, 13);
If you do want to convert to a date then, from Oracle 12.2, you can use TO_TIMESTAMP(string_value DEFAULT NULL ON CONVERSION ERROR, 'YYYY-MM-DD HH24:MI:SS:FF'):
SELECT TRUNC(
TO_TIMESTAMP(
reqts DEFAULT NULL ON CONVERSION ERROR,
'YYYY-MM-DD HH24:MI:SS:FF'
),
'HH'
) AS date_hr,
COUNT(*)
FROM idcreqresplog
WHERE logdate > trunc(SYSDATE -2)
AND logtypeid in (2,4)
GROUP BY
TRUNC(
TO_TIMESTAMP(
reqts DEFAULT NULL ON CONVERSION ERROR,
'YYYY-MM-DD HH24:MI:SS:FF'
),
'HH'
)
db<>fiddle here

Assuming as LittleFoot suggested, that some of your data is bad, you can use an inline WITH function to root out your bad data. Take the following example:
WITH FUNCTION get_timestamp
(
p_sTimeString VARCHAR2
)
RETURN TIMESTAMP
IS
BEGIN
RETURN TO_TIMESTAMP(p_sTimeString, 'YYYY-MM-DD HH24:MI:SS.FF3');
EXCEPTION WHEN OTHERS THEN RETURN NULL;
END;
SELECT TO_CHAR(s.hour, 'YYYY-MM-DD HH24') AS HOUR, COUNT(*) AS ROW_COUNT
FROM (SELECT TRUNC(get_timestamp(td.time), 'HH24') AS HOUR,
td.amount
FROM test_data td) s
WHERE s.hour IS NOT NULL
GROUP BY s.hour
ORDER BY s.hour;
Here is the DBFiddle showing this working for some good and bad data (Link).
What the query does is use an inline function to call the TO_TIMESTAMP function. Then it just catches any error and returns NULL. This saves you from your bad data messing up your query. After that, the query is pretty much as you had tried earlier. I truncate the timestamp to the hour in the inner query and then use that to group by in the outer query (Only using the rows which don't have NULL timestamps...meaning they didn't error)

Related

Oracle substract previous row

I've got this query:
SELECT user_id, from_loc_id, to_loc_id, to_char(dstamp, 'hh24:mi:ss')
FROM inventory_transaction
WHERE code = 'Pick'
AND substr(work_group,1,6) = 'BRANCH'
AND dstamp BETWEEN to_date('24/02/2022 17:00:00', 'dd/mm/yyyy hh24:mi:ss') AND
to_date('24/02/2022 18:00:00', 'dd/mm/yyyy hh24:mi:ss')
ORDER BY user_id;
That's the output:
My expected output is:
I was trying to use lag, but didn't really worked.
I've just realized I need to add a second ORDER BY, so first by user, second by to_char(dstamp, 'hh24:mi:ss').
All solutions much appreciate. Thank you.
You can use NUMTODSINTERVAL function with day argument and applying SUBSTR to extract hours:minutes:seconds portion as your data resides within a specific date such as
SELECT t.user_id,
t.dstamp,
SUBSTR(
NUMTODSINTERVAL(dstamp - LAG(dstamp)
OVER (PARTITION BY user_id ORDER BY dstamp),'day'),
12,8) AS time_diff
FROM t
Demo
Edit : The case above is applied for the column dstamp is considered to be of date data type, if its data type is timestamp, then use the following query containing date cast instead
SELECT t.user_id,
t.dstamp,
SUBSTR(
NUMTODSINTERVAL(CAST(dstamp AS date) - LAG(CAST(dstamp AS date))
OVER (PARTITION BY user_id ORDER BY CAST(dstamp AS date)),'day'),
12,8) AS time_diff
FROM t
Demo

Problem with date matching between a variable and a date column Oracle SQL

The problem is that this query is working fine:
CREATE TABLE PROCESGEN_TEST
(PROCESENDTIME TIMESTAMP);
INSERT INTO PROCESGEN_TEST
(SELECT DISTINCT PROCESENDTIME FROM dwh_procesgeneriek#xob10
WHERE PROCESENDTIME IS NOT NULL
AND PROCESENDTIME > '10-09-2020 01:00:00');
Def TIME2 = (SELECT MAX_EXEC_TIME FROM EXEC_TIME);
SELECT PROCESENDTIME
FROM PROCESGEN_TEST
WHERE PROCESENDTIME < &TIME2
AND PROCESEINDTIJD IS NOT NULL
In the above situation we first put the data into a table created in de database management system we use (named xor01 and not the xob10). In the query beneath we extract the data directly from xob10. This isn’t working when we want to select a date (greater or lower then..) and we don’t know why.
CREATE TABLE EXEC_TIME
(MAX_EXEC_TIME DATE);
INSERT INTO EXEC_TIME
(
SELECT TO_DATE(
TO_CHAR(
MAX(EXEC_DATE),
'DD-MM-YYYY HH24:MI:SS'
),
'DD-MM-YYYY HH24:MI:SS'
) - 1.1666
from L3DD_MIN_ACTIVITIES_BRD_BAK_Will
);
Def TIME3 = (SELECT MAX_EXEC_TIME FROM EXEC_TIME);
SELECT PROCESENDTIME
FROM dwh_procesgeneriek#xob10
WHERE PROCESENDTIME IS NOT NULL
AND TO_DATE(PROCESENDTIME,'DD-MM-YY HH24:MI:SS')
> TO_DATE(&TIME3, 'DD-MM-YY HH24:MI:SS');
The problem is that the query is not finding a single date in the last query and keeps on executing. If we replace TO_DATE(&TIME3, 'DD-MM-YY HH24:MI:SS') with a certain date like '10-08-2020 20:00:00' the query will find the right dates. We have tried all kinds of things, like working with TIMESTAMP format and TO_TIMESTAMP. Nothing works. It looks like a rather simple problem.
Does anyone know what’s causing the problem the query can’t find any dates in the second query?
You don't need:
the EXEC_TIME table;
to convert a timestamp to a string and then back to a date;
to use a variable; or
to filter on PROCESENDTIME IS NOT NULL (since the > filter only works on non-NULL values).
Then you can use:
SELECT PROCESENDTIME
FROM dwh_procesgeneriek#xob10
WHERE PROCESENDTIME
> (
SELECT MAX(EXEC_DATE) - INTERVAL '1 4' DAY TO HOUR
FROM L3DD_MIN_ACTIVITIES_BRD_BAK_Will
);
If you do want the EXEC_TIME table then:
CREATE TABLE EXEC_TIME( MAX_EXEC_TIME DATE );
INSERT INTO EXEC_TIME
SELECT MAX(EXEC_DATE) - INTERVAL '1 4' DAY TO HOUR
FROM L3DD_MIN_ACTIVITIES_BRD_BAK_Will;
SELECT PROCESENDTIME
FROM dwh_procesgeneriek#xob10
WHERE PROCESENDTIME > ( SELECT MAX_EXEC_TIME FROM EXEC_TIME );

How to search by to_date function?

I have table as below:
Table Temp:
ID MAX MIN DATE_C
1 34 24 21-APR-17 02.41.38.520000 PM
2 32 26 20-APR-17 02.42.44.569000 PM
I execute the below SQL query to get temperature details on respective date:
SELECT *
FROM Temp t
WHERE t.date_c = TO_DATE( '2017-04-21', 'YYYY-MM-DD')
order by t.id
But it's returning empty records. Whats wrong with my query?
You need to remove the time component on the column. Here is one way:
SELECT *
FROM Temp t
WHERE TRUNC(t.date_c) = DATE '2017-04-21'
ORDER BY t.id;
However, I usually recommend using inequalities, rather than a function on the column:
SELECT *
FROM Temp t
WHERE t.date_c >= DATE '2017-04-21' AND
t.date_c < DATE '2017-04-22'
ORDER BY t.id;
This allows the query to use an index on date_c. I should add that the original version can use an index on (trunc(date_c, id).
21-APR-17 02.41.38.520000 PM is not a DATE; it has a fractional seconds component so it is a TIMESTAMP.
So, if you want to find items that are on a particular day (inputting the TIMESTAMP using an ISO/ANSI timestamp literal):
SELECT *
FROM Temp
WHERE date_c >= TIMESTAMP '2017-04-21 00:00:00' AND
date_c < TIMESTAMP '2017-04-21 00:00:00' + INTERVAL '1' DAY;
or
SELECT *
FROM Temp
WHERE date_c >= TO_TIMESTAMP( :your_date_string, 'YYYY-MM-DD' ) AND
date_c < TO_TIMESTAMP( :your_date_string, 'YYYY-MM-DD' ) + INTERVAL '1' DAY;
it's returning empty records. Whats wrong with my query?
date_c = TO_DATE( '2017-04-21', 'YYYY-MM-DD') matches all rows where the date_c value is exactly 2017-04-21 00:00:00.000000 (including the time component); if you do not have any rows with exactly that date and time then, as you noticed, it will return nothing. If you want to get records matching that day then you need to get values within a range of times between the start and end of the day.
You need to pass date on the column. Here is a way...
SELECT *
FROM Temp t
WHERE CAST(t.CREATED_ON as date)= N'2017-04-22'
ORDER BY t.id

Oracle query time on date fields

I have a column of type DATE stored data are contains date and time . I can see value when i do
select CAST(MSG_DT AS TIMESTAMP) from table;
this is the output
17-MAR-08 15:38:59,000000000
I have to select the row using
Only date
select CAST(MSG_DT AS TIMESTAMP) from
MWRB_RECEIVE where
MSG_DT >= TO_DATE( '2000-02-03' ,'YYYY-MM-DD')
and
MSG_DT <= TO_DATE( '2010-02-03' ,'YYYY-MM-DD')
Only time (eg: every message between 12:00:11 and 23:02:55)
In DB2 i can do
SELECT *
FROM TABLE
WHERE DATE(INS_TMS) = '2014-02-18'
SELECT *
FROM TABLE
WHERE TIME(INS_TMS) > '09.55.00'
In ORACLE I can't see the equivalent.
Try this:
SELECT *
FROM your_table
WHERE TO_CHAR (start_date, 'yyyy-mm-dd') = '2014-10-06'
AND TO_CHAR (start_date, 'hh24:mi:ss') > '10:00:00'
Why are you casting the column value to a TIMESTAMP when the column in the database is a DATE type? The fractional part of the seconds will always be 0, as DATE only has resolution to the seconds value. You need to add the hours,minutes, and seconds format specifier to the query:
select MSG_DT from
MWRB_RECEIVE
where MSG_DT between TO_DATE( '2000-02-03 12:00:11' ,'YYYY-MM-DD HH24:MI:SS') AND
TO_DATE( '2010-02-03 23:02:55' ,'YYYY-MM-DD HH24:MI:SS')
There is no need to split date and hour, you can have it in a single where clause
where field > to_date('20121212 12:12:12, 'YYYYMMDD HH24:MI:SS')
Check for your reference oracle to_date() as it seems the only thing you need

Oracle date function for the previous month

I have the query below where the date is hard-coded. My objective is to remove the harcoded date; the query should pull the data for the previous month when it runs.
select count(distinct switch_id)
from xx_new.xx_cti_call_details#appsread.prd.com
where dealer_name = 'XXXX'
and TRUNC(CREATION_DATE) BETWEEN '01-AUG-2012' AND '31-AUG-2012'
Should I use sysdate-15 function for that?
Modifying Ben's query little bit,
select count(distinct switch_id)
from xx_new.xx_cti_call_details#appsread.prd.com
where dealer_name = 'XXXX'
and creation_date between add_months(trunc(sysdate,'mm'),-1) and last_day(add_months(trunc(sysdate,'mm'),-1))
The trunc() function truncates a date to the specified time period; so trunc(sysdate,'mm') would return the beginning of the current month. You can then use the add_months() function to get the beginning of the previous month, something like this:
select count(distinct switch_id)
from xx_new.xx_cti_call_details#appsread.prd.com
where dealer_name = 'XXXX'
and creation_date >= add_months(trunc(sysdate,'mm'),-1)
and creation_date < trunc(sysdate, 'mm')
As a little side not you're not explicitly converting to a date in your original query. Always do this, either using a date literal, e.g. DATE 2012-08-31, or the to_date() function, for example to_date('2012-08-31','YYYY-MM-DD'). If you don't then you are bound to get this wrong at some point.
You would not use sysdate - 15 as this would provide the date 15 days before the current date, which does not seem to be what you are after. It would also include a time component as you are not using trunc().
Just as a little demonstration of what trunc(<date>,'mm') does:
select sysdate
, case when trunc(sysdate,'mm') > to_date('20120901 00:00:00','yyyymmdd hh24:mi:ss')
then 1 end as gt
, case when trunc(sysdate,'mm') < to_date('20120901 00:00:00','yyyymmdd hh24:mi:ss')
then 1 end as lt
, case when trunc(sysdate,'mm') = to_date('20120901 00:00:00','yyyymmdd hh24:mi:ss')
then 1 end as eq
from dual
;
SYSDATE GT LT EQ
----------------- ---------- ---------- ----------
20120911 19:58:51 1
Data for last month-
select count(distinct switch_id)
from xx_new.xx_cti_call_details#appsread.prd.com
where dealer_name = 'XXXX'
and to_char(CREATION_DATE,'MMYYYY') = to_char(add_months(trunc(sysdate),-1),'MMYYYY');
I believe this would also work:
select count(distinct switch_id)
from xx_new.xx_cti_call_details#appsread.prd.com
where
dealer_name = 'XXXX'
and (creation_date BETWEEN add_months(trunc(sysdate,'mm'),-1) and trunc(sysdate, 'mm'))
It has the advantage of using BETWEEN which is the way the OP used his date selection criteria.
It is working with me in Oracle sql developer
SELECT add_months(trunc(sysdate,'mm'), -1),
last_day(add_months(trunc(sysdate,'mm'), -1))
FROM dual
Getting last nth months data retrieve
SELECT * FROM TABLE_NAME
WHERE DATE_COLUMN BETWEEN '&STARTDATE' AND '&ENDDATE';