Converting YYWW to date in Oracle - sql

I'm having some trouble with converting week number to date in oracle 11g.
The thing is that i have two columns *id|week_code* and I'd like to convert it to first day (monday) of this week.
So let's say that i have 1202 which is 2nd week of 2012, so the result would be like: 09-January-2012.
Thanks in advance.

Try to use this approach: (not tested).
with date_wk as (
select to_date('01/01/'||'20'||substr(%input%,1,2), 'MM/DD/
RRRR') + rownum - 4 dt
from dual
connect by level <= 366
),
wk_dt as (
select dt, to_number(to_char(dt, 'iw')) wk_of_yr
from date_wk
)
select min(dt)
from wk_dt
where wk_of_yr = substr(%input%,3,2)
to get the date you desire, and change it format to 'DD-MONTH-YYYY'.

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);

How do I generate all the dates of Sunday between 2 dates in oracle sql?

How do I generate all the dates of Sunday between 2 dates in oracle SQL?
Example if I want all the Sundays between "01/10/2018" and "31/12/2018" the output
will be:
07/10/2018
14/10/2018
21/10/2018
...
30/12/2018
Also how do I generate all the dates between 2 dates?
Example: from "01/12/2018" to "31/12/2018"
Output will be:
01/12/2018
02/12/2018
03/12/2018
...
31/12/2018
Here's how; the CTE (dates) creates a "calendar" of all dates starting from 2018-10-01, for number of days between 2018-10-01 and 2018-12-31. This answers your 2nd question.
For the 1st question, using TO_CHAR function with appropriate format mask (dy) and date language (because, if I didn't use it, you'd get Croatian names as that's my default language), select all Sundays.
SQL> with dates as
2 (select date '2018-10-01' + level - 1 datum
3 from dual
4 connect by level <= date '2018-12-31' - date '2018-10-01' + 1
5 )
6 select datum
7 From dates
8 where to_char(datum, 'dy', 'nls_date_language = english') = 'sun';
DATUM
-----------
07-oct-2018
14-oct-2018
21-oct-2018
28-oct-2018
04-nov-2018
11-nov-2018
18-nov-2018
25-nov-2018
02-dec-2018
09-dec-2018
16-dec-2018
23-dec-2018
30-dec-2018
13 rows selected.
SQL>
The following query does it.
First generate rows using connect by clause, each of the row will have the value of "level" column incremented by 1
Also get the day_of_week per date
Filter out the records where day_of_week='sun'
with data
as (
select to_date('1/10/2018','dd/mm/yyyy')+level as sun_day
,to_char(to_date('1/10/2018','dd/mm/yyyy')+level,'dy') day_of_week
from dual
connect by level<=to_date('31/12/2018','dd/mm/yyyy') - to_date('1/10/2018','dd/mm/yyyy')
)
select sun_day
from data
where day_of_week='sun'
For the second part of the query simply remove the filter on the day_of_week
with data
as (
select to_date('1/10/2018','dd/mm/yyyy')+level as sun_day
,to_char(to_date('1/10/2018','dd/mm/yyyy')+level,'dy') day_of_week
from dual
connect by level<=to_date('31/12/2018','dd/mm/yyyy') - to_date('1/10/2018','dd/mm/yyyy')
)
select sun_day
from data;
select
to_char(date,'DD-MON-YYYY')
from
dates
where
(trunc(date) >= '01/12/2018'
and
trunc(date)<= '31/12/2018')
and
to_char(date,'DAY') ='SUNDAY';

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;

SQL query for all the days of a month

i have the following table RENTAL(book_date, copy_id, member_id, title_id, act_ret_date, exp_ret_date). Where book_date shows the day the book was booked. I need to write a query that for every day of the month(so from 1-30 or from 1-29 or from 1-31 depending on month) it shows me the number of books booked.
i currently know how to show the number of books rented in the days that are in the table
select count(book_date), to_char(book_date,'DD')
from rental
group by to_char(book_date,'DD');
my questions are:
How do i show the rest of the days(if let's say for some reason in my database i have no books rented on 20th or 19th or multiple days) and put the number 0 there?
How do i show the number of days only of the current month so(28,29,30,31 all these 4 are possible depending on month or year)... i am lost . This must be done using only SQL query no pl/SQL or other stuff.
The following query would give you all days in the current month, in your case you can replace SYSDATE with your date column and join with this query to know how many for a given month
SELECT DT
FROM(
SELECT TRUNC (last_day(SYSDATE) - ROWNUM) dt
FROM DUAL CONNECT BY ROWNUM < 32
)
where DT >= trunc(sysdate,'mm')
The answer is to create a table like this:
table yearsmonthsdays (year varchar(4), month varchar(2), day varchar(2));
use any language you wish, e.g. iterate in java with Calendar.getInstance().getActualMaximum(Calendar.DAY_OF_MONTH) to get the last day of the month for as many years and months as you like, and fill that table with the year, month and days from 1 to last day of month of your result.
you'd get something like:
insert into yearsmonthsdays ('1995','02','01');
insert into yearsmonthsdays ('1995','02','02');
...
insert into yearsmonthsdays ('1995','02','28'); /* non-leap year */
...
insert into yearsmonthsdays ('1996','02','01');
insert into yearsmonthsdays ('1996','02','02');
...
insert into yearsmonthsdays ('1996','02','28');
insert into yearsmonthsdays ('1996','02','29'); /* leap year */
...
and so on.
Once you have this table done, your work is almost finished. Make an outer left join between your table and this table, joining year, month and day together, and when no lines appear, the count will be zero as you wish. Without using programming, this is your best bet.
In oracle, you can query from dual and use the conncect by level syntax to generate a series of rows - in your case, dates. From there on, it's just a matter of deciding what dates you want to display (in my example I used all the dates from 2014) and joining on your table:
SELECT all_date, COALESCE (cnt, 0)
FROM (SELECT to_date('01/01/2014', 'dd/mm/yyyy') + rownum - 1 AS all_date
FROM dual
CONNECT BY LEVEL <= 365) d
LEFT JOIN (SELECT TRUNC(book_date), COUNT(book_date) AS cnt
FROM rental
GROUP BY book_date) r ON d.all_date = TRUNC(r.book_date)
There's no need to get ROWNUM involved ... you can just use LEVEL in the CONNECT BY:
WITH d1 AS (
SELECT TRUNC(SYSDATE, 'MONTH') - 1 + LEVEL AS book_date
FROM dual
CONNECT BY TRUNC(SYSDATE, 'MONTH') - 1 + LEVEL <= LAST_DAY(SYSDATE)
)
SELECT TRUNC(d1.book_date), COUNT(r.book_date)
FROM d1 LEFT JOIN rental r
ON TRUNC(d1.book_date) = TRUNC(r.book_date)
GROUP BY TRUNC(d1.book_date);
Simply replace SYSDATE with a date in the month you're targeting for results.
All days of the month based on current date
select trunc(sysdate) - (to_number(to_char(sysdate,'DD')) - 1)+level-1 x from dual connect by level <= TO_CHAR(LAST_DAY(sysdate),'DD')
It did works to me:
SELECT DT
FROM (SELECT TRUNC(LAST_DAY(SYSDATE) - (CASE WHEN ROWNUM=1 THEN 0 ELSE ROWNUM-1 END)) DT
FROM DUAL
CONNECT BY ROWNUM <= 32)
WHERE DT >= TRUNC(SYSDATE, 'MM')
In Oracle SQL the query must look like this to not miss the last day of month:
SELECT DT
FROM(
SELECT trunc(add_months(sysdate, 1),'MM')- ROWNUM dt
FROM DUAL CONNECT BY ROWNUM < 32
)
where DT >= trunc(sysdate,'mm')

How to compare date (month/year) with another date (month/year)

How to compare parts of date in Informix DBMS:
I wrote the following query but I get a syntax error:
select id,name from h_history
where ( h_date::DATETIME YEAR TO MONTH >= 2/2012 )
and ( h_date::DATETIME YEAR TO MONTH <= 1/2013 )
I wanna to compare with year and month
How to do this?
If you're going to use the DATETIME route, you need to format the values correctly. As it is, you've written:
select id,name from h_history
where ( h_date::DATETIME YEAR TO MONTH >= 2/2012 )
and ( h_date::DATETIME YEAR TO MONTH <= 1/2013 )
The 2/2012 is an integer division equivalent to 0, and there's no implicit cast from integers to datetime or vice versa.
You could write:
-- Query 1
SELECT id, name
FROM h_history
WHERE (h_date::DATETIME YEAR TO MONTH >= DATETIME(2012-02) YEAR TO MONTH)
AND (h_date::DATETIME YEAR TO MONTH <= DATETIME(2013-01) YEAR TO MONTH)
That's verbose but precise. You could use a short-cut:
-- Query 2
SELECT id, name
FROM h_history
WHERE (h_date::DATETIME YEAR TO MONTH >= '2012-02')
AND (h_date::DATETIME YEAR TO MONTH <= '2013-01')
However, since h_date is a DATE rather than a DATETIME X TO Y type, there are other options available. The YEAR, MONTH, DAY functions extract the obvious parts from a DATE (and, if you pass a DATETIME to the function, then the DATETIME will be coerced into a DATE and then processed). The only fully locale-independent DATE constructor is the MDY function which takes 3 arguments, the month, day and year. All string representations are subject to interpretation by locale, and therefore won't work everywhere all the time.
You can also do:
-- Query 3
SELECT id, name
FROM h_history
WHERE (h_date >= MDY(2, 1, 2012))
AND (h_date <= MDY(1, 31, 2013))
Or:
-- Query 4
SELECT id, name
FROM h_history
WHERE ((YEAR(h_date) = 2012 AND MONTH(h_date) >= 2) OR YEAR(h_date) >= 2013)
AND ((YEAR(h_date) = 2013 AND MONTH(h_date) <= 1) OR YEAR(h_date) < 2013)
Or:
-- Query 5
SELECT id, name
FROM h_history
WHERE (YEAR(h_date) * 100 + MONTH(h_date)) >= 201202
AND (YEAR(h_date) * 100 + MONTH(h_date)) <= 201301
Given the choice, I'd probably use Query 2 as succinct but accurate, or perhaps Query 5, but all of queries 1-5 are usable.
If h_date was a column of some DATETIME type and you need to compare parts of a DATETIME, you can either use casting (as shown in Query 1) or the EXTEND function. That tends to be verbose.
Assuming that h_date is a proper date field, why wouldn't
SELECT
id
, name
FROM h_history
WHERE h_date >= '02/01/2012' and h_date <= '01/31/2013'
work for you?
Try the following
SELECT
id,name
FROM h_history
WHERE
h_date >= MDY(2, 1, 2012)
AND h_date < MDY(2, 1, 2013)
select * from table t where MONTH(t.date) = 12