How to convert Varchar into Date - sql

I'm trying to create a Trial Balance Report ,
and i have a field named : 'PERIOD_NAME' which stores the accounting period name,
and my question is : is it possible to convert the 'PERIOD_NAME' which is a "VARCHAR2" into "DATE"
in order to sort the months ASC , To give me this Result
jan- 16
jan- 17
Feb- 16
Feb- 17

Use TO_DATE( string, format_model, nls_settings ) assuming your PERIOD_NAME is like jan-17 (but it is unclear from your question what the exact format is):
SELECT TO_DATE( period_name, 'MON-YY', 'NLS_DATE_LANGUAGE=English' )
FROM your_table;
If you want to sort by month then year then you can use EXTRACT to get the month or year and sort on that:
SELECT *
FROM (
SELECT TO_DATE( period_name, 'MON-YY', 'NLS_DATE_LANGUAGE=English' ) AS period_date
FROM your_table
)
ORDER BY EXTRACT( MONTH FROM period_date ),
EXTRACT( YEAR FROM period_date );

I think this is what you are after? Basically, I have used to_date to convert the period name to a date and then extracted the month and year separately to use in the sort. Hope this helps!
Data
CREATE TABLE my_table
(
col1 VARCHAR2(10)
) ;
INSERT INTO my_table VALUES ('jan- 16');
INSERT INTO my_table VALUES ('jan- 17');
INSERT INTO my_table VALUES ('Feb- 16');
INSERT INTO my_table VALUES ('Feb- 17');
Solution
SELECT t.*
FROM my_table t
ORDER BY to_char(to_date(col1, 'mon- yy'), 'mm'),
to_char(to_date(col1, 'mon- yy'), 'yy')
Result
COL1
jan- 16
jan- 17
Feb- 16
Feb- 17

Related

Select 1st 6 months records from a table when the date columns datatype in varchar2

I wanted to select the 1st 6 months result from below given table. The problem is months field has store as string(Varchar2) using '2016 DEC' this format. I'm using oracle 12c.
Just convert to a date:
where to_date(col, 'YYYY-MON') < date '2016-07-01'
Or, if you want this dynamic:
select t.*
from (select t.*, min(to_date(col, 'YYYY-MON')) over () as min_date
from t
) t
where date < add_months(min_date, 6);

Avg date of birth year from single table (oracle sql)

Average date of birth from a single table where the column's date format is '01-JAN-2001'. Looking to return total count of users, and the average DOB YEAR (oracle sql)
mock table:
|User_ID|birth_date|
|123|01-JAN-2001|
|123|01-JAN-2001|
|123|01-JAN-2001|
Assuming your intention is to just average the year, you can parse it to a date, extract the year and average it as a number:
SELECT COUNT(*), AVG(EXTRACT(YEAR FROM TO_DATE(birth_date, 'dd-mon-yyyy'))
FROM users
(Oracle 12c:) Test table and data:
create table dt (
userid_ number generated always as identity ( start with 123 )
, d_ date
);
begin
for i in 1 .. 15
loop
insert into dt ( d_ ) values (
to_date ( trunc( dbms_random.value( 2446067, 2458100 ) ), 'J' )
) ;
end loop;
end;
/
SQL> select userid_, to_char( d_, 'DD-MON-YYYY' ) dob from dt;
USERID_ DOB
---------- --------------------
123 19-JUN-2000
124 06-OCT-2005
125 27-JAN-2012
126 09-JUL-2003
127 23-JUL-2010
128 07-FEB-1992
129 20-DEC-2002
130 19-MAY-2002
131 23-FEB-1990
132 26-DEC-1990
133 19-JUN-1999
134 16-DEC-1994
135 13-APR-2017
136 31-MAR-2000
137 23-MAY-1987
Query and result:
select
count(*) user_count
, trunc( avg( extract( year from d_ ) ) ) avg_dob_year
from dt ;
USER_COUNT AVG_DOB_YEAR
---------- ------------
15 2000
(Not taking "days" into account, just "years").
When using the method suggested by #mathguy
"... It can be calculated by subtracting a fixed date, like
2000/01/01, from all dates, taking the average of the differences, and
adding it to the fixed date."
... this may be a query we could start with (I'm sure it can be refined):
select
count(*) usercount
, to_date( '2000/01/01', 'YYYY/MM/DD' ) fixeddate
, avg( differences ) difftofixed
, to_date('2000/01/01', 'YYYY/MM/DD' ) + avg( differences ) fixedplusdiff
, extract( year from ( to_date('2000/01/01', 'YYYY/MM/DD' ) + avg( differences ) ) ) dob_year
from (
select
d_ - to_date( '2000/01/01', 'YYYY/MM/DD' ) differences
from dt
);
-- result
USERCOUNT FIXEDDATE DIFFTOFIXED FIXEDPLUSDIFF DOB_YEAR
15 01-JAN-00 250.6 07-SEP-00 2000
I don’t know why you’re trying to get the average date but I think it would go something like this :
“SELECT COUNT(user_id), AVG(EXTRACT(YEAR FROM TO_DATE(birth_date, 'dd-MM-yyyy'))
FROM users”

JOIN tables ON DATE = NUMBER?

I am trying to join two tables in Oracle SQL. One table has a DATE data type which represents a date(go figure) the other has an NUMBER data type which represents a month. I need to join the tables on the DATE's month and the NUMBER. I tried TO_CHAR() but it didn't work. Any suggestions?
Oracle's EXTRACT() function may do the trick ( https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions050.htm ). Suppose we have 2 tables, populated with test data, like so:
create table numbers_ (num_ number);
create table dates_ (date_ date);
begin
for i in 1 .. 12
loop
insert into numbers_ values (i);
end loop;
insert into dates_ values ('15-JUL-2017');
insert into dates_ values ('16-AUG-2017');
insert into dates_ values ('17-SEP-2017');
end;
/
We can use EXTRACT to get the "months" from the dates_ table:
select extract (month from date_) from dates_;
EXTRACT(MONTHFROMDATE_)
7
8
9
Use the "extracted" months for joining the tables:
select *
from
numbers_ N,
( select extract( month from date_ ) month from dates_ ) D
where N.num_ = D.month;
-- output
NUM_ MONTH
7 7
8 8
9 9
If you need more columns from the dates_ table, add them into the subquery (and to the main SELECT clause). Example:
select
N.num_
, D.date_
, D.month
from
numbers_ N,
( select
extract( month from date_ ) month
, date_
from dates_ ) D
where N.num_ = D.month;
(See also: dbfiddle)
Or - better (as #Wernfried Domscheit suggested):
select
N.num_
, D.date_
from
numbers_ N join dates_ D
on extract(month from D.date_) = N.num_ ;

Date period with breaks: make 3 rows out of 2

Edit: added an id, to make it more graspable
I stumbled over this problem a couple of times and always solved it per PL/SQL, but I am wondering, if there is a SQL-solution.
There is a table with a from_date and a to_date. The data in there is seamless for every to_date, there is a new row with a from_date on the next day.
create table test_date
(
id number,
from_date date,
to_date date
)
/
insert into test_date values(1, to_date('01022003', 'ddmmyyyy'), to_date('28022003', 'ddmmyyyy'))
/
insert into test_date values(2, to_date('01032003', 'ddmmyyyy'), to_date('31032003', 'ddmmyyyy'))
/
There is another table, which breaks this time periods.
create table test_date2
(
id number,
from_date date,
to_date date
)
/
insert into test_date2 values(3, to_date('05022003', 'ddmmyyyy'), to_date('10022003', 'ddmmyyyy'))
/
So, I want a view, that shows this time periods and the "breaks" in different columns, but this should also be seamless after the "break" with test_date2 it should go right on with the data in test_date and I can't get that going:
select typ, id, from_date, decode(typ, 1, decode(to_date+1, lead_from_date, to_date, lead_from_date-1), to_date) to_date
from(
select typ, id, from_date, to_date, lead(from_date) over (order by from_date, typ) lead_from_date
from
(select 1 typ, id, from_date, to_date
from test_date t
union all
select 2 typ, id, from_date, to_date
from test_date2 t2
) a
)
What I get here is
1 1 01/02/2003 04/02/2003
2 3 05/02/2003 10/02/2003
1 2 01/03/2003
the period between 11/02/2003 and 28/02/2003 (for the row in test_data with id=1) is missing.
So, what I want, is this:
1 1 01/02/2003 04/02/2003
2 3 05/02/2003 10/02/2003
1 1 11/02/2003 28/02/2003
1 2 01/03/2003
I think this is what you're after; your're not getting the same answer because you're not generating the full list of dates. If you normalise your data in order to get a unique list of dates you can then use LEAD() or LAG() to find the next/previous date, and re-generate your list.
I use UNPIVOT here to transform the from_date and to_date into a single column but 4 unions will provide the same result:
with all_tables as (
select *
from test_date
union all
select *
from test_date2
)
, all_dates as (
select dt
from all_tables
unpivot ( dt for dates in ( from_date, to_date ))
)
select dt
, lead(dt) over (order by dt) as to_date
from all_dates;
DT TO_DATE
---------- ----------
01/02/2003 05/02/2003
05/02/2003 10/02/2003
10/02/2003 28/02/2003
28/02/2003 01/03/2003
01/03/2003 31/03/2003
31/03/2003
6 rows selected.

select records with a 6 month interval

I want to select with an oracle sql statement the records with a 6 month time interval.
Example
01/06/2011 AMOUNT
01/12/2011 AMOUNT
01/06/2012 AMOUNT
01/12/2012 AMOUNT
And so on
How can I do this with oracle sql?
select ADD_MONTHS(trunc(sysdate), (rownum - 1) * 6) some_date
from dual
connect by level <= 5;
SOME_DATE
-----------
18.04.2014
18.10.2014
18.04.2015
18.10.2015
18.04.2016
WITH got_r_num AS
(
SELECT t.* -- OR WHATEVER YOU WANT
, DENSE_RANK () OVER ( PARTITION BY TRUNC (created_date, 'MONTH')
ORDER BY TRUNC (created_date) -- DESC
) AS r_num
FROM test_table
WHERE MOD ( MONTHS_BETWEEN ( TRUNC (SYSDATE)
, TRUNC (created_date)
)
, 6
) = 0
)
SELECT * -- or list all columns except r_num
FROM got_r_num
WHERE r_num = 1
;
Have a look here please .
If you need to sum of all records of 6 months in the AMOUNT field:
You can subquery with sum function and query with CONNECT BY LEVEL
SELECT x AS l_date,
(
SELECT sum(your_data)
FROM your_table
WHERE table_date >= x
AND table_date < add_months(x,6)
)AMOUNT
FROM(
SELECT add_months(to_date('01/06/2011','dd/mm/yyyy'),(LEVEL-1)*6) x
FROM dual
CONNECT BY LEVEL <= 4
);