Stored procedure to get one year of data splitted per months - oracle

I am relatively new to Oracle so i'd really appreciate some help.
I have a query like:
SELECT * FROM reservations WHERE date between (date1) and (date2).
What I need is to get all the reservations within the interval : today's date and today's date -1 year, so basically 1 year of history.
I want to run the above query with interval of 1 months, and export the query set to excel.
I need some help in the logic of the loop (create a stored procedure or function), as i will think later for the export to excel.

This will give all records from 1 year back to today:
SELECT * FROM reservations
WHERE date >= trunc( sysdate ) - interval '1' year
AND date < trunc( sysdate ) + interval '1' day
I want to run the above query with interval of 1 months,
I understand that you want to run this query 12 times, each time for another monthly period. If yes, then run this query 12 times changing the parameter X (within SELECT 1 As X FROM dual subquery), beginning from 12 to 1 (or 1 to 12):
SELECT * FROM reservations
CROSS JOIN (
SELECT 1 As X FROM dual
) x
WHERE date >= trunc( sysdate ) - x * interval '1' month
AND date < trunc( sysdate ) + interval '1' day - ( x - 1 ) * interval '1' month

Here is the procedure to create the queries month-wise starting from sysdate .
create or replace procedure monthwise_query
as
t_date DATE := TRUNC(SYSDATE) ;
c_date DATE ;
BEGIN
FOR i in 1..12
LOOP
c_date := t_date;
dbms_output.put_line('select * from reservations where rev_date between to_date('''||c_date||''',''DD-MON-YY'') and (select add_months(to_date('''||c_date||''',''DD-MON-YY''),-1) from dual);');
select add_months(c_date,-1)
into t_date
from dual;
END LOOP;
END;
- It can be extended with UTIL_FILE utility inside this procedure to write each query result to single/multiple files.
- SPOOL from SQL*plus for each query will be easy
- UNIX script to loop through each monthwise-query to load to file also possible

Related

between function and year variable in oracle

[I have a table T1 with 2 columns NAME & DT as shown in figure
CREATE TABLE T1 ( NAME VARCHAR2(1), DT TIMESTAMP(3) );
INSERT INTO T1 VALUES ( ‘A’ , TO_TIMESTAMP(‘2021-02-04 12:00:00.000000000’, ‘YYYY-MM-DD HH24:MI:SS.FF’));
INSERT INTO T1 VALUES ( ‘B’ , TO_TIMESTAMP(‘2021-02-05 12:00:00.000000000’, ‘YYYY-MM-DD HH24:MI:SS.FF’));
INSERT INTO T1 VALUES ( ‘C’ , TO_TIMESTAMP(‘2021-02-15 12:00:00.000000000’, ‘YYYY-MM-DD HH24:MI:SS.FF’));
After executing above query, data gets populated and I want to select the records from DT from 1st jan till today of this year.
I have created a procedure as shown below such that if I run the same procedure next year, it needs to select the records from 1st jan till sysdate of that particular year i.e. here year changes..I have created a variable for the year and assigned current year to it..In next year, year variable will be 2022 etc..
How should I query it in WHERE clause to select the records from 1st Jan till sysdate of particular year (i.e. 2021/2022/2023 so on)..and you can see DT format is Timestamp
CREATE PROCEDURE P1
AS
V_YEAR VARCHAR2(4); -- year variable
V_YEAR := TO_CHAR((SYSDATE), ‘YYYY); -- Assigning current year to this variable
BEGIN
SELECT * FROM T1 WHERE DT BETWEEN TO_DATE(‘01/01/2021’, ‘mm/dd/yyyy’) AND TO_DATE(SYSDATE, ‘mm/dd/yyyy’);
END;
/]
1
You can use trunc in your query as follows:
DT BETWEEN TRUNC(SYSDATE,'YYYY') AND SYSDATE;
TRUNC(SYSDATE,'YYYY') returns the starting day of the year
Yet another option would be
WHERE TRUNC(DT, 'YEAR') = TRUNC(SYSDATE, 'YEAR');
If you wanted to get everything in the current year

Loop Insert Date a daily commit day to day timestamp ORACLE

im executing query in oracle. i need insert data everyday commit in looping like this :
DECLARE
start_date NUMBER;
end_date NUMBER;
business_date VARCHAR2 (8);
BEGIN
start_date := TO_NUMBER (TO_CHAR (TO_DATE ('2017-01-01', 'yyyy-MM-dd')));
end_date := TO_NUMBER (TO_CHAR (TO_DATE ('2018-01-01', 'yyyy-MM-dd')));
FOR cur_r IN start_date .. end_date
LOOP
INSERT INTO file_backup
SELECT *
FROM file_core
WHERE TO_NUMBER (TO_CHAR (TO_DATE (datecreated, 'yyyy-MM-dd')))>=start_date+cur_r
AND TO_NUMBER (TO_CHAR (TO_DATE (datecreated, 'yyyy-MM-dd')))<=end_date;
COMMIT;
END LOOP;
END;
I dunt know error this script .. please help me .. btw i newbie in oracle sorry ..
As #boneist pointed out, your manipulation using numbers isn't going to work. You should keep the data type as it is and compare with values of the same data type.
Assuming you have a legitimate need to do this in a loop you could do something like this:
BEGIN
FOR r IN (
select date '2017-01-01' + level -1 as this_date
from dual
connect by level <= date '2018-01-01' - date '2017-01-01'
)
LOOP
INSERT INTO file_backup
SELECT *
FROM file_core
WHERE datecreated >= r.this_date
AND datecreated < r.this_date + 1;
COMMIT;
END LOOP;
END;
/
Or if the data type is actually a timestamp rather than a date as suggested in a comment, something like:
BEGIN
FOR r IN (
select timestamp '2017-01-01 00:00:00'
+ (level -1) * interval '1' day as this_timestamp
from dual
connect by level <= extract(day from timestamp '2018-01-01 00:00:00'
- timestamp '2017-01-01 00:00:00')
)
LOOP
INSERT INTO file_backup
SELECT *
FROM file_core
WHERE datecreated >= r.this_timestamp
AND datecreated < r.this_timestamp + interval '1' day;
COMMIT;
END LOOP;
END;
/
... though you might want to work on the condition for the connect-by query, e.g.
FOR r IN (
select timestamp '2017-01-01 00:00:00'
+ numtodsinterval(level -1, 'DAY') as this_timestamp
from dual
connect by timestamp '2017-01-01 00:00:00'
+ numtodsinterval(level -1, 'DAY') < timestamp '2018-01-01 00:00:00'
)
LOOP
...
or as #boneist suggested in a comment, with a simpler loop:
BEGIN
FOR num_days in 0..(date '2018-01-01' - date '2017-01-01' - 1)
LOOP
INSERT INTO file_backup
SELECT *
FROM file_core
WHERE datecreated >= timestamp '2017-01-01 00:00:00'
+ numtodsinterval(num_days, 'DAY')
AND datecreated < timestamp '2017-01-01 00:00:00'
+ numtodsinterval(num_days + 1, 'DAY');
COMMIT;
END LOOP;
END;
/
The main problem with this approach is restartability. If there is an error part way through the loop you can't just re-run it, as you'd be inserting duplicates.
Multiple inserts and commits are also less efficient that a single insert, or even multiple inserts and a single commit. If you don't have enough undo space to allow a single transaction to do all the work you need you should be fixing the database configuration to allow that, rather than working around it and potentially compromising data integrity.
i need backup this table . and insert only 2 month in new table
That sounds like you need to partition the table by month and use partition swaps to shift old months from the live to the backup table, perhaps. Partitioning costs more but if you have those data volumes it may be justified.
Failing that you could consider renaming your current table to backup, recreating your original table, and just copying the two months' worth of data you want to keep back to that. But that's a one-off thing, you still have the ongoing problem of ageing records out of the main table and into backup. And it has its own issues with dependencies, constraints, etc.
You don't need any loop and you should skip all these TO_CHAR, TO_NUMBER, TO_DATE conversions. Try this:
INSERT INTO file_backup
SELECT *
FROM file_core
WHERE datecreated BETWEEN DATE '2017-01-01' AND DATE '2018-01-01';
Perhaps datecreated has time values different to 00:00:00, in this case you should run
INSERT INTO file_backup
SELECT *
FROM file_core
WHERE TRUNC(datecreated) BETWEEN DATE '2017-01-01' AND DATE '2018-01-01';
or in case datecreated is a VARCHAR2 data type rather than DATE run
INSERT INTO file_backup
SELECT *
FROM file_core
WHERE TO_DATE(datecreated, 'YYYY-MM-DD') BETWEEN DATE '2017-01-01' AND DATE '2018-01-01';
Assuming your datecreated column is of data type DATE and you only want one copy of the rows of the table then you do not need PL/SQL:
INSERT INTO file_backup
SELECT *
FROM file_core
WHERE datecreated BETWEEN DATE '2017-01-01' AND DATE '2018-01-01';
COMMIT;
You could use a DATE literal to set the start_date and end_date and use a loop like this.
DECLARE
start_date NUMBER;
end_date NUMBER;
BEGIN
start_date := DATE '2017-01-01';
end_date := DATE '2018-01-01';
FOR cur_r IN 0 .. (end_date - start_date)
LOOP
INSERT INTO file_backup
SELECT *
FROM file_core
WHERE TRUNC (datecreated) = start_date + cur_r;
COMMIT;
END LOOP;
END;
Why wouldn't you do it using a simple INSERT, such as
INSERT INTO file_backup
SELECT *
FROM file_core
WHERE datecreated BETWEEN DATE '2017-01-01' AND DATE '2018-01-01';
If you're just practicing PL/SQL and loops, well, remove COMMIT out of the LOOP. Both START and END_DATE should be "converted" to a number using a proper format mask (i.e. yyyymmdd). FOR loop's index should go from 1 to a difference between END and START_DATE.
[EDITED, after reading MT0's comment]
[EDITED #2, after reading some more comments]
Bah, my code is rubbish, should've thought about what I'm doing. Basically, if I meant to write it properly, it would have looked like #Kaushik Nayak's, and there's really no point in doing it twice.

Get date of the previous day in Oracle

I need to bring the day immediately preceding date in Oracle using a truncate but not how. He was using the following line but bring me some records for the current day of execution and should not be. Neceisto only the previous day; investigation found the truncate with dates in Oracle but not how to use it.
and fnxs.FECHA_INGRESO BETWEEN (TO_CHAR (SYSDATE-1, 'DD-MON-YY')) AND (TO_CHAR (SYSDATE, 'DD-MON-YY'));
I appreciate your help
Using BETWEEN with dates in Oracle is generally a bad idea. I see it all the time, and most of the time people get it wrong (like in the accepted answer above). Even when they fully understand that the two dates are included, they still make logical errors because they forget about timestamps.
The OP is asking for yesterday dates. The following sql shows that today falls within "BETWEEN TRUNC( SYSDATE ) - 1 AND TRUNC( SYSDATE )"
with adate as (
select trunc(sysdate) today from dual
) select today from adate where today between trunc(sysdate) -1
and trunc(sysdate);
16-Apr-15 00:00:00
[returns the record for today]
I find it easier to be correct with dates when you're more explicit about the end points:
SELECT * from your_table
WHERE fnxs.FECHA_INGRESO >= TRUMC(SYSDATE) - 1
AND fnxs.FECHA_INGRESO < TRUNC(SYSDATE);
Upon looking closer, the OP's date-like column might be a VARCHAR2 (could still be a date that was implicitly cast in the comparison he gave). If it is a VARCHAR, then it needs to be converted first (using an appropriate format string):
SELECT * FROM your_table
WHERE TO_DATE(fnxs.FECHA_INGRESO, 'DD-MON-YY') >= TRUMC(SYSDATE) - 1
AND TO_DATE(fnxs.FECHA_INGRESO, 'DD-MON-YY') < TRUNC(SYSDATE);
Assuming your column is of type DATE
SELECT *
FROM TABLE_NAME
WHERE FECHA_INGRESO BETWEEN TRUNC( SYSDATE ) - 1
AND TRUNC( SYSDATE );
If it is a character string then:
SELECT *
FROM TABLE_NAME
WHERE TO_DATE( FECHA_INGRESO, 'DD-MON-YY' )
BETWEEN TRUNC( SYSDATE ) - 1
AND TRUNC( SYSDATE );

Get First Day Of Week From Week Number

In Oracle, is there a straightforward way to get the first day of the week given a week number?
For example, today's date is 12/4/2012. If I run:
select to_char(sysdate,'WW') from dual;
It returns 49 for the week number.
What I would like to do is somehow return 12/2/2012 for the first day...given week 49 (assuming Sunday as first day of the week).
Any ideas? Thanks in advance for any help!
try this:
select next_day(max(d), 'sun') requested_sun
from (select to_date('01-01-2012', 'dd-mm-yyyy') + (rownum-1) d from dual connect by level <= 366)
where to_char(d, 'ww') = 49-1;
just set your year to_date('01-01-2012' and week number-1 49-1 as applicable.
the sunday in the 49th week of 2008?
SQL> select next_day(max(d), 'sun') requested_sun
2 from (select to_date('01-01-2008', 'dd-mm-yyyy') + (rownum-1) d from dual connect by level <= 366)
3 where to_char(d, 'ww') = 49-1;
REQUESTED
---------
07-DEC-08
and 2012
SQL> select next_day(max(d), 'sun') requested_sun
2 from (select to_date('01-01-2012', 'dd-mm-yyyy') + (rownum-1) d from dual connect by level <= 366)
3 where to_char(d, 'ww') = 49-1;
REQUESTED
---------
02-DEC-12
Try this,
select
next_day(trunc(to_date(in_year,'yyyy'),'yyyy') -1,'Mon') + (7 * (in_week - 1))
from dual;
If you have the date, not just the week number, you can try this:
Get the day number of the week of your date with: to_char(theDate, 'D')
substract that number from your date plus 1, and you'll get the Sunday of that week.
Add 7 and you'll get the date of end of the week(Saturday).
Like this:
SELECT theDate - to_char(theDate, 'D') + 1 as BeginOfWeek,
theDate,
theDate - to_char(theDate, 'D') + 7 as EndOfWeek
FROM TableName
I can't comment on questions yet, so I'll add another one. But this is based on #Dazzals answer.
His solution doesn't work for week one and for ISO-weeks. Also it doesn't work, if the first day of the week is not sunday, which can be controlled by NLS_SETTINGS.
This one does:
SELECT MIN(D)
FROM (SELECT TO_DATE('01-01-2013', 'dd-mm-yyyy') + (ROWNUM-10) D, ROWNUM R
FROM DUAL
CONNECT BY LEVEL <= 376)
WHERE TO_CHAR(D,'IYYYIW') = '201301'
Because we are spanning more than one year, we need to check the year too.
Using the trunc function #Justin used, I think this is what you want:
select trunc(to_date('2012-01-01', 'YYYY-MM-DD') + (49 - 1) * 7, 'WW') from dual;
I ended up doing this:
function getFirstDayOfWeek(y in binary_integer, w in binary_integer) return date
is
td date;
begin
td:=TO_DATE(TO_CHAR(y)||'0101', 'YYYYMMDD');
for c in 0..52
loop
if TO_NUMBER(TO_CHAR(td, 'IW'))=w then
return TRUNC(td, 'IW');
end if;
td:=td+7;
end loop;
return null;
end;

Oracle Date - How to add years to date

I have a date field
DATE = 10/10/2010
sum = 4 (this are number of years by calculation)
is there a way to add four years to 10/10/2010 and make it
10/10/2014?
Try adding months (12 * number of years) instead. Like this-
add_months(date'2010-10-10', 48)
Use add_months
Example:
SELECT add_months( to_date('10-OCT-2010'), 48 ) FROM DUAL;
Warning
add_months, returns the last day of the resulting month if you input the last day of a month to begin with.
So add_months(to_date('28-feb-2011'),12) will return 29-feb-2012 as a result.
I believe you could use the ADD_MONTHS() function. 4 years is 48 months, so:
add_months(DATE,48)
Here is some information on using the function:
http://www.techonthenet.com/oracle/functions/add_months.php
http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:1157035034361
You can try this:
someDate + interval '4' year
INTERVAL
I am not sure, if I understood Your question correctly, but
select add_months(someDate, numberOfYears * 12) from dual
might do the trick
One more option apart from ADD_MONTHS
SELECT
SYSDATE,
SYSDATE
+ TO_YMINTERVAL ( '1-0' )
FROM
DUAL;
SYSDATE SYSDATE+TO_YMINTERVAL('1-0')
--------- ----------------------------
29-OCT-13 29-OCT-14
1 row selected.
SELECT
SYSDATE,
SYSDATE
+ TO_YMINTERVAL ( '2-0' )
FROM
DUAL;
SYSDATE SYSDATE+TO_YMINTERVAL('2-0')
--------- ----------------------------
29-OCT-13 29-OCT-15
1 row selected.
SELECT
TO_DATE ( '29-FEB-2004',
'DD-MON-YYYY' )
+ TO_YMINTERVAL ( '1-0' )
FROM
DUAL
*
Error at line 4
ORA-01839: date not valid for month specified
But the last one is illegal since there is no 29th day of February in 2005, hence it fails on leap year cases (Feb 29)
Read the documentation for the same
SELECT TO_CHAR(SYSDATE,'YYYY')-2 ANO FROM DUAL

Resources