using date function to query - oracle

I have the following table
Alarm (AlarmID INT, InstalledDate Date)
Given that the alarm need to be replace every 5 years, how do i display all the alarms that is due for replacement in the next 6 months?
I tried the following and there was no result:
SELECT AlarmID
FROM Alarm
WHERE Add_months(InstalledDate, 60)
BETWEEN SYSDATE AND Add_months(SYSDATE, 6);

"I tried the following and there was no result:"
The query you propose looks correct, so perhaps you don't have any ALARMS which are five years old?
"it seems like there is a difference using BETWEEN SYSDATE AND
Add_months(SYSDATE, 6) compare to BETWEEN Add_months(SYSDATE, 6) AND
SYSDATE;"
The BETWEEN operator demands that we pass the two values in a specific order, lower bound then upper bound. So this filter is true:
where date '2012-03-01' between date '2012-01-01' and date '2012-06-01'
whereas this is false:
where date '2012-03-01' between date '2012-06-01' and date '2012-01-01'
Perhaps this seems unfair, but the Oracle documentation makes it clearer by translating the BETWEEN operator into lt and gt statements:
where date '2012-03-01' >= date '2012-01-01'
and date '2012-03-01' <= date '2012-06-01'
If you swap the values of the second and third expressions you'll see why the reversed order returns false.

Related

Is it possible to use multiple conditions in a check constraint in Oracle?

I want to use (between, and, or) conditions in a check constraint at the same time. For example:
Alter table called_no add constraint called_no_chck
check ("call_time" between '01:00:00 AM' and '12:59:59 AM' or
between '01:00:00 PM' and '12:59:59 PM')
You certainly may use multiple conditions in a single check constraint.
Your attempt fails because you wrote an invalid condition. The same condition would fail if it was in the where clause of a query, or in any other place where you need a condition.
The correct way to check that a date-time is between A and B or between C and D looks like this:
...check (call_time between A and B OR call_time between C and D)
^^^^^^^^^
You understand the shorthand, where you don't repeat the column name (call_time) before the second between. I understand it too. A computer does not - they are not human. OR must appear between two conditions. A condition such as between must be applied to something, and you can't leave that "something" out and expect the computer to understand it's the same "something" you already used in another condition.
There are other mistakes in your condition though. 12:59:59 AM is about one hour after midnight; it's not about one hour after noon, as you seem to believe. Nothing is ever between 1 AM and 12:59:59 AM (of the same day), because 1 AM is after 12:59:59 AM.
It's also not clear what the data type of your column is. If it's date (as it almost surely should be), you can't just compare it to strings (as you have in your condition). And, you can't simply compare a date-time to something that's just time-of-day.
All these mistakes, though, are out of the scope of your question as you asked it (which was: Can you use "compound" conditions in a constraint; the answer, again, is YES).
Do not use a string column.
If you just want a time without a date, use an INTERVAL DAY(0) TO SECOND(0) column.
If you want a date-time column with accuracy down to whole seconds (and no time-zone) then use a DATE column (which always has the components year, month, day, hour, minute and seconds).
With either of those two options, you do not need a CHECK constraint as it will not accept invalid times.
If you want to format the value using the 12-hour clock then you can use a virtual column.
CREATE TABLE table_name (
time INTERVAL DAY(0) TO SECOND(0),
datetime DATE,
formatted_time VARCHAR2(11)
GENERATED ALWAYS AS (TO_CHAR(DATE '1970-01-01' + time, 'HH12:MI:SS AM')),
formatted_datetime VARCHAR2(11)
GENERATED ALWAYS AS (TO_CHAR(datetime, 'HH12:MI:SS AM'))
);
INSERT INTO table_name (time, datetime)
SELECT INTERVAL '00:12:34' HOUR TO SECOND, SYSDATE FROM DUAL UNION ALL
SELECT INTERVAL '12:34:56' HOUR TO SECOND, SYSDATE + 0.5 FROM DUAL;
Then:
SELECT * FROM table_name;
Outputs:
TIME
DATETIME
FORMATTED_TIME
FORMATTED_DATETIME
+0 00:12:34
2021-12-18 15:37:34
12:12:34 AM
03:35:47 PM
+0 12:34:56
2021-12-19 03:37:34
12:34:56 PM
03:35:47 AM
If you are storing it in a string (don't) then, yes, you can use multiple conditions in a single constraint:
CREATE TABLE table_name (
time VARCHAR2(11)
CHECK (
SUBSTR(time, 1, 2) BETWEEN '01' AND '12'
AND SUBSTR(time, 4, 2) BETWEEN '00' AND '59'
AND SUBSTR(time, 7, 2) BETWEEN '00' AND '59'
AND SUBSTR(time, 10, 2) IN ('AM', 'PM')
AND time LIKE '__:__:__ __'
)
);
or multiple constraints:
CREATE TABLE table_name (
time VARCHAR2(11)
CONSTRAINT invalid_hours CHECK (SUBSTR(time, 1, 2) BETWEEN '01' AND '12')
CONSTRAINT invalid_minutes CHECK (SUBSTR(time, 4, 2) BETWEEN '00' AND '59')
CONSTRAINT invalid_seconds CHECK (SUBSTR(time, 7, 2) BETWEEN '00' AND '59')
CONSTRAINT invalid_meridian CHECK (SUBSTR(time, 10, 2) IN ('AM', 'PM'))
CONSTRAINT invalid_format CHECK (time LIKE '__:__:__ __')
);
But its much easier to not implement all those constraints and just use either an INTERVAL or a DATE when you won't be allowed to enter invalid data.
db<>fiddle here

How can I use an expression in my where clause to return a dynamic date range for my query

I am working in BIDS 2008r2 on a SSRS report that pulls data from an Oracle database.
I have a where clause that uses a hard date range, I want to change it to an expression that will dynamically change as time progresses.
This is the where clause that currently works to return the 1st day of the previous month to the last day of the previous month. ie I am looking for all data from the previous month
WHERE CHRGDTTM BETWEEN {ts '2015-12-01 00:00:00'} AND {ts '2015-12-31 23:59:00'}
I have written an expression that returns the beginning of last month:
DateAdd(DateInterval.Month, -1, DateSerial(Year(Date.Now), Month(Date.Now), 1))
and one that returns the end of last month:
DateAdd(DateInterval.Minute, -1, DateSerial(Year(Date.Now), Month(Date.Now), 1))
How do I get those into my where clause?
Thank you.
If you want to do this entirely within the Oracle where clause you can do:
WHERE CHRGDTTM >= ADD_MONTHS(TRUNC(sysdate, 'MM'), -1)
AND CHRGDTTM < TRUNC(sysdate, 'MM')
The TRUNC(date) function truncates the supplied date - the system date in this case; by default it removes the time part so gives you midnight this morning, but this modified that behaviour with the MM format model, and gives you midnight on the first of the current month. So today TRUNC(SYSDATE, 'MM') gives you 2016-01-26 00:00:00. You can use that as it is for the upper end of your date range.
The ADD_MONTHS() function, well, adds a number of months, -1 here to give you 2015-12-01 00:00:00 instead. Put together that gives you everything from 2015-12-01 00:00:00 up to, but no including, 2016-01-01 00:00:00, which is equivalent to your BETWEEN range.
You could also use an interval calculation to get the start of the previous month:
WHERE CHRGDTTM >= TRUNC(sysdate, 'MM') - INTERVAL '1' MONTH
AND CHRGDTTM < TRUNC(sysdate, 'MM')
which has the same effect, and is safe as you're always going to end up with a valid date from the calculation; dates at the ends of months can be more problematic.
You can read more about datetime/interval arithmetic in the documentation.
As an alternative, you can create two parameters of type Date/Time and set the Default Values for the parameters with the expressions you've developed. Then in the query it just becomes WHERE CHRGDTTM BETWEEN :StartDate AND :EndDate. If the user needn't worry about this, set the visibility to Hidden for both.

convert minutes to hh/mi/ss format in oracle query

I want to know the query which converts minutes to the format of hh/mi/ss in Oracle.I 've already seen lot of same questions from many forums but nothing helped me to get the exact result.
The query I used -Select to_char(to_date(mod(100,60),'mi'),'hh/mi/ss') from dual;
But I don't know how to get the hour value.Because mod function returns only the remainder I don't know how to take the quotient part and substitute into the hour field.
I suppose there are two ways of storing "minutes" in an Oracle database - you can either store them in a field whose datatype is INTERVAL DAY TO SECOND, or you can store them in a NUMBER. The simplest case to handle is the INTERVAL - in this case, the TO_CHAR function converts the value to a string of the form SDD HH:MM:SS.FFFFFF, where 'S' is sign ('+' or '-' as intervals can be positive or negative), DD = days, HH= hours, 'MM' = minutes, 'SS' = seconds, and 'FFFFFF' = fractions; thus, to get the HH:MI:SS all we need to do is use the SUBSTR function, as in
SUBSTR(TO_CHAR(I_VAL), 5, 8)
where I_VAL is an INTERVAL DAY TO SECOND value.
If the value to be converted is in a numeric field it gets a bit messy as we have to compute the individual field values, then subtract the previous fields as part of getting the next field. However, since the value stored is in minutes instead of seconds it's not particularly difficult:
create table TST (N_VAL NUMBER,
I_VAL INTERVAL DAY TO SECOND);
INSERT INTO TST(N_VAL, I_VAL) VALUES (666, INTERVAL '666' MINUTE);
SELECT N_VAL,
TRUNC(N_VAL/60) AS HOURS,
N_VAL-(TRUNC(N_VAL/60) * 60) AS MINUTES,
0 AS SECONDS,
TO_CHAR(I_VAL),
SUBSTR(TO_CHAR(I_VAL), 5, 8) AS HMS_FROM_INTERVAL
FROM TST;
SQLFiddle here
Best of luck.

Dynamic order by date data type in Oracle using CASE

My code in the stored procedure:
SELECT * FROM
my_table ir
WHERE
--where clause goes here
ORDER BY
CASE WHEN p_order_by_field='Id' AND p_sort_order='ASC' THEN IR.ID end,
CASE WHEN p_order_by_field='Id' AND p_sort_order='DESC' THEN IR.ID end DESC,
CASE WHEN p_order_by_field='Date' AND p_sort_order='ASC' THEN TO_CHAR(IR.IDATE, 'MM/dd/yyyy') end,
CASE WHEN p_order_by_field='Date' AND p_sort_order='DESC' THEN TO_CHAR(IR.IDATE, 'MM/dd/yyyy') end DESC;
Problem is that sorting is done based on the char, which comes out wrong for the date case. CASE statement, however, won't allow any other datatype other than char. So what is the solution in this case? I need to be able to pass the p_order_by_field into the stored procedure.
Thanks
Should be simple - just use ISO date format in your case:
TO_CHAR(IR.IDATE, 'yyyy-mm-dd')
and you should be fine.
Another problem could occure when you want to sort on the date difference (let say number of days between two days).
For example such a sort would return number 13 (days) before 9 (days).
The solution is that you concatenate length of date difference and the difference itself:
length(trunc(date2) - trunc(date1)) || to_char(date2 - date1)

Adding one month to saved date(oracle)

I have a table A which contains a Date type attribute. I want to write a query to select the date in another table B with value one month after the value in A.Any one know how to do it in oracle?
uhm... This was the first hit on google:
http://psoug.org/reference/date_func.html
It seems you're looking for the "add_months" function.
You need to use the ADD_MONTHS function in Oracle.
http://www.techonthenet.com/oracle/functions/add_months.php
Additional info: If you want to use this function with today's date you can use ADD_MONTHS(SYSDATE, 1) to get one month from now.
The question is to select a date_field from table b where date_field of table b is one month ahead of a date_field in table a.
An additional requirement must be taken into consideration which is currently unspecified in the question. Are we interested in whole months (days of month not taken into consideration) or do we want to include the days which might disqualify dates that are one month ahead but only by a couple of days (example: a=2011-04-30 and b=2011-05-01, b is 1 month ahead but only by 1 day).
In the first case, we must truncate both dates to their year and month values:
SELECT TRUNC( TO_DATE('2011-04-22','yyyy-mm-dd'), 'mm') as trunc_date
FROM dual;
gives:
trunc_date
----------
2011-04-01
In the second case we don't have to modify the dates.
At least two approaches can be used to solve the initial problem:
First one revolves around adding one month to the date_field in table a and finding a row in table b with a matching date.
SELECT b.date_field
FROM tab_a as a
,tab_b as b
WHERE ADD_MONTHS( TRUNC( a.date_field, 'mm' ), 1) = TRUNC( b.date_field, 'mm' )
;
Note the truncated dates. Leaving this out will require a perfect day to day match between dates.
The second approaches is based on calculating the difference in months between two dates and picking a calculation that gives a 1 month difference.
SELECT b.date_field
FROM tab_a as a
,tab_b as b
WHERE months_between( TRUNC( b.date_field, 'mm') , TRUNC(a.date_field, 'mm') ) = 1
The order of the fields in months_between is important here. In the provided example:
for b.date_field one month ahead of a.date_field the value is 1
for b.date_field one month before a.date_field the value is -1 (negative one)
Reversing the order will also reverse the results.
Hope this answers your question.

Resources