How to select records from Oracle Database where 2 dates are not in same month and year - oracle

How to select records from Oracle Database where 2 dates are not in same month and year.
Below table is example, here I want all records where created date and updated date are not in same month and year.
The value for both date field in millisecond (ex.1454738400000) and
Data type of both date field is NUMBER(32).
---------------------------------
id| Created Date | Updated Date |
---------------------------------
1 | 02/26/2018 | 02/26/2017 |
---------------------------------
2 | 03/28/2018 | 03/26/2018 |
---------------------------------
3 | 04/26/2018 | 04/28/2017 |
---------------------------------
4 | 02/26/2018 | 02/26/2016 |
---------------------------------

Have a look at these two options:
select record_id
from your_table
where to_char(created_date, 'mm.yyyy') <> to_char(updated_date, 'mm.yyyy');
select record_id
from your_table
where trunc(created_date, 'yyyy') <> trunc(updated_date, 'yyyy')
and trunc(created_date, 'mm') <> trunc(updated_date, 'mm');
If there's a lot of data involved, consider creating a function-based index(es) on DATE columns.
[EDIT]
If those values really are numbers, then you have to convert them to DATE datatype first, then apply TRUNC function. For example:
SQL> select trunc(to_date(20190226, 'yyyymmdd'), 'yyyy') result from dual;
RESULT
----------
01.01.2019
SQL>
Note that it'll fail if format is wrong, for example 20190231 (which is supposed to be 31st of February 2019) as there aren't 31 days in February.
If possible, change those columns' datatype to DATE.

Related

Oracle 19c - Zero padded year

I have some zero padded year DATE column values (e.g. 0018-04-30), but not all values. Most are 4 digit years (e.g. 2022). What's the most efficient way of converting to a year with century (i.e. 2018-04-30).
I'm not a Oracle guy, but what I came up with is to use a CASE statement with EXTRACT and ADD_MONTHS functions:
CASE
WHEN EXTRACT(YEAR FROM DateColumn) < 1000 THEN ADD_MONTHS(DateColumn,(12*2000))
ELSE DateColumn
END
Example:
SELECT
TO_CHAR(
CASE
WHEN EXTRACT(YEAR FROM (DATE '18-04-30')) < 1000 THEN ADD_MONTHS((DATE '18-04-30'),(12*2000))
ELSE (DATE '18-04-30')
END,'YYYY-MM-DD') AS CorrectedDate
FROM DUAL ;
Is there a better, more efficient, way of doing this?
Use an INTERVAL literal to add 2000 years to your dates:
SELECT dt + INTERVAL '2000' YEAR(4)
FROM DATE_TABLE
db<>fiddle here
Also, your CASE expression can be a bit simpler:
CASE
WHEN DateColumn < DATE '1000-01-01' THEN DateColumn + INTERVAL '2000' YEAR(4)
ELSE DateColumn
END
You may remove leading zeroes from the date using ltrim function and use RR date format element to convert the result to a date, which was introduced for y2k problem (I presume this is the source of such dates):
with dates(dt) as (
select *
from sys.odcidatelist(
date '0018-04-30',
date '3018-05-30',
date '0070-12-29'
)
)
select
to_date(
ltrim(to_char(dt, 'yyyy-mm-dd'), '0'),
'rr-mm-dd'
) as dt
from dates;
| DT |
| :--------- |
| 2018-04-30 |
| 3018-05-30 |
| 1970-12-29 |
db<>fiddle here

Oracle: Difference between DATE and TIMESTAMP(0)

What is the difference, if any, between DATE and TIMESTAMP(0) in Oracle? Both data types occupy 6 bytes, and contain date with time, but without fractional seconds and a timezone.
The TIMESTAMP was added to Oracle about 20 years after DATE to comply with ANSI standard. TIMESTAMP or TIMESTAMP(6) can hold fractions of seconds, so it is different from DATE, but is there any difference between TIMESTAMP(0) and DATE?
One is a DATE data type and one is a TIMESTAMP data type.
They both take up 7 bytes (century, year-of-century, month, day, hour, minute and second).
The DATE will be implicitly formatted by the NLS_DATE_FORMAT session parameter when it is displayed.
The TIMESTAMP will be implicitly formatted by the NLS_TIMESTAMP_FORMAT session parameter when it is displayed.
For example:
CREATE TABLE table_name (
dt DATE,
ts TIMESTAMP(0)
);
INSERT INTO table_name ( dt, ts ) VALUES ( SYSDATE, SYSTIMESTAMP );
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD"T"HH24:MI:SS';
ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD"T"HH24:MI:SS.FF6';
Then:
SELECT dt,
DUMP( dt ),
ts,
DUMP( ts )
FROM table_name;
Outputs:
DT | DUMP(DT) | TS | DUMP(TS)
:------------------ | :---------------------------------- | :------------------------- | :-----------------------------------
2020-10-08T14:21:35 | Typ=12 Len=7: 120,120,10,8,15,22,36 | 2020-10-08T14:21:36.000000 | Typ=180 Len=7: 120,120,10,8,15,22,37
Functions that require a TIMESTAMP input won't perform an implicit CAST from a DATE to a TIMESTAMP.
So:
SELECT FROM_TZ( dt, 'UTC' ) FROM table_name;
SELECT FROM_TZ( CAST( dt AS TIMESTAMP(0) ), 'UTC' ) FROM table_name;
SELECT FROM_TZ( ts, 'UTC' ) FROM table_name;
The first statement raises an ORA-00932: inconsistent datatypes: expected TIMESTAMP got DATE exception but the second and third works.
As mentioned by #WernfriedDomscheit in comments, arithmetic is also different between DATE and TIMESTAMP data types. For example:
SELECT dt - dt,
( dt - dt ) DAY TO SECOND,
ts - ts
FROM table_name
Outputs:
DT-DT | (DT-DT)DAYTOSECOND | TS-TS
----: | :------------------ | :------------------
0 | +00 00:00:00.000000 | +000000000 00:00:00
The top expression is DATE - DATE and the result is the difference in days (expressed as a NUMBER data type). However, the bottom expression is TIMESTAMP - TIMESTAMP and the result is an INTERVAL DAY TO SECOND data type. It is possible to get DATE subtraction to output an INTERVAL DAY TO SECOND data type but, as shown in the middle example, you need to explicitly state that that is the output you require.
Addition to a DATE or TIMESTAMP also has differences. For example:
SELECT dt + 1,
dt + INTERVAL '1' DAY,
ts + 1,
ts + INTERVAL '1' DAY
FROM table_name
Outputs:
DT+1 | DT+INTERVAL'1'DAY | TS+1 | TS+INTERVAL'1'DAY
:------------------ | :------------------ | :------------------ | :-------------------------
2020-10-09T19:52:21 | 2020-10-09T19:52:21 | 2020-10-09T19:52:22 | 2020-10-09T19:52:22.000000
Adding a number or an INTERVAL to both a DATE and a TIMESTAMP is both syntactically correct (but may not be as expected). If you look you will see that the output for adding a number or an interval to a date gives the same output in both cases. However, there is a difference between the output when adding a number to a timestamp compared to adding an interval to a timestamp; this is because you can only add numbers to date values and by adding a number to a timestamp then Oracle has performed an implicit cast from timestamp down to a date so:
SELECT ts + 1 FROM table_name
is effectively performing:
SELECT CAST(ts AS DATE) + 1 FROM table_name
So, if you want to perform arithmetic with a TIMESTAMP data type then use INTERVAL data types rather than numbers.
db<>fiddle here

Oracle find record if the given date is within the range

I need to get the daily schedule for the tasks done by people.
The data in my db is stored like this
sno | start_date_time | end_date_time |
---------------------------------------------------
1 |06-10-2016 09:30:00 | 06-10-2016 17:00:00 |
2 |12-10-2016 08:00:00 | 15-10-2016 13:00:00 |
My question is if i give a date for example 14-10-2016 I would like to get the second record.
I dont know how to query it since the given date does not fall into start_date_time or end_date_time
Is it possible to do that in oracle?
Try between
select * from your_table
where your_date between start_date_time and end_date_time
Example:
select * from (
select '2016-01-05' as t1, '2016-02-05' as t2
union all
select '2016-05-05' as t1, '2016-08-05' as t2
) a
where '2016-02-01' between t1 and t2
gives
t1 t2
2016-01-05 2016-02-05
Of course this also works not only for dates but also for timestamps.
Keep in mind that when comparing dates and timestamp the date is casted as a
timestamp like this '2016-01-01' -> '2016-01-01 00:00:00'
Usually start and end dates allow null values, so apart from between in PhillipsD answer you might also need nvl:
select * from your_table
where your_date between nvl(start_date_time, your_date) and nvl(end_date_time, your_date)

How to create a year column in monetdb to automatically be set with the current time

I want to create a table with default value set to the current year
CREATE TABLE Date
(
"year" smallint NOT NULL DEFAULT extract(year from current_time)
);
It throws an error:
!42000!syntax error, unexpected YEAR in: "select year"
The same command if I run using mclient it works fine.
sql>select extract(year from current_date);
+---------------------+
| second_current_date |
+=====================+
| 2015 |
+---------------------+
1 tuple (0.130ms)
sql>
sql>select "year"(NOW());
+-------------------+
| current_timestamp |
+===================+
| 2015 |
+-------------------+
1 tuple (0.380ms)
I only want the year. Is there any way to do it?
It looks like you are attempting to extract 'year' from CURRENT_TIME when you should be attempting to get it from CURRENT_DATE
Try:
CREATE TABLE Date
(
"year" smallint NOT NULL DEFAULT extract(year from current_date)
);

Creating fact table with year of 9999

I'm building a simple fact table in oracle based on a customer status where a customer has a status, 'Active' and 'Lost' and a date they started with that status and a date they ended.
A sample 3 rows would be;
CustID | status | date_start | date_end
---------------------------------------
1 | active | 1/1/13 | 1/12/14
1 | lost | 1/12/14 | 31/12/9999
2 |active | 1/12/14 | 31/12/9999
Here, cust 1 was active and then was lost. When a account status is current (as of today) the end date column is 31/12/9999. Cust 2 is active as of today
My question is, how can I bring this into a fact table?
CREATE TABLE temp AS
SELECT CS.contract_status_id , to_char(ASH.Contract_Status_Start, 'DD/MM/YYYY') AS cust_status_start_date, to_char(ASH.CONTRACT_STATUS_END, 'DD/MM/YYYY') As cust_status_end_date
FROM account_status_history ASH,
customer_status_dim CS
WHERE ASH.contract_status = CS.contract_status
Fact Table:
CREATE TABLE customer_status_fact AS
SELECT T.cust_status_start_date, T.cust_status_end_date, T.contract_status_id,
count(T.contract_status_id) AS TOTAL_ACCOUNTS
FROM temp T
GROUP BY T.cust_status_start_date, T.cust_status_end_date, T.contract_status_id
And testing it;
select sum(F.TOTAL_ACCOUNTS), CS.contract_status_txt
from customer_status_fact F, customer_status_dim CS
where F.contract_status_id = CS.contract_status_id
and F.cust_status_start_date <= sysdate
and F.cust_status_end_date = '31/12/9999'
group by CS.contract_status_txt
I can't seem to get oracle to recognise the year 9999 Any help is appreciated
and F.cust_status_end_date = '31/12/9999'
'31/12/9999' is NOT a DATE, it is a string enclosed within single-quotation marks. You must use TO_DATE to explicitly convert it into a DATE.
For example,
SQL> alter session set nls_date_format='DD/MM/YYYY HH24:MI:SS';
Session altered.
SQL> SELECT to_date('31/12/9999 23:59:59','DD/MM/YYYY HH24:MI:SS') FROM dual;
TO_DATE('31/12/9999
-------------------
31/12/9999 23:59:59
SQL>
OR,
SQL> SELECT to_date(5373484, 'J') + (1 - 1/24/60/60) FROM dual;
TO_DATE(5373484,'J'
-------------------
31/12/9999 23:59:59
SQL>
CREATE TABLE temp AS SELECT CS.contract_status_id ,
to_char(ASH.Contract_Status_Start, 'DD/MM/YYYY') AS
cust_status_start_date, to_char(ASH.CONTRACT_STATUS_END, 'DD/MM/YYYY')
As cust_status_end_date
Why would you create a table with DATEs converted to a STRING? You should let the dates as it is. You should use TO_CHAR only for display purpose. For any date calculations, let the date remain as a date.

Resources