SQL Query required ot get records count on hourly basis - sql

I am trying to get records from 29th April,2022 on hourly basis from Oracle DB, however with the below query I am getting records count older than 29th April as well(all previous records count as well). Can you help fine tune the query?
SELECT DISTINCT
COUNT(*),
STATUS,
TO_CHAR(LOAD_DATE,'DD-MON-YY HH24')
FROM
TARGET_HIST
WHERE
STATUS = 'A'
AND TO_CHAR(LOAD_DATE, 'DD-MON-YY HH24:MI:SS') > '29-APR-22 00:00:00'
GROUP BY
STATUS,
TO_CHAR(LOAD_DATE,'DD-MON-YY HH24')
ORDER BY
STATUS,
TO_CHAR(LOAD_DATE,'DD-MON-YY HH24');

Try this. Since you didn't provide any sample data I didn't test it for you
select trunc(load_date,'HH') "HOUR", count(*)
from target_hist
where status='A' AND
load_date between to_date('29/04/2022','DD/MM/YYYY') and to_date('29/04/2022 23:59:59','DD/MM/YYYY HH24:MI:SS');
Group by trunc(load_date,'HH')
Order by trunc(load_date,'HH')

Two problem with the same reason:
In your WHERE clause you look for rows after '29-APR-22 00:00:00', but you get rows before that.
In your ORDER BY clause you get the dates sorted in a mangled order.
This is because you have converted the datetimes to strings where '29-APR-22' comes after '01-MAY-22', but before '30-JAN-22', because '2' comes after '1' and before '3'.
If you want to sort and compare datetimes, then use datetimes. You can truncate them down to the hour with TRUNC(load_date, 'hh').
select
trunc(load_date, 'hh') as load_hour,
status,
count(*)
from target_hist
where status = 'A'
and load_date >= date '2022-04-29'
group by trunc(load_date, 'hh'), status
order by trunc(load_date, 'hh'), status;
Leave it to your app to display the datetime in the format the user wants to see it. If you need a particular format, e.g. for exporting the data into a file, you can apply TO_CHAR on the truncated datetime TO_CHAR(trunc(load_date, 'hh'),'DD-MON-YY HH24') in the select clause (and only there).
Please note that I have removed DISTINCT from the query, because there are no duplicates to remove. And I am using a date literal in the WHERE clause. And >=in order to include midnight.
This query considers all days since April 29. If you want this day only, then add and load_date < date '2022-04-30'.

Related

Oracle SQL: How to modify query in order to get only results within a certain timeframe?

I use this statement in Oracle SQL Developer
select to_char(time,'DD/MM/YY hh24'),count(column) as xyz from table
where to_char(time,'DD/MM/YY')>= '08/04/21'
and to_char(time,'DD/MM/YY')<= '09/04/21'
and column='xyz'
group by to_char(time,'DD/MM/YY hh24')
order by to_char(time,'DD/MM/YY hh24');
What I expect is a result/table in which the result is ordered by time in ascending order (starting with the earliest hour on 08/04/21 and ending with the latest on 09/04/21. I would expect only entries for days 08/04/21 and 09/04/21. Instead, I get a result where also other dates are included like 09/02/21 or 08/12/20.
How can I modify my query?
You are converting your native date values to strings (with two-digit years!) and then comparing those strings. The string '08/12/20' is 'less than' the string '09/04/21'.
Compare your dates with other dates, which is easier as literals:
select to_char(trunc(time, 'HH'), 'DD/MM/YY HH24'), count(column) as xyz
from table
where time >= date '2021-04-08'
and time < date '2021-04-10'
and column='xyz'
group by trunc(time, 'HH')
order by trunc(time, 'HH');
I've used trunc() to remove/zero the minute and seconds parts, which means you can then group and order by that value; and just convert to a string for display at the last moment.
I've also converted to_char(time,'DD/MM/YY')<= '09/04/21' to time < date '2021-04-10' rather than time < date '2021-04-09'as your version include all data from the 9th; which may or may not be what you intended - you might have been trying to get a single day.
db<>fiddle demo
Assuming that time is of data type date, you don't want to do a to_char on it in your where clause or in your order by. As written, you're doing string comparisons rather than date comparisons so you're getting rows where the to_char(time string sorts alphabetically between the two values not rows where the date is between the two dates. Compare against date literals or do explicit to_date calls on your string literals
My wager is that you really want something like this
select trunc(time, 'HH24'),count(column) as xyz
from table
where time >= date '2021-08-04'
and time <= date '2021-09-04'
and column='xyz'
group by trunc(time, 'HH24')
order by trunc(time, 'HH24');

My TO_DATE seeming not to function properly

So I am trying to get the section id and the amount of students in that section who enrolled on 02/10/2007. The query returns no results when it should return 6 rows.
The date format its in already is DD-MON-YY.
This is what I have so far:
I took the TO_DATE from another query I did and it worked properly on. The query works without it so im sure its somthing to do with the TO_DATE
SELECT section_id, COUNT(student_id) "ENROLLED"
FROM enrollment
WHERE enroll_date = TO_DATE('2/10/2007', 'MM/DD/YYYY')
GROUP BY section_id
ORDER BY ENROLLED;
Most probably the issue is that there is a fractional date component that you are not taking into account. You can ignore that fractional date component by truncating the column in your query:
SELECT section_id, COUNT(student_id) "ENROLLED"
FROM enrollment
WHERE TRUNC(enroll_date) = TO_DATE('2/10/2007', 'MM/DD/YYYY')
GROUP BY section_id
ORDER BY ENROLLED;
I am assuming that the column enroll_date is of the data type DATE.
Some explanation: Oracle stores dates as described here, it does NOT store a date as you state "The date format its in already is DD-MON-YY.". That is only the format you see the date in, which is determined by the parameter NLS_DATE_FORMAT for your session.
Lets do a quick test with a test table. Create table and check the NLS_DATE_FORMAT form my session.
create table DATE_TST
( id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY,
test_date DATE
);
INSERT INTO date_tst (test_date) VALUES (SYSDATE);
SELECT value
FROM nls_session_parameters
WHERE parameter = 'NLS_DATE_FORMAT';
DD-MON-YYYY
This is how I will see my dates.
SELECT * FROM date_tst;
04-OCT-2020
So I have todays date. Cool. Now lets see if I can query using that date:
SELECT * FROM date_tst WHERE test_date = TO_DATE('04-OCT-2020','DD-MON-YYYY');
no rows.
No rows are shown because the date format I get my date in does not have a time component. DATE has Year, month, day, hour, minute and seconds. The format only has year, month and day. Lets query the data to check if there is a time component.
SELECT TO_CHAR(test_date,'DD-MON-YYYY HH24:MI:SS') FROM date_tst;
4-OCT-2020 21:12:39
Ah there it is... SYSDATE is the current time up to the second. Now lets try that query again with a more precise date format:
SELECT * FROM date_tst WHERE test_date = TO_DATE('04-OCT-2020 21:12:39','DD-MON-YYYY HH24:MI:SS');
04-OCT-2020
And there is our row. The TRUNC command will cut off the time component:
SELECT TO_CHAR(TRUNC(test_date),'DD-MON-YYYY HH24:MI:SS') FROM date_tst;
04-OCT-2020 00:00:00
So you can simplify your query:
SELECT * FROM date_tst WHERE TRUNC(test_date) = TO_DATE('04-OCT-2020','DD-MON-YYYY');
04-OCT-2020
TO_DATE('2/10/2007', 'MM/DD/YYYY') gives you a date at midnight; however,this will only match values at that instant. What you need to do is either:
TRUNCate the dates in your column back to midnight so that your value matches (however, this will prevent you using an index on the column and you would need to use a function-based index); or
A better solution is to use a date range starting at midnight of the day you want to match and going up-to, but not including, midnight of the next day.
You can do this using TO_DATE or using a date literal:
SELECT section_id,
COUNT(student_id) "ENROLLED"
FROM enrollment
WHERE enroll_date >= DATE '2007-02-10'
AND enroll_date < DATE '2007-02-11'
GROUP BY section_id
ORDER BY ENROLLED;
As an aside:
The date format its in already is DD-MON-YY.
Assuming that the enroll_date column has a DATE data type then this has no format; it is a binary data type consisting of 7 bytes (for century, year-of-century, month, day, hour, minute and second).
What you are seeing is the default date format the user interface applies when it displays the binary date value to the user and you can change it using:
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD"T"HH24:MI:SS';
(or to whatever format you want.)
This does not change the binary data stored in the column.

two results when i use oracle date

i am running below sql in sql developer:
SELECT count(*)
from FTTH_AMS_DEVICE_METRICS DAT
where TRUNC(DAT.COLLECTION_TMS)>(select start_dt from ETL_JOB_CONTROL where job_name='s_m_ftth_prfrm_fact_tbl')
and TRUNC(DAT.COLLECTION_TMS)<=(select end_dt from ETL_JOB_CONTROL where job_name='s_m_ftth_prfrm_fact_tbl')
i get 38 million rows.
when i use hardcoded values in filter as below and run the query:
SELECT count(*)
from FTTH_AMS_DEVICE_METRICS DAT
where TRUNC(DAT.COLLECTION_TMS)> TO_DATE('10-14-2016','MM-DD-YYYY')
and TRUNC(DAT.COLLECTION_TMS)<= TO_DATE('10-15-2016','MM-DD-YYYY')
i am getting 12 million rows.
because i pass the date values through parameter files in informatica.
I am very much confused how to handle this and get 38 million rows when i run in Informatica.
data type for COLLECTION_TMS is timestamp and end_dt is datetime.
if you need more information on this, i will share immediately.
Thanks for your help.
If the dates are exactly as you show them, then in the second query you should have > to_date ('10-15-2016', 'mm-dd-yyyy') and <= to_date('10-16-2016', 'mm-dd-yyyy'). You are comparing to the wrong dates.
The following is speculation, informed speculation. The date data type in Oracle supports a time component. However, when you print out the date, often the time component is removed.
I am guessing that the values in start_dt and end_dt in ETL_JOB_CONTROL have time components.
If so, the following query should return at least 38 million:
select count(*)
from FTTH_AMS_DEVICE_METRICS DAT
where TRUNC(DAT.COLLECTION_TMS) > TO_DATE('10-14-2016', 'MM-DD-YYYY') and
TRUNC(DAT.COLLECTION_TMS) <= TO_DATE('10-16-2016', 'MM-DD-YYYY');
If this is the case, then just determine the full value for the two columns and use that:
select to_char(start_dt, 'YYYY-MM-DD HH24:MI:SS'),
to_char(end_dt, 'YYYY-MM-DD HH24:MI:SS')
from ETL_JOB_CONTROL;

Why am I getting different results when comparing dates two ways?

This query:
select count(*), trim(data_date)
from man
where data_status = 'received' and data_date > sysdate-7
group by trim(data_date);
gives result like:
199 05-APR-16
But this query:
select count(*), trim(data_date)
from man
where data_status = 'received' and trunc(data_date) = date '2016-04-05'
group by trim(data_date);
gives results like:
347 05-APR-16
Why are the queries giving different results for the same day?
Because your man_date_sub values are not all at midnight. If you keep running the first query the number of records returned will (probably) gradually reduce. That is only happening to the count for the 5th as that is a week ago. Your sysdate - 7 is a moving target, not just as you move from day to day, but as time passes during the day.
You can check the times with:
select to_char(man_date_sub, 'YYYY-MM-DD HH24:MI:SS'),
to_char(sysdate - 7, 'YYYY-MM-DD HH24:MI:SS'),
man_date_sub - (sysdate - 7)
from man
where trunc(man_date_sub) = date '2016-04-05';
You'll see that some have times before the current sysdate time, while others have times after it. The third, generated, column will show some positive and some negative values.
In your second query you're comparing trunc(man_date_sub), which sets the time part to midnight, with date '2016-04-05', which is also at midnight; so all the records at any time on that day now match.
You can go back to midnight on your 7-day range, and get an equivalent result, by truncating sysdate:
select count(*), trim(man_date_sub)
from man
where man_status = 'SUBMITTED' and man_date_sub > trunc(sysdate)-7
group by trim(man_date_sub);
Your use of the trim() function is a bit odd; all you're doing is removing leading and trailing whitespace from the string '05-APR-16', which isn't actually doing anything. You're also relying on implcit conversion of the date to a string using your session NLS_DATE_FORMAT. It would be better to specify the format:
select count(*), to_char(man_date_sub, 'YYYY-MM-DD')
from man
where man_status = 'SUBMITTED' and man_date_sub > trunc(sysdate)-7
group by to_char(man_date_sub, 'YYYY-MM-DD');
If you ran your original query in a session that had an NLS_DATE_FORMAT that included time elements then you wouldn't get the result you expect.
I'm not sure if you're confusing it with trunc(), though clearly you're using that elsewhere. Truncating a date sets the time portion to midnight (by default; it can do other things), but leaves it as a date, which would be suitable for grouping but should still be formatted explicitly for display.

Sql strictly more than query

I'm in PostgreSQL.
I need to print all mailing with creation date strictly more that 2015-04-04. I tried the following queries:
SELECT *
FROM mailing.mailing
WHERE creation_date > '2015-04-04';
and
SELECT *
FROM mailing.mailing
WHERE creation_date >= '2015-04-04';
But they produced the same result set(including '2015-04-04'). Is it possible to write such a query without explicitly saying WHERE creation_date >= '2015-04-05';
UPD: The column's type is timestamp without time zone.
If your creation_date field is of type datetimetry comparing it to '2015-04-04 23:59:59' instead, as '2015-04-04 08:30:00' seems to be greater than '2015-04-04'.
Assuming your default date format for your database is 'YYYY-MM-DD' and creation_date field is a date type, your query will actually be converted automatically to something like:
SELECT *
FROM mailing.mailing
WHERE creation_date > to_date('2015-04-04', 'YYYY-MM-DD');
The date value you have provided represents the first second of that day, that's why you see no difference between your queries. (Your first query would exclude the first second of the day though.)
What you could do to avoid this is:
where creation_date >= to_date('2015-04-05 00:00:00', 'YYYY-MM-DD HH24:MI:SS')
or
where date_trunc(creation_date-1) = '2015-04-04'