在OFSA系统中, 有时候需要从多个维度展示数据,而系统中设置的维度可能不够, 这就要我们自己来添加一个展示的维度,即leaf. OFSA系统目录下提供了一个脚本可以用来在OFSA系统中增加一个leaf。改脚本的内容如下,执行该脚本先是在所有的Instrument表或Ledger表中增加leaf字段, 然后在OFSA中注册该字段。 部分内容加了注释。
PROCEDURE add_leaf (p_login_name IN VARCHAR2, p_colName IN varchar2,
p_displayName IN varchar2, p_leafType IN varchar2,
p_dbfName IN varchar2)
IS
/***************************************************************************
Nested Functions and Procedures
**************************************************************************/
procedure add_column (p_login_name IN VARCHAR2, p_tabName IN VARCHAR2,
p_colName IN VARCHAR2);
procedure column_not_null (p_login_name IN VARCHAR2, p_tabName IN VARCHAR2,
p_colName IN VARCHAR2);
PROCEDURE update_leaf (p_login_name IN VARCHAR2, p_tabName IN VARCHAR2,
p_colName IN VARCHAR2);
PROCEDURE modify_view (p_login_name IN VARCHAR2, p_viewName IN VARCHAR2,
p_colName IN VARCHAR2);
PROCEDURE exec (p_login_name IN VARCHAR2,
p_command IN VARCHAR2,
p_proc_name IN VARCHAR2);
PROCEDURE big_stmt_exec (p_login_name IN VARCHAR2,
p_sqlSTMT IN DBMS_SQL.VARCHAR2S,
p_sqlLB IN NUMBER,
p_sqlUB IN NUMBER,
p_proc_name IN VARCHAR2);
PROCEDURE parse_sql_string ( p_sql_string IN VARCHAR2,
p_viewName IN VARCHAR2,
p_sqlSTMT OUT DBMS_SQL.VARCHAR2S,
p_sqlUB OUT NUMBER);
/**************************************************************************
Private variables
***************************************************************************/
v_tabName VARCHAR2(30);
v_viewName VARCHAR2(30);
v_err_num INTEGER;
/**************************************************************************
Private Exceptions
***************************************************************************/
e_parameter EXCEPTION;
/**************************************************************************
Private Cursors
***************************************************************************/
--得到所有的Instrument表和Ledger表。
cursor bothTables is
SELECT DISTINCT(A.table_name)
FROM ofsa_table_class_assignment A, user_tables U
WHERE A.table_classification_cd in (50, 200, 210, 300, 310, 351, 360, 370)
AND U.table_name = A.table_name
AND NOT EXISTS (select null from user_tab_columns UC2
where UC2.table_name = A.table_name
and UC2.column_name = UPPER(p_colName));
-- 只得到Ledger表。
cursor ledgerTables is
SELECT DISTINCT(A.table_name)
FROM ofsa_table_class_assignment A, user_tables U
WHERE A.table_classification_cd in (352 )
AND U.table_name = A.table_name
AND NOT EXISTS (select column_name from user_tab_columns UC2
where UC2.table_name = A.table_name
and UC2.column_name = UPPER(p_colName));
--所有Instrument表和Ledger表,Leaf类型所需要的视图列表。
-- This cursor gets the list of views required by Leaf Type
-- 'Both Instrument and Ledger'
cursor bothViews IS
SELECT DISTINCT(A.table_name)
FROM ofsa_table_class_assignment A, user_views U
WHERE A.table_classification_cd in (50, 200, 210, 300, 310, 351, 360, 370)
AND U.view_name = A.table_name
AND NOT EXISTS (select null from user_tab_columns UC2
where UC2.table_name = A.table_name
and UC2.column_name = UPPER(p_colName));
/****************************************************************************
Procedure: MODIFY_VIEW
Purpose: This procedure is called by ADD_LEAF to add the leaf column
as a column in the SELECT list of a view.
Any views with an "@" will not be modified
as they are views which access objects across a dblink.
Arguments: p_login_name -- the User Name performing the DDL
v_viewName -- the table being altered
p_colName -- the column being added
****************************************************************************/
procedure modify_view (p_login_name IN VARCHAR2, p_viewName IN VARCHAR2,
p_colName IN VARCHAR2) IS
-- get the view text
cursor view_text is
SELECT text
FROM user_views
WHERE view_name = p_viewName;
v_hold_long_text VARCHAR2(32767); -- holds converted view_text from
-- LONG datatype
v_begin_counter PLS_INTEGER;
v_letters_to_get PLS_INTEGER;
v_text_length PLS_INTEGER; -- length of the view_text
v_dblink_sign PLS_INTEGER;
v_asterisk_sign PLS_INTEGER;
--+ Storage et al. for our dynamic SQL statement
v_sqlSTMT DBMS_SQL.VARCHAR2S; --+actual SQL statement original definition 256
v_sqlUB number := 1; --+stmt table's upper bound
v_sqlLB CONSTANT number := 1; --+stmt table's lower bound
e_dblink EXCEPTION;
BEGIN
--dbms_output.put_line('Begin modify view');
-- parse the LONG view text into a varchar2 data type
FOR the_view_text IN view_text LOOP
v_hold_long_text :=the_view_text.text;
END LOOP;
-- Remove carriage returns
v_hold_long_text := replace(v_hold_long_Text,chr(10),' ');
-- Remove square characters
v_hold_long_text := replace(v_hold_long_Text,chr(13),' ');
-- Replace tabs with spaces
v_hold_long_text := replace(v_hold_long_Text,chr(9),' ');
-- search the view text for the @ sign
v_dblink_sign :=0;
v_dblink_sign := instr(v_hold_long_text,'@');
--dbms_output.put_line('db_link is '||v_dblink_sign);
-- Only process views with no @ sign - db link views not supported
-- for this utility
IF v_dblink_sign = 0 THEN
--dbms_output.put_line('no dblink');
-- search the view for an asterisk ('*') - for views without a *,
-- we need to explicitely add in the new leaf column in the select
-- list
v_asterisk_sign :=0;
v_asterisk_sign := instr(v_hold_long_text,'*');
IF v_asterisk_sign = 0 THEN
-- Convert to uppercase
v_hold_long_text := UPPER(v_hold_long_text);
-- Replace "SELECT" with "SELECT leaf_col,"
v_hold_long_text := replace(v_hold_long_text,'SELECT','SELECT '||p_colName||',');
END IF;
-- add a new line character after every comma
-- this ensures that our SQL statement won't be put into
-- a single line (which overflows the line buffer)
v_hold_long_text := replace(v_hold_long_text,',',','||chr(10));
v_hold_long_text := 'CREATE OR REPLACE VIEW '||p_viewName||' AS '||v_hold_long_text;
-- send the SQL statement to the parse function to split each new line
-- into an array element
parse_sql_string(v_hold_long_text,p_viewName, v_sqlSTMT, v_sqlUB);
-- send the SQL statement to be executed
big_stmt_exec(p_login_name,v_sqlSTMT,v_sqlLB, v_sqlUB,'ADD_LEAF');
ELSE
RAISE e_dblink;
END IF;
EXCEPTION
WHEN e_dblink THEN
dbms_output.put_line('View: '||p_viewName);
dbms_output.put_line('References a database link');
dbms_output.put_line('Update this view manually to include the new leaf column');
WHEN OTHERS THEN NULL;
END modify_view;
/****************************************************************************
Procedure: UPDATE_LEAF
Purpose: This procedure is called by ADD_LEAF to set the
value for the new leaf Column to '0'
Arguments: p_login_name -- the User Name performing the update
v_tabName -- the table being updated
p_colName -- the column being updated
****************************************************************************/
PROCEDURE update_leaf (p_login_name IN VARCHAR2, p_tabName IN VARCHAR2,
p_colName IN VARCHAR2) IS
c_tbl_prop_cd_INSTR_REQ_COL CONSTANT NUMBER(5) := 10;
c_tbl_prop_cd_TP_OPTION_COST CONSTANT NUMBER(5) := 100;
c_tbl_class_cd_LEDGER_STAT CONSTANT NUMBER(5) := 50;
c_Target_Rows_Per_Partition CONSTANT NUMBER(6) := 100000;
v_row_count PLS_INTEGER;
v_tot_partitions PLS_INTEGER;
v_org_partitions PLS_INTEGER;
v_orgs_per_partition PLS_INTEGER;
v_org_partition_num PLS_INTEGER;
v_org_upper NUMBER(14);
v_org_lower NUMBER(14);
v_text VARCHAR2(400);
c01 INTEGER;
v_rc INTEGER;
FUNCTION exec_select (p_command IN VARCHAR2) RETURN NUMBER IS
-- Dynamically executes a SELECT statement and returns a
-- single numeric value from returned by that statement
-- (useful for dynamic SELECT COUNT(*) queries).
c001 INTEGER;
v_result INTEGER := 0;
v_return NUMBER;
BEGIN
c001 := dbms_sql.open_cursor;
dbms_sql.parse(c001, p_command , dbms_sql.native );
dbms_sql.define_column(c001, 1, v_return);
v_result := dbms_sql.execute_and_fetch (c001);
dbms_sql.column_value(c001, 1, v_return);
dbms_sql.close_cursor(c001);
RETURN v_return;
EXCEPTION
WHEN OTHERS THEN
RETURN NULL;
END exec_select;
BEGIN -- update_leaf
v_row_count := exec_select('SELECT COUNT(*) FROM ' || p_tabName);
IF v_row_count = 0 THEN
--表中的记录为空时则直接跳过, 不执行将字段值更新为0的过程。
RETURN;
END IF;
v_tot_partitions := ROUND(v_row_count / c_Target_Rows_Per_Partition);
IF (v_tot_partitions <= 1) THEN
v_text := 'UPDATE ' || p_tabName || ' SET '|| p_colName || ' = 0';
exec(p_login_name, v_text, 'UPDATE_LEAF');
COMMIT;
ELSE
-- Partition only by ORG_UNIT_ID.
v_org_partitions := v_tot_partitions;
SELECT ROUND(count(*) / v_org_partitions)
INTO v_orgs_per_partition
FROM ofsa_detail_org_unit;
v_org_upper := -99999999999999;
FOR v_org_partition_num IN 1..v_org_partitions+1 LOOP
v_org_lower := v_org_upper;
IF v_org_partition_num = v_org_partitions+1 THEN
v_org_upper := 99999999999999;
ELSE
v_text :=
'SELECT org.leaf_node FROM ' ||
'(SELECT leaf_node, rownum rn' ||
' FROM ofsa_detail_org_unit' ||
' ORDER BY leaf_node) org ' ||
'WHERE org.rn = ' ||
TO_CHAR(v_org_partition_num * v_orgs_per_partition);
v_org_upper := exec_select(v_text);
IF v_org_upper IS NULL THEN
v_org_upper := 99999999999999;
END IF;
END IF;
-- 把表的新增leaf字段更新为0以org_unit_id的值为范围。
v_text := 'UPDATE ' || p_tabName || ' SET '|| p_colName || ' = 0' ||
' WHERE org_unit_id > '|| TO_CHAR(v_org_lower) ||
' AND org_unit_id <= ' || TO_CHAR(v_org_upper) ;
exec(p_login_name, v_text, 'UPDATE_LEAF');
COMMIT;
IF v_org_upper = 99999999999999 THEN
EXIT;
END IF;
END LOOP;
END IF;
END update_leaf;
/****************************************************************************
Procedure: ADD_COLUMN
Purpose: This procedure is called by ADD_LEAF to perform the DDL
for adding a column to a table
Arguments: p_login_name -- the User Name performing the DDL
v_tabName -- the table being altered
p_colName -- the column being added
****************************************************************************/
procedure add_column (p_login_name IN VARCHAR2, p_tabName IN VARCHAR2,
p_colName IN VARCHAR2) IS
BEGIN
exec(p_login_name, 'ALTER TABLE '||p_tabName||' add '||p_colName||
' NUMBER(14)', 'ADD_LEAF');
END add_column;
/****************************************************************************
Procedure: COLUMN_NOT_NULL
Purpose: This procedure is called by ADD_LEAF to perform the DDL
for modifying the new Leaf column to NOT NULL
Arguments: p_login_name -- the User Name performing the DDL
v_tabName -- the table being altered
p_colName -- the column being modified to NOT NULL
****************************************************************************/
procedure column_not_null (p_login_name IN VARCHAR2, p_tabName IN VARCHAR2,
p_colName IN VARCHAR2) IS
BEGIN
exec(p_login_name, 'ALTER TABLE '||p_tabName||' modify '||p_colName||
' not null', 'COLUMN_NOT_NULL');
END column_not_null;
/****************************************************************************/
PROCEDURE exec (p_login_name IN VARCHAR2,
p_command IN VARCHAR2,
p_proc_name IN VARCHAR2) IS
-- ==========================================================================
-- Executes the SQL statement passed to it, including DDL, using the
-- DBMS_SQL package for dynamic SQL, and logs the operation in the OFSA_STP
-- table. An error is raised if the SQL statement fails.
-- This procedure should only be used for executing DDL or other SQL that
-- must be built "on-the-fly". All other SQL statements should be executed
-- directly or in a declared cursor.
-- ==========================================================================
v_cursor INTEGER ;
v_result INTEGER := 0;
v_sqlerr NUMBER;
BEGIN
v_cursor := dbms_sql.open_cursor;
dbms_sql.parse(v_cursor, p_command , dbms_sql.native );
v_result := dbms_sql.execute (v_cursor);
dbms_sql.close_cursor(v_cursor);
BEGIN
INSERT into ofsa_stp (id, username, timestamp,
taskname, description, status)
VALUES (ofsa_stp_seq.nextval, p_login_name, sysdate,
p_proc_name, substr(p_command,1,198), v_result);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line(
'Error in posting SQL operation to OFSA_STP. ' ||
' USERNAME: ' || p_login_name ||
'TASKNAME: ' || p_proc_name ||
' SQL: ' || substr(p_command, 1, 198) );
END;
EXCEPTION
WHEN OTHERS THEN
v_sqlerr := sqlcode;
dbms_sql.close_cursor(v_cursor);
BEGIN
INSERT into ofsa_stp (id, username, timestamp,
taskname, description, status)
VALUES (ofsa_stp_seq.nextval, p_login_name, sysdate,
p_proc_name, substr(p_command,1,198), v_sqlerr);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line(
'Error in posting SQL operation to OFSA_STP. ' ||
'TASKNAME: ' || p_proc_name ||
' ERROR: ' || v_sqlerr ||
' SQL: ' || substr(p_command, 1, 140));
END;
RAISE;
END exec;
/****************************************************************************/
PROCEDURE big_stmt_exec (p_login_name IN VARCHAR2,
p_sqlSTMT IN DBMS_SQL.VARCHAR2S,
p_sqlLB IN NUMBER,
p_sqlUB IN NUMBER,
p_proc_name IN VARCHAR2) IS
-- ==========================================================================
-- Used for large SQL statements with linesize >2500
-- Executes the big SQL statement passed to it, including DDL, using the
-- DBMS_SQL package for dynamic SQL, and logs the operation in the OFSA_STP
-- table. An error is raised if the SQL statement fails.
-- This procedure should only be used for executing DDL or other SQL that
-- must be built "on-the-fly". All other SQL statements should be executed
-- directly or in a declared cursor.
-- ==========================================================================
v_cursor INTEGER ;
v_result INTEGER := 0;
v_sqlerr NUMBER;
BEGIN
--+ Open a cursor handle and parse the statement.
v_cursor := dbms_sql.open_cursor;
--dbms_output.put_line('sqlstmt1 '||p_sqlSTMT(1));
-- dbms_output.put_line('sqlstmt_last '||p_sqlSTMT(sqlUB-1));
--dbms_output.put_line('UB = '||p_sqlUB);
dbms_sql.parse(v_cursor,p_sqlSTMT,p_sqlLB,p_sqlUB-1,TRUE, --+not sure if this is needed but assume no harm done
dbms_sql.native);
--dbms_output.put_line('after the parse');
-- dbms_sql.parse(v_cursor, v_hold_long_text , dbms_sql.native );
v_result := dbms_sql.execute (v_cursor);
dbms_sql.close_cursor(v_cursor);
BEGIN
INSERT into ofsa_stp (id, username, timestamp,
taskname, description, status)
VALUES (ofsa_stp_seq.nextval, p_login_name, sysdate,
p_proc_name, substr(p_sqlSTMT(p_sqlLB),1,198), v_result);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line(
'Error in posting SQL operation to OFSA_STP. ' ||
' USERNAME: ' || p_login_name ||
'TASKNAME: ' || p_proc_name ||
' SQL: ' || substr(p_sqlSTMT(p_sqlLB), 198) );
END;
EXCEPTION
WHEN OTHERS THEN
v_sqlerr := sqlcode;
dbms_sql.close_cursor(v_cursor);
BEGIN
INSERT into ofsa_stp (id, username, timestamp,
taskname, description, status)
VALUES (ofsa_stp_seq.nextval, p_login_name, sysdate,
p_proc_name, substr(p_sqlSTMT(p_sqlLB),1,198), v_sqlerr);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line(
'Error in posting SQL operation to OFSA_STP. ' ||
'TASKNAME: ' || p_proc_name ||
' ERROR: ' || v_sqlerr ||
' SQL: ' || substr(p_sqlSTMT(p_sqlLB), 1, 140));
END;
RAISE;
END big_stmt_exec;
/****************************************************************************
Procedure: PARSE_SQL_STRING
Purpose: This procedure is called by modify_view to delimit
large sql statements into separate items within a
PL/SQL table. This allows large statements to be
dynamically executed, where the linesize would
otherwise be >2500 bytes.
Arguments: sql_string IN VARCHAR2
****************************************************************************/
procedure parse_sql_string ( p_sql_string IN VARCHAR2, p_viewName IN VARCHAR2,
p_sqlSTMT OUT DBMS_SQL.VARCHAR2S,
p_sqlUB OUT NUMBER) IS
v_sql_text varchar2(32700);
v_new_line number;
c_line_size CONSTANT number := 250; --+stmt table's column size
e_out_of_bounds EXCEPTION;
begin
p_sqlUB :=1; -- initialize the Upper bound
v_sql_text := p_sql_string;
--dbms_output.put_line('in the parse');
while v_sql_text is not null loop
v_new_line := instr(v_sql_text, chr(10));
--dbms_output.put_line('v_new_line ='||v_new_line);
if v_new_line = 0 then
v_new_line := c_line_size; --+no more delimiters
p_sqlSTMT(p_sqlUB) := substr(v_sql_text, 1, v_new_line-1);
elsif v_new_line > c_line_size then
v_new_line := c_line_size; --+just in case
p_sqlSTMT(p_sqlUB) := substr(v_sql_text, 1, v_new_line);
end if;
--+ Added extra line for debugging
p_sqlSTMT(p_sqlUB) := substr(v_sql_text, 1, v_new_line-1);
p_sqlUB := p_sqlUB + 1;
IF p_sqlUB > 256 THEN
RAISE e_out_of_bounds;
END IF;
v_sql_text := substr(v_sql_text, v_new_line+1);
end loop;
--dbms_output.put_line('outside the loop');
EXCEPTION
WHEN e_out_of_bounds THEN
dbms_output.put_line('View: '||p_viewName);
dbms_output.put_line('Exceeds limit of 256 explicit columns in SELECT list');
dbms_output.put_line('Use * in the SELECT list to reduce number of explicit columns');
dbms_output.put_line('in the view SELECT list');
end parse_sql_string;
BEGIN
--判断字段名长度不能大于30,显示名长度不能大于40,并且leaf类型只能为B或L.
IF LENGTH(p_colName) > 30 or LENGTH(p_displayName) > 40
or UPPER(p_leafType) NOT IN ('L','B') THEN
RAISE e_parameter;
END IF;
--如果为B,则执行该部分.
IF UPPER(p_leafType) = 'B' THEN
--检查所有的Instrument表。
OPEN bothTables;
LOOP
FETCH bothTables into v_tabName;
EXIT when bothTables%NOTFOUND;
--循环在表中增加字段。
add_column (p_login_name, v_tabName, p_colName);
--顶用FDM ADMIN的过程注册一个字段, 并且OFSA数据类型为Leaf
--原代码已经被封装
ofsa_ins_utils.ins_tab_column_record(v_tabName, UPPER(p_colName), 10, 1,
p_displayName, substr(p_dbfName,1,10),
p_displayName, v_err_num);
-- 如果表中已有数据把新增加的字段的值更新为默认的值0
update_leaf(p_login_name, v_tabName, p_colName);
--把表的字段设置为非空。
column_not_null(p_login_name, v_tabName, p_colName);
END LOOP;
CLOSE bothTables;
--查找所有相关的视图。
OPEN bothViews;
LOOP
FETCH bothViews into v_viewName;
EXIT when bothViews%NOTFOUND;
--更新所有的视图
modify_view (p_login_name, v_viewName, p_colName);
--调用FDM ADMIN存储过程来注册字段。 并把OFSA数据类型设置为Leaf.
ofsa_ins_utils.ins_tab_column_record(v_viewName, UPPER(p_colName), 10, 1,
p_displayName, substr(p_dbfName,1,10),
p_displayName, v_err_num);
END LOOP;
CLOSE bothViews;
--如果leaf类型为L,则只更新leadger_stat表,
ELSIF UPPER(p_leafType) = 'L' THEN
OPEN ledgerTables;
LOOP
FETCH ledgerTables into v_tabName;
EXIT when ledgerTables%NOTFOUND;
add_column (p_login_name, v_tabName, p_colName);
--调用FDM ADMIN存储过程注册字段,并将OFSA数据类型设置为Leaf.
ofsa_ins_utils.ins_tab_column_record(v_tabName, UPPER(p_colName), 10, 1,
p_displayName, substr(p_dbfName,1,10),
p_displayName, v_err_num);
--如果原表中有数据,则将新增加的leaf设置为0
update_leaf(p_login_name, v_tabName, p_colName);
-- 把字段类型该为非空字段。
column_not_null(p_login_name, v_tabName, p_colName);
END LOOP;
CLOSE ledgerTables;
END IF;
EXCEPTION
when e_parameter then
dbms_output.put_line('ERROR: Invalid Parameter');
dbms_output.put_line('Leaf Column Name must be <= 30 characters');
dbms_output.put_line('Display Name must be <= 40 characters');
dbms_output.put_line('Leaf Type must be in L or B');
END add_leaf;