Oracle date comparison in where clause issue - sql

I am seeing different results for a query whenever I rearrange the conditions of the where clause; I am trying to figure out why this is. I was given to try and figure out what is going on, so I could use some help to ascertain what may be causing an issue. To be clear, we are not receiving errors when running the select statement, but receiving unintended data results. This is an Oracle database (10-g I believe). We have two conditions for the same field. The first is to make sure the results are greater than 30 years ago (this database was not set my by myself, but the team that owns the application decided to change the created_date to 30 in the past was a good idea).....the second is to have a more specific date range of September 1st to October 1st.
SELECT created_date
FROM tableName
WHERE
created_date > sysdate - 11000 -- 30 years in the past
AND short_name like 'FOO%'
AND trunc(created_date) BETWEEN TO_DATE('09-01-2017', 'MM-DD-YYYY')
AND TO_DATE('10-01-2017', 'MM-DD-YYYY')
When running the query above, we receive results with a created_date outside of the condition at the end of the where clause.
However, when I place the comparison between the created_date and the sysdate AFTER the comparison between the two dates, we receive the correct results.
SELECT created_date
FROM tableName
WHERE
short_name like 'FOO%'
AND trunc(created_date) BETWEEN TO_DATE('09-01-2017', 'MM-DD-YYYY')
AND TO_DATE('10-01-2017', 'MM-DD-YYYY')
AND created_date > sysdate - 11000 -- 30 years in the past
Also, I removed the trunc portion of "AND trunc(created_date) BETWEEN TO_DATE('09-01-2017', 'MM-DD-YYYY') " to "AND created_date BETWEEN TO_DATE('09-01-2017', 'MM-DD-YYYY') " and received the intended results with the sysdate comparison before or afterward.
SELECT created_date
FROM tableName
WHERE
created_date > sysdate - 11000 -- 30 years in the past
AND short_name like 'FOO%'
AND created_date BETWEEN TO_DATE('09-01-2017', 'MM-DD-YYYY')
AND TO_DATE('10-01-2017', 'MM-DD-YYYY')
EDIT:
There are numerous records in the database. There is only 1 result with a created_date of 22-Sep-17.
SELECT created_date
FROM tableName
WHERE
created_date > sysdate - 11000 -- 30 years in the past
AND short_name like 'FOO%'
AND trunc(created_date) BETWEEN TO_DATE('09-01-2017', 'MM-DD-YYYY')
AND TO_DATE('10-01-2017', 'MM-DD-YYYY')
Produces 25 entries with a created_date of 17-Feb-04 and 1 with 22-Sep-17.
I understand sysdate - 11,000 is a little more than 30 years. The current requirement is to do a check for 11000 days and not 30 years. I was off in the year estimate. The sysdate is used because records that are considered old or updated will be placed with a created_date further than sysdate - 11000. I understand this makes no sense when there is already another date range condition in this query, but the solution has already been built and sent off. In order to make changes, I need to figure out WHY I am getting what I am getting, which is why I am posting here.
The second date range, in this case, 09-01-2017 and 10-01-2017, are parameters passed in by so they could be any range from any day in the past up until today. I understand it is a bad design, but I did not build it.
These will give two subtly different results for the values between
2017-10-01 00:00:01 and 2017-10-01 23:59:59. The query using TRUNC
will include that range where without TRUNC that range will be
excluded.
The intended result is to have on record returned back within the date range 22-Sep-17; however, we are receiving 25 records all with dates of 17-Feb-04....If I remove the created_date > sysdate - 11000 I receive the only one record as well.

The first is to make sure the results are greater than 30 years ago
You aren't subtracting 30 years, you are subtracting 11,000 days which is approximately (depending on leap years) 30 years and 42 days.
If you want to subtract 30 years then use:
ADD_MONTHS( date_value, -30 * 12 )
the second is to have a more specific date range of September 1st to October 1st.
This makes the first condition irrelevant - if it is between 2017-09-01 and 2017-10-01 then it will be within the last 30 years and you do not need that first condition.
I removed the trunc portion of "AND trunc(created_date) BETWEEN TO_DATE('09-01-2017', 'MM-DD-YYYY')" to "AND created_date BETWEEN TO_DATE('09-01-2017', 'MM-DD-YYYY')" and received the intended results with the sysdate comparison before or afterward.
These will give two subtly different results for the values between 2017-10-01 00:00:01 and 2017-10-01 23:59:59. The query using TRUNC will include that range where without TRUNC that range will be excluded.
Also, using TRUNC means that Oracle will not us an index on the created_date column (and would require a function-based index on TRUNC( created_date )). If you want to use an index then just use the column and test to see if it is greater or equal than the start of the range and less than the end of the range plus one day (to include up to 23:59:59 of that day).
The simplified equivalent of your TRUNC query is:
SELECT created_date
FROM tableName
WHERE short_name like 'FOO%'
AND created_date >= DATE '2017-09-01' -- range start
AND created_date < DATE '2017-10-01' + INTERVAL '1' DAY -- range end + 1 day

Related

Sorting SQL Query By Date Column

I'm working on a project with a db that contains a date column for patient visits in the format of %m-%d-yyyy and need to sort so that it only pulls the rows where that date is within the last two weeks. I've tried a few different functions of convert, to_date, and can't seem to get anything to work.
I'm still very new to SQL and I don't know if this is a special case because I'm working with an oracle db
Not the full code, because it has dozens of queries and multiple joins (would that affect the date syntax?) but this is the format I'm trying for...
create table "Visits"
insert into "Visits" (
'John Doe',
'5/24/2021',
'Story about the visit',
'More room for story if needed')
select
"User_Name",
"Visit_Date",
"Visit_Narrative",
"Visit_Narrative_Overflow"
from "Visits"
where "Visits"."Visit_Date" >= TRUNC(SYSDATE) - 14
I'm working on a project with a db that contains a date column for patient visits in the format of %m-%d-yyyy
No, you don't have it in the format mm.dd.yyyy. A DATE data type value is stored within the database as a binary value in 7-bytes (representing century, year-of-century, month, day, hour, minute and second) and this has no format.
need to sort so that it only pulls the rows where that date is within the last two weeks.
You want a WHERE filter:
If you want to have the values that happened in the last 14 days then TRUNCate the current date back to midnight and subtract 14 days:
SELECT visit_date
FROM patient
WHERE visit_date >= TRUNC(SYSDATE) - INTERVAL '14' DAY
or
SELECT visit_date
FROM patient
WHERE visit_date >= TRUNC(SYSDATE) - 14
(or subtract 13 if you want today and 13 days before today.)
If you want it after Monday of last week then TRUNCate to the start of the ISO Week (which is always a Monday) and subtract 7 days:
SELECT visit_date
FROM patient
WHERE visit_date >= TRUNC(SYSDATE, 'IW') - INTERVAL '7' DAY
I ended up figuring it out based on an answer from another forum (linked below);
it appears that my original to_date() was incomplete without a second and operator in my where clause. This code is working perfectly
select
"User_Name",
"Visit_Date",
"Visit_Narrative",
"Visit_Narrative_Overflow"
from "SQLUser"."Visits"
where "SQLUser"."Visits"."Visit_Date" >= to_date('5/10/2021', 'MM/DD/YYYY')
and "SQLUser"."Visits"."Visit_Date" < to_date('5/24/2021', 'MM/DD/YYYY')

Right parenthesis error in oracle SQL Developer

SELECT titol, data_prestec
FROM if_llibres, if_llibre_prestec
WHERE
if_llibre_prestec.ubicacio = if_llibres.ubicacio
AND data_devolucio IS NULL
AND data_prestec <= date_sub(current_date(),interval 30 day);
There is no DATE_SUB() function in Oracle. You could phrase this as:
select
titol,
data_prestec
from if_llibres il
inner join if_llibre_prestec ilp
on ilp.ubicacio = il.ubicacio
where
data_devolucio is null
and data_prestec <= current_date - interval '30' day;
Note that I rewrote your query to use a standard, explicit join (with the on keyword) rather than an implicit join (with a comma in the from clause): this old syntax from decades ago should not be used in new code.
I would also recommend prefixing each column in the SELECT and WHERE clause with the (alias of the) table it belongs to: this makes the query unambiguous and easier to understand.
Subtracting 30 days smells like "previous month". If that was your intention, well, not all months have 30 days so you'll get wrong result. Note that Oracle offers the ADD_MONTHS function which allows you to subtract any number of months. In your case - 1 month.
Furthermore, current_date returns both date and time, so of you subtract 1 month (or 30 days, doesn't matter), you'll move back to that very second, not date itself.
Here's what I mean:
SQL> alter session set nls_Date_format = 'dd.mm.yyyy hh24:mi:ss';
Session altered.
SQL> select current_date as curdat,
2 current_date - interval '30' day as cur_30,
3 add_months(current_date, -1) as adm_1,
4 --
5 trunc(current_date) - interval '30' day as cur_trun_30,
6 add_months(trunc(current_date), -1) as adm_trun_1
7 from dual;
CURDAT CUR_30 ADM_1 CUR_TRUN_30 ADM_TRUN_1
------------------- ------------------- ------------------- ------------------- -------------------
14.06.2020 21:49:43 15.05.2020 21:49:43 14.05.2020 21:49:43 15.05.2020 00:00:00 14.05.2020 00:00:00
SQL>
CURDAT is current date (along with time)
CUR_30 subtracted 30 days; as you can see, you aren't on 14.05. but 15.05. at 21:49:43 so your query will return rows whose date value depends on that time as well
ADM_1 subtracted 1 month from current date, and it brought you back to 14.05. - again with the time component
CUR_TRUN_30 truncated current date (and "removed" the time component, setting time to midnight at beginning of that day), but you're again at 15.05. - not 14.05.
ADM_TRUN_1 truncated current date and set you to 14.05.2020 00:00 which is - I believe - what you wanted
Therefore, my suggestion would be
SELECT titol,
data_prestec
FROM if_llibres join if_llibre_prestec ON if_llibre_prestec.ubicacio = if_llibres.ubicacio
WHERE data_devolucio IS NULL
AND data_prestec <= ADD_MONTHS(TRUNC(current_date), -1);
Not related to your problems, but I'd suggest you to always use table aliases. The way you wrote the query, it is impossible to know which column belongs to which table and it makes confusion.

What is the difference between these 2 sql queries?

select * from accounts where sysdate - 100 < last_active_dt;
select * from accounts where sysdate - 100 <= last_active_dt;
Both of the queries returned the same result.
I was thinking that for the first query, it would not select
records on the day of last_active_dt. For the 2nd query, it
will select records on the day of last_active_dt.
But this does not seem to be the case as both returned the same results.
Is there any difference to it? And how can I see the difference?
last_active_dt column follows a date format DD-MON-YYYY
last_active_dt column follows a date format DD-MON-YYYY
No, it doesn't (but it may look like it does in the user interface you are using).
A DATE data type always has year, month, day, hour, minute and second components and is stored (without any formatting) in 7 bytes. It is the user interface that applies a (default) format to the date and this may hide the time component from you.
You can use:
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';
To change the default date format used in SQL*Plus (and SQL Developer) to see the date with all the components.
Equally, SYSDATE is of a DATE data type and has year, month, day, hour, minute and second components so what you are probably intending is:
select * from accounts where TRUNC( sysdate ) - INTERVAL '100' DAY < last_active_dt;
This will get all the columns where the last_active_dt is after midnight of the day 100 days ago (even if it 1 second after). This will work if all your last_active_dt values have the times TRUNCated to exactly midnight 00:00:00 but not if the column as non-zero time components as you will still get values with times from 00:00:01 to 23:59:59 of the day 100 days ago.
If your last_active_dt has a non-zero time component then you want:
select * from accounts where TRUNC( sysdate ) - INTERVAL '99' DAY <= last_active_dt;

Sql query to return data if AVG(7 days record count) > (Today's record count)

I want to write a SQL query in Oracle database for:
A priceindex(field name) have around 120(say) records each day and I have to display the priceindex name and today's date, if the avg of last 7 days record count is greater than Todays record count for the priceindex(group by priceindex).
Basically, There will be 56 priceindex and each should have around 120 records each day and is dump to database each day from external site. So want to make sure all records are downloaded to the database everyday.
Except for the clarification I requested in a Comment to your question (having to do with "how can we know today's final count, when today is not over yet), the problem can be solved along the following lines. Not tested since you didn't provide sample data.
From your table, select only the rows where the relevant DATE is between "today" - 7 and "today" (so there are really EIGHT days: the seven days preceding today, and today). Then group by PRICEINDEX. Count total rows for each group, and count rows just for "today". The rows for "today" should be less than 1/8 times the total count (this is easy algebra: this is equivalent to being less than 1/7 times the count of OTHER days).
Such conditions, at the group level, must be in the HAVING clause.
select priceindex
from your_table
where datefield >= trunc(sysdate) - 7 and datefield < trunc(sysdate) + 1
group by priceindex
having count(case when datefield >= trunc(sysdate) then 1 end) < 1/8 * count(*)
;
EDIT The OP clarified that the query runs every day at midnight; this means that "today" should actually mean "yesterday" (the day that just ended). In Oracle, and probably in all of computing, midnight belongs to the day that BEGINS at midnight, not the one that ends at midnight. The time-of-day at midnight is 00:00:00 (beginning of the new day), not 24:00:00.
So, the query above will have to be changed slightly:
select priceindex
from your_table
where datefield >= trunc(sysdate) - 8 and datefield < trunc(sysdate)
group by priceindex
having count(case when datefield >= trunc(sysdate) - 1 then 1 end)
< 1/8 * count(*)
;

Oracle SQL - Return records where date1 is within 24 months of date2

I'm quite new to SQL but I've been asked to write something I don't fully understand. I'm looking to select records where a date is within 24 months of another date. Something like :-
select *
from table
where (date1 within 24 months of date2)
So for example, if date1 was 01/01/2010, where date2 has the following values the corresponding records in the table would be returned:-
05/09/2009
01/02/2008
06/03/2011
but where date2 is, for example, 25/12/2013, the corresponding data in the table would not be returned.
It sounds like you want
SELECT *
FROM table
WHERE date1 >= add_months( date2, -24 )
AND date1 <= add_months( date2, 24 )
Your sample data isn't clear as to whether you want to include dates that are exactly 24 months earlier or later or not-- you can obviously adjust the <= and >= to be < and > if you want to omit dates that are exactly 24 hours apart.
An Oracle DATE column always has both a day an a time component. The query I posted will compare dates including whatever time is present. If you want to set all the time components to midnight, you can use the trunc function (i.e. trunc(date1) and trunc(date2)). If you trunc(date1) in the predicate, however, you won't be able to use an index on date1 in your query. You could create a function-based index on trunc(date1) instead, however.
An alternative to the add_months method:
select *
from table
where abs(months_between(date1, date2)) <= 24