Tuesday, May 25, 2010

OBIEE - Script for creating Time Dimension

A basic plsql script for creating time dimension table and populating it with data for years together.
CREATE TABLE mh_time_periods
(
time_period_id NUMBER NOT NULL
,YEAR NUMBER(4) NOT NULL
,quarter NUMBER(1) NOT NULL
,MONTH NUMBER(2) NOT NULL
,DAY NUMBER(2) NOT NULL
,month_end_flag char(1) DEFAULT 'N'
,year_end_flag char(1) default 'N'
,quarter_end_flag CHAR(1) DEFAULT 'N'
,period_date DATE NOT NULL
,CONSTRAINT mh_time_periods_pk PRIMARY KEY(time_period_id)
,CONSTRAINT mh_time_periods_chk1 CHECK(month_end_flag IN ('N','Y'))
,constraint mh_time_periods_chk2 check(year_end_flag in ('N','Y'))
,CONSTRAINT mh_time_periods_chk3 CHECK(quarter_end_flag IN ('N','Y'))
);
/
CREATE SEQUENCE mh_time_period_seq START WITH 1 INCREMENT BY 1;
/

Currently the time dimension table has basic settings. More columns can be added as and when required.
CREATE OR REPLACE PROCEDURE mh_fill_time_data(
p_end_date DATE
)
IS
l_year NUMBER(4) := 2005;
l_month NUMBER(2) := 1;
l_day NUMBER(2) := 1;
l_quarter NUMBER(1) := 0;
--l_flag BOOLEAN := FALSE;
l_month_end_flag CHAR(1) := 'N';
l_year_end_flag char(1) := 'N';
l_quarter_end_flag CHAR(1) := 'N';
l_loop_day_end NUMBER(2);
l_end_year NUMBER(4) := TO_NUMBER(TO_CHAR(p_end_date,'yyyy'));
l_end_month NUMBER(2) := TO_NUMBER(TO_CHAR(p_end_date,'mm'));
l_end_day NUMBER(2) := TO_NUMBER(TO_CHAR(p_end_date,'dd'));
BEGIN
SAVEPOINT start_insert;
LOOP
LOOP
IF l_month IN (1,2,3) THEN
l_quarter := 1;
ELSIF l_month IN (4,5,6) THEN
l_quarter := 2;
ELSIF l_month IN (7,8,9) THEN
l_quarter := 3;
ELSE
l_quarter := 4;
END IF;
IF l_month IN (1,3,5,7,8,10,12) THEN
l_loop_day_end := 31;
ELSIF l_month IN (4,6,9,11) THEN
l_loop_day_end := 30;
ELSE
l_loop_day_end := get_feb_day(l_year);
end if;
IF l_year = l_end_year AND l_month = l_end_month THEN
l_loop_day_end := l_end_day;
END IF;
LOOP
IF l_day = l_loop_day_end THEN
l_month_end_flag := 'Y';
IF l_month = 12 THEN
l_year_end_flag := 'Y';
END IF;
end if;
if l_month_end_flag = 'Y' then
if l_month IN (3,6,9,12) then
l_quarter_end_flag := 'Y';
END IF;
END IF;
INSERT INTO mh_time_periods(
time_period_id
,YEAR
,quarter
,MONTH
,DAY
,month_end_flag
,year_end_flag
,quarter_end_flag
,period_date
)
VALUES(
mh_time_period_seq.NEXTVAL
,l_year
,l_quarter
,l_month
,l_day
,l_month_end_flag
,l_year_end_flag
,l_quarter_end_flag
,TO_DATE(TO_CHAR(l_day)||'-'||TO_CHAR(l_month)||'-'||TO_CHAR(l_year),'DD-MM-YYYY')
);
l_day := l_day + 1;
EXIT WHEN l_day > l_loop_day_end;
END LOOP; -- end of day loop
l_quarter_end_flag := 'N';
l_month_end_flag := 'N';
l_day := 1;
l_month := l_month + 1;
EXIT WHEN l_month > 12 OR (l_year = l_end_year AND l_month > l_end_month);
END LOOP; -- End of month loop
l_year_end_flag := 'N';
l_month := 1;
l_year := l_year + 1;
EXIT WHEN l_year > l_end_year;
END LOOP; -- End of year loop
COMMIT;
dbms_output.put_line('Done');
EXCEPTION
WHEN others THEN
dbms_output.put_line(SQLERRM);
ROLLBACK TO start_insert;
END mh_fill_time_data;
/


CREATE OR REPLACE FUNCTION get_feb_day (nYr IN NUMBER) RETURN NUMBER IS
v_day varchar2(2);
begin
select to_char(last_day(to_date( '01-FEB-'|| to_char(nYr), 'DD-MON-YYYY')), 'DD') into v_day from dual;
return to_number(v_day);
END;
/

The above plsql populates from 01-JAN-2005. But can be changed to any starting date.

Note:
The code is open for scrutiny

Wednesday, May 19, 2010

OBIEE Datetime Functions

To get the start date of the last 4 quarters in OBIEE Answers

TIMESTAMPADD(SQL_TSI_QUARTER, -3,
TIMESTAMPADD(
SQL_TSI_DAY,-1*(DAY_OF_QUARTER(CURRENT_DATE)-1), CURRENT_DATE
)
)

Friday, May 7, 2010

Practice Notes: UTL_FILE mess

It is most common for apps developer to create outbound interfaces in text files and the usual requirement will of delivering 40 - 60 columns of data. Typical choice for a developer is to use UTL_FILE package.

Crux of the mess is we have to deliver above said number of column data on the average. Usually beginners would start like this

l_utl_file UTL_FILE.FILE_TYPE;
l_utl_file := UTL_FILE.FOPEN(p_dir, p_file_name,'w');

Then goes on to write to this file. All things set, compiles successfully and when run, produces following error:

ORA-29285: file write error

What happened here? we do have permission to write to that directory but still we get this error. It happens because, on average utl file with 40 - 60 columns takes at least 1500 - 2000 characters each line with proper formatting while default for an utl file line size is around 1000. The following is the procedure declaration for FOPEN.
function fopen returns record
(
location in varchar2,
filename in varchar2,
open_mode in varchar2,
max_linesize in binary_integer default

Most references we find for FOPEN miss the fourth default parameter set to 32K of characters. Hence for a typical interface flat file, it is necessary to open the file with required line size to not to see the write error.