Oracle SQL Create Date from Parts - sql

I have two date fields in a database called "EFFECTIVE_DATE" and "POINT_DATE"
How do I create a new date field, where the date is made up from the year of "EFFECTIVE_DATE", the month of "POINT_DATE" and the day of "POINT_DATE" ?
I would normally use Datefromparts, but this is an Oracle Database not Microsoft
Kind Regards

Here's an approach using ADD_MONTHS and EXTRACT(YEAR FROM ....). You simply add or subtract the needed number of months (always a multiple of 12, since you are only changing the year). Unlike the TO_CHAR / TO_DATE solutions, this approach handles leap days (Feb. 29). On the other hand, be advised that changing the date from 28 Feb. 2003 to the year 2012 will change it to Feb. 29 (ADD_MONTHS changes the last day of a month to the last day of the resulting month).
with
inputs as (
select date '2013-03-22' as effective_date,
date '2017-08-14' as point_date
from dual
)
-- end of TEST data (do not include in the solution!)
select effective_date, point_date,
add_months(point_date, 12 * ( extract (year from effective_date) -
extract (year from point_date) )
) as mixed_date
from inputs;
EFFECTIVE_DATE POINT_DATE MIXED_DATE
-------------- ---------- ----------
03/22/2013 08/14/2017 08/14/2013

Hmmm . . . this produces a nice string in the YYYY-MM-DD format:
select to_char(effective_date, 'YYYY') || '-' || to_char(point_date, 'MM-DD')
And this parses it back to a date:
select to_date(to_char(effective_date, 'YYYY') || '-' || to_char(point_date, 'MM-DD'), 'YYYY-MM-DD')
Note: You might want to be careful about Feb 29th.

Assuming all coulmns are DATE data types, you can use this one
TO_DATE(TO_CHAR(EFFECTIVE_DATE, 'YYYY') || TO_CHAR(POINT_DATE, 'MMDD'), 'YYYYMMDD')
Be aware of leap years!

Related

How to get Year/Week according to ISO?

I have the below code, which gives week 202153. However, I would need to have, instead of the first week of year 2021 as 202153, as 202053.
Is there any built-in functionality to address this?
SELECT
to_char(TO_DATE('20210104', 'YYYYMMDD') - 3, 'YYYYIW') AS yearweek
FROM
dual
Expected output: 202053. I could simply decode the value, but I'm hoping for a more robust solution for future years.
Use IYYY (ISO-year) rather than the YYYY (calendar year) format model.
SELECT TO_CHAR( DATE '2021-01-04' - 3, 'IYYYIW') AS yearweek
FROM DUAL
Which outputs:
YEARWEEK
202053
db<>fiddle here

SQL Query to get the first day of the month passed in the parameter of the current year

I want to get the first day and last day of the month passed in the parameter.
I tried to use the below query, but it is erroring out.
select LAST_DAY(to_date(to_char(('01'||:P_MONTH||'2020'),'DDMMYYYY'),'YYYYMM'))
FROM DUAL
I want to use this in a query
select * from
GL_CODE_TAB
where effective_start_date = LAST_DAY(to_date(to_char(('01'||:P_MONTH||'2020'),'DDMMYYYY'),'YYYYMMDD'))
and search the table GL_CODE_TAB for the first/last day of the month I pass, e.g. if I pass "May"
select * from
GL_CODE_TAB
where effective_start_date = LAST_DAY(to_date(to_char(('01'||'May'||'2020'),'DDMMYYYY'),'YYYYMMDD'))
which should do a search like
select * from
GL_CODE_TAB
where effective_start_date = '20200531'
Something is off in your logic. You don't need to convert strings to strings. So, I am thinking:
SELECT LAST_DAY(TO_DATE('01' || :P_MONTH || '2020', 'DDMMYYYY'))
FROM DUAL
The pattern MM is looking for a numerical month. (You also have the format pattern 'DDMMYYYY' twice in your query; I am not sure why.)
It so happens that that Oracle defaults the year to the current year and the day of the month to the current month. So, you don't even need to mess around with strings:
SELECT LAST_DAY(TO_DATE(:P_MONTH , 'MM'))
FROM DUAL;
The above assumes a numerical value for your parameter. If you want to handle a string, then:
SELECT LAST_DAY(TO_DATE('01' || :P_MONTH || '2020', 'DDMONYYYY'))
FROM DUAL
Or:
SELECT LAST_DAY(TO_DATE(:P_MONTH , 'MON'))
FROM DUAL;
If you want to pass the full month name, use MONTH instead of MON.
The inner call to TO_CHAR() is the problem. Just use TO_DATE() as follows, with the proper format specifier as second argument:
SELECT LAST_DAY(TO_DATE('01 ' || :P_MONTH || ' 2020'),'DD MONTH YYYY'))
FROM DUAL
It is unclear whether the parameter is a full or abbreviated month name (May is ambiguous in that regard). I assumed it's the former. If that's a short name, then use 'DD MON YYYY' instead.
In your query:
SELECT *
FROM GL_CODE_TAB
WHERE effective_start_date = LAST_DAY(TO_DATE('01 ' || :P_MONTH || ' 2020'),'DD MONTH YYYY'))
If you need 20200531 in the where clause, I think you want
select to_char(last_day(to_date(('01'||'May'||'2020'),'DDMMYYYY')), 'YYYYMMDD')
from dual;

More on extracting year from date - Oracle

I have a question about selecting year from a date. This is in Oracle database 12c.
Given that SELECT trunc(SYSDATE) FROM DUAL; returns 02/06/2020
These work proper and return current year of 2020 -
SELECT EXTRACT(YEAR FROM trunc(SYSDATE)) FROM DUAL;
SELECT TO_CHAR(trunc(SYSDATE,'YYYY')) FROM DUAL;
These do not work and give error -
SELECT EXTRACT(YEAR FROM '02/06/2019') FROM DUAL;
Gives error: ORA-30076: invalid extract field for extract source
SELECT TO_CHAR('02/06/2019','YYYY') FROM DUAL;
Gives error: ORA-01722: invalid number
The same format is being passed with sysdate and hard coded date of 02/06/2019. Why is it that one works and the other does not?
I know I could just select 2019 from dual but that is not the point or use case here.
You can't extract year from a string (which '02/06/2019' is). First convert it to date:
SQL> SELECT EXTRACT(YEAR FROM to_date('02/06/2019', 'dd/mm/yyyy')) year FROM DUAL;
YEAR
----------
2019
SQL>
Or, if you know that last 4 digits are valid year, then
SQL> select substr('02/06/2019', -4) year from dual;
YEAR
----
2019
SQL>
It comes down to the data type being passed. sysdate by default is a DATE field. A hard date like '02/06/2020' by default is considered a string.
To get around that, just cast the string as a date. All good.
SELECT TO_CHAR(cast('6-feb-2019' as date),'YYYY') FROM DUAL;

Extract month and year from date in oracle

what is the query for extract month and year from full date.
my data is like this: 1/29/2008
I tried this query:
select ID_NO, CHECKED_DATE, to_date(TO_CHAR(CHECKED_DATE, 'MON-YYYY'), 'MON-YYYY') AS A
from Doctor_Checkup;
but It will give output: 1/1/2008
Expected output: 1-2008
If the field is already a date column, you can simply cast it to the format you want:
select ID_NO,CHECKED_DATE,ltrim(TO_CHAR(CHECKED_DATE,'mm-yyyy'),'0') AS A from Doctor_Checkup;
If it is a text column, you will need to cast to a date with format first:
select ID_NO,CHECKED_DATE,ltrim(TO_CHAR(TO_DATE(CHECKED_DATE,'dd/mm/yyyy'),'mm-yyyy'),'0') AS A from Doctor_Checkup;
A date does not have a format - it is stored internally to the database as 7-bytes (representing year, month, day, hour, minute and second) and it is not until whatever user interface you are using (i.e. SQL/Plus, SQL Developer, Java, etc) tries to display it to you, the user, and converts it into something you would find meaningful (usually a string) that the date has a format.
One thing to note is that a date always has the year, month, day, hour, minute and second components. Doing:
to_date(TO_CHAR(CHECKED_DATE, 'MON-YYYY'), 'MON-YYYY')
Is effectively the same as doing:
TRUNC( Checked_Date, 'MM' )
and will still have a day, hour, minute and second component but will have been truncated to midnight of the first day of the month. The user interface may just be have its preferences set to not display the time component (but the date will still have one).
What you want to do is convert the date to a formatted string:
select ID_NO,
CHECKED_DATE,
TRIM( LEADING '0' FROM TO_CHAR( CHECKED_DATE, 'MM-YYYY') ) AS A
from Doctor_Checkup;
or
select ID_NO,
CHECKED_DATE,
EXTRACT( MONTH FROM CHECKED_DATE )
|| '-' || EXTRACT( YEAR FROM CHECKED_DATE ) AS A
from Doctor_Checkup;
You want a string representing month and year in the format [M]M-YYYY. Oracle's TO_CHAR only supports MM-YYYY, though, so you'd have to get rid of a leading zero if any.
Two solutions:
trim(leading '0' from to_char(checked_date, 'mm-yyyy'))
or
extract(month from checked_date) || '-' || extract(year from checked_date) from dual
To get 1-2008 format use the following format with trimming leading zeroes:
select ID_NO,CHECKED_DATE,ltrim(TO_CHAR(CHECKED_DATE,'MM-YYYY'),'0') AS A from Doctor_Checkup;
SELECT ID_NO, CHECKED_DATE FROM DOCTOR_CHECKUP EXTRACT(MONTH FROM CHECKED_DATE) IN (6) AND EXTRACT(YEAR FROM CHECKED_DATE) IN (2019);
6 MEANS THE MONTH AND 2019 IS THE YEAR
LTRIM(TO_CHAR(TO_DATE(<date_field>,'YYYYMMDD'),'YYYY-MM'),'09'))

How to convert a sysdate month value to number in oracle?

Im trying to return the CARDS of my CARD table that will expire in the next month. But the problem is that the table has two columns to represent the card date. The columns are EXPIREDAY and EXPIREMONTH ,both are numbers. So when i do that query i get an error:
select * from CARD WHERE EXPIREDAY <= sysdate - interval '2' DAY;
//Oracle error: ORA-00932: inconsistent datatypes: expected NUMBER got DATE
Is there a way to convert the sysdate - interval '2' DAY as Number data type?
Thanks!
If you want to compare the values as strings you can use this to convert the SYSDATE
SELECT TO_CHAR(sysdate, 'MM') || TO_CHAR(sysdate, 'DD') MONTH_NUM FROM DUAL
-- gives you "0922"
and this for your numeric columns which will pad with leading zeros if you only have a single digit
SELECT TO_CHAR(9, 'FM00') || TO_CHAR(22, 'FM00') MONTH_NUM FROM DUAL
-- also gives you "0922"
If you have control over the table schema it would be best practise to store both the DAY and MONTH values in a single numeric field, so that 9-SEP would be stored in this column as the numeric value 0922 where the month is first so that the natural ordering is used.
A simple and not necessarily very efficient approach is to convert the day and month values into an actual date, using to_date(), and then compare that with your target date range:
select * from card
where to_date(lpad(expireday, 2, '0')
||'/'|| lpad(expiremonth, 2, '0'), 'DD/MM')
between sysdate and add_months(sysdate, 1);
Which appears to work. But this will have problems if the dates span the end of the year. Because your table doesn't specify the year, you either have to work one out, or allow to_date to default it to the current year. And if you let it default then it won't work. For example, if you have values for December and January in your table, and run this query in December, then the January dates will be seen as January 2014, and won't be counted as being in the next month. So you'll need to do more to pick the right year.
This treats any month numbers before the current one as being next year, which may be good enough for you as you only have a one-month window:
select * from card
where to_date(lpad(expireday, 2, '0')
||'/'|| lpad(expiremonth, 2, '0')
||'/'|| (extract(year from sysdate) +
case when expiremonth < extract(month from sysdate) then 1 else 0 end),
'DD/MM/YYYY')
between sysdate and add_months(sysdate, 1);
SQL Fiddle using a date range from December to January.
And you can see the ways the two columns are being combined to form a date in this Fiddle.
As so often, the moral is... store things as the right data type. Store dates as dates, not as string or numbers.
Im trying to return the CARDS of my CARD table that will expire in the next month. But the problem is that the table has two columns to represent the card date.
Assuming:
you are using floating months (say: from 23 dec. to 23 jan.) and
your table somehow only contains one (floating ?) year of data
Why can't you use simple arithmetics? Like that:
-- some constant definitions for testing purpose
with cst as (
select EXTRACT(DAY from TO_DATE('23/12','DD/MM')) as theDay,
EXTRACT(MONTH from TO_DATE('23/12','DD/MM')) as theMonth
from dual)
-- the actual query
select card.* from card,cst
where (expiremonth = theMonth AND expireday > theDay)
or (expiremonth = 1+MOD(theMonth,12) AND expireday <= theDay);
-- ^^^^^^^^^^^^^^^^^^
-- map [01 .. 12] to [02 .. 12, 01] (i.e.: next month)
This will simply select all "pseudo-dates" from tomorrow to the end of the month, as well as any one before (and including) the current day# next month.
See this example.
For something a little bit more generic, but probably more efficient than converting all your values TO_DATE, you might want to try that:
-- the calendar is the key part of the query (see below)
with calendar as (
select extract(month from sysdate + level) as theMonth,
extract(day from sysdate + level) as theDay
from DUAL connect by ROWNUM <= 8)
-- ^
-- adjust to the right number of days you are looking for
select card.* from card join calendar
on expiremonth = theMonth and expireDay = theDay
The idea here is to simply build a calendar with all the upcoming days and then join your data table on that calendar. See an example here.
Try using to_char(sysdate - interval '2' DAY,'ddmmyyyy') to convert to character type. The date format('ddmmyyyy') will depend of the value of expiredate