Oracle date extract and concatenate - sql

I have to take a date (dd-mon-yyyy) and change the year based on a user input. How do I extract the day and month and add the year while keeping the format?
For example, it is currently 01-JAN-2015, and the user wants it to be 01-JAN-2016. I currently am doing:
SELECT EXTRACT(DAY FROM contract_year) || EXTRACT(MONTH FROM contract_year) || '2016'
FROM table
WHERE program = programid
And my output is 112016, but I want 01-JAN-2016. Any suggestions?

Try this
SELECT to_char(add_months('01-JAN-2015', 12*1),'dd-MON-yyyy') -- replace 1 with n where n = years
from dual
If you want to add use given year to current date/month, use this
with your_table as(
select '01-JAN-2015' as contract_year from dual
)
select to_date(substr(to_char(contract_year),1,7)||'2017','DD-MON-YYYY') from your_table
If your are not seeing output as required, it means that you have to change your client setting. Try running this in SQL Plus window.

Combine to_date and to_char:
SELECT to_date(to_char(contract_year,'dd-mm-') || '2016','dd-mm-yyyy')
FROM table
WHERE program = programid

It's not entirely clear to me whether contract_year is a date or a string.
If it's a date:
select to_char(contract_year, 'DD-MON-') || '2016'
from table
If it's a string in the format DD-MON-YYYY then:
select to_char(to_date(contract_year, 'DD-MON-YYYY'), 'DD-MON-') || '2016'
from table

Related

Extracting data from only the year

I have data in a table in SQL with dates, but how do I select only those that happen in 2021. (The dates look like 31-oct-2020) in the table. The dates are the actual date variable, not just text.
You should avoid storing your dates as text, but rather should use a proper date column. That being said, you may check the right 4 characters of the date string:
SELECT *
FROM yourTable
WHERE RIGHT(date_col, 4) = '2021';
If the column be an actual date type then use:
SELECT *
FROM yourTable
WHERE date_col >= '2021-01-01' AND date_col < '2022-01-01';
I suspect that your DB is Oracle after checking out your previous post. Then you can use
SELECT *
FROM yourTable
WHERE EXTRACT(year FROM dt) = 2021
or
SELECT *
FROM yourTable
WHERE TRUNC(dt,'YYYY') = date'2021-01-01'
or
SELECT *
FROM yourTable
WHERE dt BETWEEN date'2021-01-01' AND date'2021-12-31'
You can benefit the index if there's one on the date column(namely dt) by using the last SELECT statement
If using MSSQL, you can leverage the YEAR(...) to extract the year from a date.
Replace and , with the table name and date column name respectively.
select * from <tablename> where year(<datecolumn>) = 2021

Year in DD/MON/YY converts to 1943 and 2043 in 2 different tables with the same data

I am trying the same SQL on 2 different tables in the same database.
SELECT date_of_birth_1 from Table1 where id = '1111';
The output is 31/DEC/43.
SELECT date_of_birth_2 from Table2 where id = '1111';
The output is 31/DEC/43 again.
But when I run
SELECT extract(year from date_of_birth_1) from Table1 where id = '1111';
The output is 1943.
And when I run
SELECT extract(year from date_of_birth_2) from Table2 where id = '1111';
The output is 2043.
I don't understand what is going on, could you please help me. I want both the tables to use the same reference year which is 1900.
Edit: This happens only for some dates.
select EXTRACT(year FROM TO_DATE('01/AUG/43')) from dual;
The output is 2043.
select EXTRACT(year FROM TO_DATE('04/MAR/53')) from dual;
The output is 1953.
By default, Oracle is only showing the last two digits of the year. In one table, the date would seem to be 1943-12-31 and in the other 2043-12-31.
You can see the full date using to_char():
select to_char(dob, 'YYYY-MM-DD')
If you need to fix the data, you can do something like:
update t
set dob = add_months(dob, -12 * 100)
where dob > <whatever threshold you want here>

Using CONCAT in combination with SUBSTR

I want to write a select that would return me a distinct years only (2018, 2017,2016).
I have column AS_OF_DATE in table HISTORY.
Here are some example values of AS_OF_DATE:
31-05-18,
31-04-17,
31-07-16,
...
This is what I tried, but it doesn't work:
SELECT CONCAT('20',DISTINCT SUBSTR(AS_OF_DATE, 7, 2) FROM HISTORY
I used CONCAT to add 20 in front of the result and SUBSTR that would start at the 7th string and would be 2 strings long (so I get 18,17,16...)
try like below
SELECT DISTINCT '20' || SUBSTR(AS_OF_DATE, 7, 2) FROM HISTORY
Normally, you would do this with extract() or to_char():
select extract(year from as_of_date) as yyyy
or
select to_char(as_of_date, 'YYYY') as yyyy
This assumes that as_of_date is a date, which is should be.
You can add select distinct if you want a result set with the distinct years.
You can use
SELECT CONCAT('20', SUBSTR(AS_OF_DATE, -2) ) as "Years"
FROM HISTORY
GROUP BY SUBSTR(AS_OF_DATE, -2)
ORDER BY "Years"
Demo
this will work:
select distinct extract(year from as_of_date) from History;
the extract function takes off the as_of_date provided it is a date datatype and distinct select only one for dates which are multiple times in the extract.

UPDATE month and year to current but leave day

I have situation in Oracle DB where I need to UPDATE every month some dates in table following this condition:
1) If date in table like '03.06.2017' UPDATE to '03.11.2017'
2) If date in table like '29.06.2016' UPDATE to '29.11.2017'
2) If date in table like '15.02.2016' UPDATE to '15.11.2017'
So basically always UPDATE part of date(month, year) to current month/year but always leave day as it is.
Edit:
It will be all months from 1-12 not only June. I need to do something like this... UPDATE table SET date = xx.(month from sysdate).(year from sysdate) WHERE... xx (day) leave as it is in DB.
Br.
You can use MONTHS_BETWEEN to determine how many months you need to add and then use the ADD_MONTHS function:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE dates ( value ) AS
SELECT DATE '2017-06-03' FROM DUAL UNION ALL
SELECT DATE '2016-06-29' FROM DUAL UNION ALL
SELECT DATE '2016-02-15' FROM DUAL UNION ALL
SELECT DATE '2016-03-31' FROM DUAL;
Update:
UPDATE dates
SET value = ADD_MONTHS(
value,
CEIL( MONTHS_BETWEEN( TRUNC( SYSDATE, 'MM' ), value ) )
);
Query 1:
SELECT * FROM dates
Results:
| VALUE |
|----------------------|
| 2017-11-03T00:00:00Z |
| 2017-11-29T00:00:00Z |
| 2017-11-15T00:00:00Z |
| 2017-11-30T00:00:00Z | -- There are not 31 days in November
Probably you want
update your_table
set this_date = add_months(this_date, 5)
where ...
This will add five months to the selected dates.
Your edited question says you want to update all the dates to the current month and year; you can automate it like this ...
update your_table
set this_date = add_months(this_date,
months_between(trunc(sysdate,'mm'), trunc(this_date, 'mm')))
-- or whatever filter you require
where this_date between trunc(sysdate, 'yyyy') and sysdate
/
Using month_between() guarantees that you won't get invalid dates such as '2017-11-31'. You say in a comment that all the dates will be < 05.mm.yyyy but your sample data disagrees. Personally I'd go with a solution that doesn't run the risk of data integrity issues, because the state of your data tomorrow may will be different from its state today.
Check out the LiveSQL demo.
I would start off with something like this to get my dates and then craft an update from it (substitute old_date with your date column and source_table with the table name):
select old_date, to_char(sysdate, 'YYYY-MM-') || to_char(old_date, 'DD') from source_table;

For loop in a select statement?

Can you use a for loop in a select statement (specifically, a case) in order to define multiple values?
Here's my code:
select case when sysdate in ( declare k DATE:= '2015-01-01',
Begin
FOR i in 1...365 LOOP
k:=sydate +1;
END LOOP;
END;
) then '1' else 'n/a' end FISCAL_YEAR from dual
There may be multiple issues with my syntax..I am trying to say that when today's date is within the year, then a '1' shows up in the FISCAL_YEAR column. This would be a much cleaner way then using a case statement for each day, so any help would be appreciated. Thank you.
You probably just need something like:
select case when to_char(sysdate,'YYYY') = '2015' then 1 else 0 end
from dual
No idea what you're trying to do with that "nested" pl/sql block O.o
Just fill in "the year" in place of 2015 as you need. (ie a variable as you need).
If that's not what you need, please provide a small sample showing input and output expected.
There are easier ways of doing this:
SELECT CASE WHEN TRUNC(sysdate, 'YEAR') = DATE'2015-01-01' THEN '1' ELSE 'n/a' END AS fiscal_year
FROM dual;
or you could use DECODE():
SELECT DECODE(TRUNC(sysdate, 'YEAR', DATE'2015-01-01', '1', 'n/a') AS fiscal_year
FROM dual;
Instead of TRUNC(sysdate, 'YEAR') you could use TO_CHAR(sysdate, 'YYYY') etc.
There are two places you can put your check, depending on which rows you want in your result set. If you want all rows back to a specific date which may be beyond the current FY, use a check in a case in the selection list.
select <whatever>,
case when DateField between date '2015-01-01'
and Add_Months( date '2015-01-01', 12 ) - 1
then 'Y' else 'N' end
as InFY2015
from DataSource;
If you want the result set to contain only rows with dates within the FY, put the check in the where clause.
select <whatever>
from DataSource
where DateField between date '2015-01-01'
and Add_Months( date '2015-01-01', 12 ) - 1;
In the latter query, there is no need for an indicator that says "this row in FY2015" because they all are.
I think previous answer with time interval is the right solution to your problem.
However to answer your question whether there is a way how to loop inside select statement: No, but there is a way how to generate set of increasing numbers, so this code works like your intended pseudocode:
select case when a.c>0 then '1' else 'n/a' end FISCAL_YEAR
from (select count(*) c from (
SELECT ROWNUM+to_date('2015-01-01','yyyy-mm-dd')-1 n
FROM ( SELECT 1
FROM dual
CONNECT BY LEVEL <= 365)) lo
where trunc(sysdate)=lo.n) a