Keep Getting the same error on both codes!!
DROP TABLE Date_Dimension CASCADE CONSTRAINTS ;
CREATE TABLE Date_Dimension
(
date_key NUMBER NOT NULL ,
full_date DATE ,
day_of_week NUMBER ,
day_num_in_month NUMBER ,
day_num_overall NUMBER ,
day_name VARCHAR2 (9) ,
day_abbrev VARCHAR2 (3) ,
week_num_in_year NUMBER ,
week_num_overall NUMBER ,
week_begin_date DATE ,
MONTH NUMBER ,
month_number_overall NUMBER ,
month_name VARCHAR2 (9) ,
month_abbrev VARCHAR2 (3) ,
quarter NUMBER ,
YEAR VARCHAR2 (20) ,
century NUMBER
) ;
ALTER TABLE Date_Dimension ADD CONSTRAINT Date_Dimension_PK PRIMARY KEY ( date_key ) ;
Create or replace PROCEDURE sp_DATE_DIMENSION(v_STARTDATE IN INT, v_END_YEAR IN INT) IS
v_STARTDATE DATE;
v_ENDDATE DATE;
v_STARTDATE Date := to_date('2005/01/01' || v_START_YEAR, 'YYYY/MM/DD');
v_ENDDATE Date := to_date('2020/12/31' || v_END_YEAR,'YYYY/MM/DD');
BEGIN
INSERT INTO
Date_Dimension
(date_key,full_date, day_of_week, day_num_in_month, day_num_overall, day_name, day_abbrev, week_num_in_year, week_num_overall, month, month_name, month_abbrev, quarter, year, century)
VALUES
(
'1',TO_DATE(v_STARTDATE, 'yyyy/mm/dd'), TO_NUMBER(v_STARTDATE, 'D'), TO_NUMBER(v_STARTDATE, 'DD'), TO_NUMBER(v_STARTDATE, 'DDD'), TO_CHAR(v_STARTDATE, 'DAY'), TO_CHAR(v_STARDATE, 'DY'), TO_NUMBER(v_STARTDATE, 'IW'), TO_NUMBER(v_STARTDATE, 'WW'), TO_NUMBER(v_STARTDATE, 'MM'), TO_CHAR (v_STARTDATE, 'MONTH'), TO_CHAR (v_STARTDATE, 'MON'), TO_NUMBER (v_STARTDATE, 'Q'), TO_CHAR (v_STARTDATE, 'YEAR'), TO_NUMBER (v_STARTDATE, 'CC')
)
;
IF v_STARTDATE > v_ENDDATE THEN
DBMS_OUTPUT.PUT_LINE ('ERROR IN CODE REGARDING DATES CHOSEN');
ELSE
WHILE v_STARTDATE <= V_ENDDATE LOOP
DBMS_OUTPUT.PUT_LINE ('Date : '||to_char(v_StartDate,'YYYY / MM / DD'));
v_STARTDATE := v_STARTDATE + 1;
END LOOP;
END IF;
END;
Your code gets
PLS-00410: duplicate fields in RECORD,TABLE or argument list are not permitted
You have duplicated the name startdate from the procedure's format argument list as a local variable; and then repeated both of them again. I think you meant the formal argument to just be the year, since it's a number. Your conversion to a date is also then wrong:
to_date('2005/01/01' || v_START_YEAR, 'YYYY/MM/DD')
... doesn't make sense, as you already have the year 2005 hard-coded.
I think for that part you want something more like:
create or replace PROCEDURE sp_DATE_DIMENSION(p_START_YEAR IN NUMBER, p_END_YEAR IN NUMBER) IS
l_START_DATE Date := to_date(p_START_YEAR ||'-01-01', 'YYYY-MM-DD');
l_END_DATE Date := to_date(p_END_YEAR ||'-01-01', 'YYYY-MM-DD');
BEGIN
with other variables references tweaked to match. In your insert you're passing the first value as the string '1' instead of the number 1. You then call to_date() against variables which are already dated; and you call to_number() with a format mask for a date element - for those you need to convert to a string, and then to a number. So that insert woudl become more like:
INSERT INTO Date_Dimension (date_key, full_date, day_of_week, day_num_in_month,
day_num_overall, day_name, day_abbrev, week_num_in_year, week_num_overall,
month, month_name, month_abbrev, quarter, year, century)
VALUES (1,
l_START_DATE,
TO_NUMBER(TO_CHAR(l_START_DATE, 'D')),
TO_NUMBER(TO_CHAR(l_START_DATE, 'DD')),
TO_NUMBER(TO_CHAR(l_START_DATE, 'DDD')),
TO_CHAR(l_START_DATE, 'DAY'),
TO_CHAR(l_START_DATE, 'DY'),
TO_NUMBER(TO_CHAR(l_START_DATE, 'IW')),
TO_NUMBER(TO_CHAR(l_START_DATE, 'WW')),
TO_NUMBER(TO_CHAR(l_START_DATE, 'MM')),
TO_CHAR(l_START_DATE, 'MONTH'),
TO_CHAR(l_START_DATE, 'MON'),
TO_NUMBER(TO_CHAR(l_START_DATE, 'Q')),
TO_CHAR (l_START_DATE, 'YEAR'),
TO_NUMBER(TO_CHAR(l_START_DATE, 'CC'))
);
It isn't good practice to use dbms_output for error messages, as (a) a calling program has no other indication that something is wrong, and (b) even in a simple client call a user may well not even have capture or display of those enabled. It's better to throw an exception:
IF l_START_DATE > l_END_DATE THEN
RAISE_APPLICATION_ERROR (-20001, 'ERROR IN CODE REGARDING DATES CHOSEN');
END IF;
WHILE l_START_DATE <= l_END_DATE LOOP
DBMS_OUTPUT.PUT_LINE ('Date : ' || to_char(l_START_DATE, 'YYYY / MM / DD'));
l_START_DATE := l_START_DATE + 1;
END LOOP;
END;
/
The exception will cause the procedure to terminate early, so you don't need the else part as nothing beyond that is reached anyway if the dates are wrong.
Even so, you probably really want to do the insert inside the loop so you create all the relevant rows; and it would make sense to do the check and exception throw right at the start (maybe comparing the years rather than the dates, but doesn't really matter. And probably other things I've forgotten - hopefully this will put you more towards the right track. You don't really need a procedure for this, or even PL/SQL, as it could be done in plain SQL and a single insert, but hopefully this is an exercise.
Related
I have a field (v_date) that contains date and time value e.g.(30-07-2021 13:30)
I want to import to my table two new dates and time values according to this field.
The first will be seven days later (08-06-2021 13:30), and the second one will be 7 days and one hour later (08-06-2021 14:30).
This is my code:
create or replace PROCEDURE "PR_ADD_MASSIVE_SESSIONS"
(v_date date)
IS
v_counter_day number :=7;
v_session_temp_date date;
v_session_temp_end date;
BEGIN
v_session_temp_date := to_date(v_date, 'MM-DD-YYYY HH24:MI') + v_counter_day;
v_session_temp_end := to_date(v_date, 'MM-DD-YYYY HH24:MI') + (v_counter_day + (1/1440*60));
INSERT INTO SESSIONS (CLIENTS_ID, SESSION_AREA, SESSION_DATE, SESSION_STOP, PAYMENT_TYPE_ID, PRICE, STATUS, COMPLETED, PAYMENT_STATUS, SESSION_NO)
VALUES (5, 1, v_session_temp_date, v_session_temp_end, 2, 40, 1, 1, 1, 1);
END;
And I get this error: Ajax call returned server error ORA-01843: not a valid month.
I try with to_char but the compiler only wants to_date in order to have a successful procedure.
The variables v_session_temp_date and v_session_temp_date are defined as DATE. The function TO_DATE converts a string to a date. It should not be used on variables of type DATE. Remove the TO_DATE and you're good to go. Example code below (using an anonymous block, not a procedure)
CREATE TABLE sessions
(
clients_id NUMBER,
session_area NUMBER,
session_date DATE,
session_stop DATE,
payment_type_id NUMBER,
price NUMBER,
status NUMBER,
completed NUMBER,
payment_status NUMBER,
session_no NUMBER
);
Table SESSIONS created.
--original code
DECLARE
v_date date := SYSDATE;
v_counter_day number := 7;
v_session_temp_date date;
v_session_temp_end date;
BEGIN
v_session_temp_date := to_date(v_date, 'MM-DD-YYYY HH24:MI') + v_counter_day;
v_session_temp_end := to_date(v_date, 'MM-DD-YYYY HH24:MI') + (v_counter_day + (1/1440*60));
INSERT INTO SESSIONS (CLIENTS_ID, SESSION_AREA, SESSION_DATE, SESSION_STOP, PAYMENT_TYPE_ID, PRICE, STATUS, COMPLETED, PAYMENT_STATUS, SESSION_NO)
VALUES (5, 1, v_session_temp_date, v_session_temp_end, 2, 40, 1, 1, 1, 1);
END;
/
ORA-01843: not a valid month
ORA-06512: at line 7
01843. 00000 - "not a valid month"
*Cause:
*Action:
--fixed code
DECLARE
v_date date := SYSDATE;
v_counter_day number := 7;
v_session_temp_date date;
v_session_temp_end date;
BEGIN
v_session_temp_date := v_date + v_counter_day;
v_session_temp_end := v_date + (v_counter_day + (1/1440*60));
INSERT INTO SESSIONS (CLIENTS_ID, SESSION_AREA, SESSION_DATE, SESSION_STOP, PAYMENT_TYPE_ID, PRICE, STATUS, COMPLETED, PAYMENT_STATUS, SESSION_NO)
VALUES (5, 1, v_session_temp_date, v_session_temp_end, 2, 40, 1, 1, 1, 1);
END;
/
PL/SQL procedure successfully completed.
How to check if it's a weekend in SQL?
I know I can convert sysdate to a number using this
SQL> select to_char(sysdate, 'd') from dual;
TO_CHAR(SYSDATE,'D')
But I'm not really sure how to check if today is a 6 or 7.
Do not use TO_CHAR with the D format model for this as it is dependant on the NLS_TERRITORY session parameter.
For example, when SYSDATE = 2018-09-10 (a Monday):
ALTER SESSION SET NLS_TERRITORY = 'France';
SELECT TO_CHAR( SYSDATE, 'D' ) FROM DUAL;
Outputs 1 but the same query in a different territory:
ALTER SESSION SET NLS_TERRITORY = 'America';
SELECT TO_CHAR( SYSDATE, 'D' ) FROM DUAL;
Outputs 2.
Instead, you can use TRUNC and the IW format model:
SELECT TRUNC( SYSDATE ) - TRUNC( SYSDATE, 'IW' ) FROM DUAL
Outputs 0 for Monday (and 1 for Tuesday ... 6 for Sunday) and is independent of the NLS_TERRITORY setting.
So you could filter this to give weekends as:
SELECT *
FROM DUAL
WHERE TRUNC( SYSDATE ) - TRUNC( SYSDATE, 'IW' ) IN ( 5, 6 )
or
SELECT *
FROM DUAL
WHERE SYSDATE - TRUNC( SYSDATE, 'IW' ) >= 5
If you want the days 1-indexed for consistency with your expected output from TO_CHAR (rather then 0-indexed) then just add 1 to the value.
sysdate is a pseudo column. You don't need to query it, you can evaluate it directly:
IF TO_CHAR(SYSDATE, 'D') IN ('6', '7') THEN
-- Do something
END IF;
I would avoid the ambiguous 'D' format as this varies between territories (the week starts after the weekend where I live), and use
if to_char(sysdate,'fmDY','nls_date_language=English') like 'S%'
then
Regarding the 'D' format, unfortunately to_char doesn't let you specify nls_territory inline, so without an explicit alter session command, it will rely on the session settings at runtime. I've seen production bugs due to this, where the same code worked in London but failed in New York.
And here's the logic in a reusable function, but flipped to ask "Is it a weekday?" (not a weekend). You could add a parameter for the start day of the weekdays (2 is the default in Oracle; Sunday is day #1).
CREATE OR REPLACE FUNCTION is_weekday (date_in IN DATE)
RETURN BOOLEAN
IS
BEGIN
RETURN TO_CHAR (date_in, 'D') BETWEEN 2 AND 6;
END;
/
DECLARE
l_date DATE := DATE '2018-09-10';
BEGIN
DBMS_OUTPUT.put_line ('If your weekend is Saturday and Sunday....');
FOR indx IN 1 .. 7
LOOP
DBMS_OUTPUT.put_line (
TO_CHAR (l_date, 'FMDay, Month DD YYYY')
|| ' is '
|| CASE WHEN NOT is_weekday (l_date) THEN 'not ' END
|| 'a weekday');
l_date := l_date + 1;
END LOOP;
END;
/
Try it in LiveSQL:
https://livesql.oracle.com/apex/livesql/file/content_G8NQSY6NP48NPJX96RLQ51SUE.html
I have a stored procedure in Oracle. I want to call it in JOBS of Oracle.
DECLARE
P_DATE DATE;
BEGIN
P_DATE := TO_DATE ('19/10/2016', 'DD/MM/YYYY');
MITRA.PENJUALAN_ANTAR_CABANG.REPORTKONSOLRK ( P_DATE );
COMMIT;
END;
the result is right when I execute P_DATE as '19/10/2016'. But when I change it like this below the result is not display.
DECLARE
P_DATE DATE;
BEGIN
P_DATE := TO_DATE (TRUNC (SYSDATE - 1), 'DD/MM/YYYY');
MITRA.PENJUALAN_ANTAR_CABANG.REPORTKONSOLRK (P_DATE);
COMMIT;
END;
What is my query mistake, while if I run select trunc(SYSDATE-1) from dual the date was right.
SYSDATE - 1 is already a date. Passing it to to_date will produce a syntax error. Just drop the to_date call and you should be fine:
P_DATE := TRUNC(SYSDATE - 1);
I'm very new to plsql and am having a bit of trouble with one of my assignments. I've been asked to create a date dimension table and populate it with various date information.
I created my table as follows
CREATE TABLE date_dimension_table
(
v_date DATE NOT NULL,
full_date VARCHAR2(50) NOT NULL,
day_of_week NUMBER(1,0) NOT NULL,
day_in_month NUMBER(2,0) NOT NULL,
day_in_year NUMBER(3,0) NOT NULL,
last_day_of_week_flag VARCHAR2(1) NOT NULL,
last_day_of_month_flag VARCHAR2(1) NOT NULL,
end_of_week_date DATE NOT NULL,
month_number NUMBER(2,0) NOT NULL,
month_name VARCHAR2(32) NOT NULL,
year_month CHAR(32) NOT NULL,
quarter_number NUMBER(1,0) NOT NULL,
year_and_quarter VARCHAR2(32) NOT NULL,
v_year NUMBER(4,0) NOT NULL,
CONSTRAINT date_dimension_table_PK PRIMARY KEY (v_date)
);
I then created the following procedure to populate the table.
create or replace PROCEDURE sp_populate_dates(
v_first_date_in DATE,
v_second_date_in DATE)
AS
--Declare two variables as DATE datatypes
v_current_date DATE;
v_end_date DATE;
BEGIN
--Assign the start date and end date to the variables
v_current_date := TO_DATE(v_first_date_in, 'DDMMYYYY');
v_end_date := TO_DATE(v_second_date_in, 'DDMMYYYY');
--Delete whats currently in the table
DELETE FROM date_dimension_table;
--condition checks if the first date is before the second date
WHILE v_current_date <= v_end_date
LOOP
--insert into table
INSERT INTO date_dimension_table
(
v_date,
full_date,
day_of_week,
day_in_month,
day_in_year,
last_day_of_week_flag,
last_day_of_month_flag,
end_of_week_date,
month_number,
month_name,
year_month,
quarter_number,
year_and_quarter,
v_year
)
VALUES
(
v_current_date, --date
TO_CHAR(v_current_date, 'Day, Month DD, YYYY'), --full date
TO_NUMBER(TO_CHAR(v_current_date, 'D')) -1, --day in week
TO_CHAR(v_current_date,'DD'), --day in month
TO_CHAR(v_current_date,'DDD'), --day in year
CASE --last day of week flag
WHEN TO_CHAR(v_current_date,'FMDay') = 'Sunday' THEN 'Y'
ELSE 'N'
END,
CASE --last day of month flag
WHEN last_day(TO_DATE(v_current_date, 'MM/DD/YYYY')) = TO_DATE(v_current_date, 'MM/DD/YYYY') THEN 'Y'
ELSE 'N'
END,
CASE --date of next sunday
WHEN TO_CHAR(v_current_date,'FMDay') = 'Sunday' THEN v_current_date
ELSE NEXT_DAY(v_current_date,'SUNDAY')
END,
TO_CHAR(v_current_date,'MM'), --month number
TO_CHAR(v_current_date,'MONTH'), --name of month
TO_CHAR(v_current_date,'MONTH YYYY'), --month and year
TO_CHAR(v_current_date,'Q'), --number of quarter
TO_CHAR(v_current_date,'YYYY Q'), --year and quarter
TO_CHAR(v_current_date,'YYYY') --year
);
--Increment and assign the current date value to be re-evaluated
v_current_date := v_current_date + 1;
END LOOP;
END;
As the program has to insert information from a range of dates, I was using two dates as the input parameters and it was throwing up an error.
Example:
BEGIN
sp_populate_dates(01/01/2005, 01/01/2006);
END;
I was then getting errors suggesting it was the wrong type of argument to call in the function. I was wondering if anyone could help with this please, and show me where I'm going wrong. Cheers
First of all, you have procedure that get
v_first_date_in DATE,
v_second_date_in DATE
DATEs, but you're trying to pass even not a string. Try something like this
BEGIN
sp_populate_dates(TO_DATE('01/01/2005', 'dd/mm/yyyy'), TO_DATE('01/01/2005', 'dd/mm/yyyy'));
END;
another thing is - you process these parameters in the procedure, so you probably wanted to pass varchar2 type variables. That means,
create or replace PROCEDURE sp_populate_dates(
v_first_date_in IN VARCHAR2,
v_second_date_in IN VARCHAR2)
...
and call it like
BEGIN
sp_populate_dates('01/01/2005','01/01/2005');
END;
However be careful here: if type of date here is ('dd/mm/yyyy'), your processing date in the procedure ('ddmmyyyy') is not correct.
Hope it will help!
If you don't really need any date formatting and parsing, always use the SQL standard DATE literal:
BEGIN
sp_populate_dates(DATE '2005-01-01', DATE '2006-01-01');
END;
You can populate such a table (for the dates between 2000-2030) by using the following query:
CREATE TABLE DIM_DATE
as
(
select
TO_NUMBER(TO_CHAR(DATE_D,'j')) as DATE_J,
DATE_D,
TO_CHAR(DATE_D,'YYYY-MM-DD') as DATE_V,
TO_NUMBER(TO_CHAR(DATE_D,'YYYY')) as YEAR_NUM,
TO_NUMBER(TO_CHAR(DATE_D,'Q')) as QUARTER_NUM,
TO_NUMBER(TO_CHAR(DATE_D,'MM')) as MONTH_NUM,
TRIM(TO_CHAR(DATE_D,'Month','nls_date_language=english')) as MONTH_DESC,
TO_NUMBER(TO_CHAR(DATE_D,'IW')) as ISO_WEEK_NUM,
TO_NUMBER(TO_CHAR(DATE_D,'IYYY')) as ISO_YEAR_NUM,
TO_NUMBER(TO_CHAR(DATE_D,'DD')) as DAY_OF_MONTH_NUM,
TO_NUMBER(TO_CHAR(DATE_D,'D')) as DAY_OF_WEEK_NUM,
TRIM(TO_CHAR(DATE_D,'Day','nls_date_language=english')) as DAY_OF_WEEK_DESC,
(CASE WHEN TRIM(TO_CHAR(DATE_D,'Day','nls_date_language=english')) IN ('Saturday','Sunday') THEN 'Weekend' ELSE 'Weekday' END) as DAY_TYPE_DESC
from
(
select
to_date('1999-12-31','YYYY-MM-DD')+ROWNUM as DATE_D
from
dual
connect by level <= to_date('2029-12-31','YYYY-MM-DD')-to_date('1999-12-31','YYYY-MM-DD')
)
);
Given 2 dates (StartDate and EndDate), how to do i generate quarterly periods in Pl/SQL.
Example:
Start Date: 01-JAN-2009
End Date: 31-DEC-2009
Expected Output:
StartDate EndDate
01-JAN-2009 31-MAR-2009
01-APR-2009 30-JUN-2009
01-JUL-2009 30-SEP-2009
01-OCT-2009 31-DEC-2009
SELECT ADD_MONTHS( TRUNC(PARAM.start_date, 'Q'), 3*(LEVEL-1) ) AS qstart
, ADD_MONTHS( TRUNC(PARAM.start_date, 'Q'), 3*(LEVEL) ) -1 AS qend
FROM ( SELECT TO_DATE('&start_date') AS start_date
, TO_DATE('&end_date') AS end_date
FROM DUAL
) PARAM
CONNECT BY ADD_MONTHS( TRUNC(PARAM.start_date, 'Q'), 3*(LEVEL) ) -1
<= PARAM.end_date
Rules for params, you may need to adjust the query to suit your purposes:
If start_date is not exact quarter start it effectively uses the quarter contain start date.
If end_date is not exact quarter end then we end on the quarter that ended BEFORE end_date (not the one containing end date).
Here's one way that you can do it with PL/SQL
declare
startDate Date := '01-JAN-2009';
endDate Date := '31-DEC-2009';
totalQuarters number := 0;
begin
totalQuarters := round(months_between(endDate, startDate),0)/3;
dbms_output.put_line ('total quarters: ' || totalQuarters);
for i in 1..totalQuarters loop
dbms_output.put_line('start date: '|| startDate || ' end date:' || add_months(startDate -1,3));
startDate := add_months(startDate,3) ;
end loop;
end;