Reference fields in cursor - oracle

I need to reference different fields from the same cursor, but I can't choose which name I'll display.
I already tried to put "... AS 'NAME1"
SET SERVEROUTPUT ON SIZE UNLIMITED;
SET FEEDBACK OFF;
DECLARE
CURSOR GET_TOOLKITS
IS
SELECT PROJ.NAME -- first name
, PROJ.SHORT_NAME
, SNAP.NAME SNAP_NAME --second name
, BRAN.NAME AS BRANCH_NAME -- third name
, SNAP.SNAPSHOT_ID
, PROJ.PROJECT_ID
, TRUNC(PROJ.CREATED_ON)
FROM LSW_SNAPSHOT SNAP
, LSW_PROJECT PROJ
, LSW_BRANCH BRAN
WHERE 1=1
AND SNAP.PROJECT_ID = PROJ.PROJECT_ID
AND SNAP.BRANCH_ID = BRAN.BRANCH_ID
AND PROJ.IS_TOOLKIT = 'T'
AND SNAP.IS_ARCHIVED = 'F'
AND SNAP.NAME IS NOT NULL;
CURSOR GET_SNAPSHOTS
IS
SELECT PROJ.NAME
, PROJ.SHORT_NAME
, SNAP.NAME SNAP_NAME
, BRAN.NAME AS BRANCH_NAME
, SNAP.SNAPSHOT_ID
, PROJ.PROJECT_ID
, TRUNC(PROJ.CREATED_ON)
FROM LSW_SNAPSHOT SNAP
, LSW_PROJECT PROJ
, LSW_BRANCH BRAN
WHERE 1=1
AND SNAP.PROJECT_ID = PROJ.PROJECT_ID
AND SNAP.BRANCH_ID = BRAN.BRANCH_ID
AND PROJ.IS_TOOLKIT = 'F'
AND SNAP.IS_ARCHIVED = 'F'
AND SNAP.NAME IS NOT NULL;
SET_TOOLKITS GET_TOOLKITS%ROWTYPE;
SET_SNAPSHOTS GET_SNAPSHOTS%ROWTYPE;
V_COUNT NUMBER:=0;
BEGIN
OPEN GET_TOOLKITS;
LOOP
FETCH GET_TOOLKITS INTO SET_TOOLKITS;
EXIT WHEN GET_TOOLKITS%NOTFOUND;
V_COUNT := V_COUNT+1;
DBMS_OUTPUT.PUT_LINE (SET_TOOLKITS.NAME --ref first name
|| ' '
|| SET_TOOLKITS.NAME --ref second name
|| ' '
|| SET_TOOLKITS.NAME --ref third name
|| ' ');
END LOOP;
END;
And if anyone can help me with that condition "IS_TOOLKIT" to dynamically change I would be thankful.

Should be this one:
DBMS_OUTPUT.PUT_LINE (SET_TOOLKITS.NAME --ref first name
|| ' '
|| SET_TOOLKITS.SNAP_NAME --ref second name
|| ' '
|| SET_TOOLKITS.BRANCH_NAME --ref third name
|| ' ');

Related

ORA-00933: SQL command not properly ended when running SP

I'm troubled with this error when running SP, I don't know why. pls help me
error in line AND thlt.CODE = ' || p_CODE|| ' when CODE is varchar2
A_ID NUMBER, P_A_Ids VARCHAR2, v_expression VARCHAR2, p_CODE VARCHAR2
...
OPEN v_cursor FOR
' SELECT thltCt.A_ID A_ID,
Sum( ' || v_expression || ' ) "VALUE"
FROM tableA thlt join tableB thltCt on thlt.ID = thltCt.THLT_ID
WHERE thlt.LS IS NOT NULL
AND thlt.CODE = ' || p_CODE|| '
AND thltCt.A_ID IN (' || P_A_Ids || ' )
GROUP BY (thltCt.A_ID)';
From the comments i think it will work like this :
' SELECT thltCt.A_ID A_ID,
Sum( ' || v_expression || ' ) "VALUE"
FROM tableA thlt join tableB thltCt on thlt.ID = thltCt.THLT_ID
WHERE thlt.LS IS NOT NULL
AND thlt.CODE = '' ' || p_CODE|| ' ''
AND thltCt.A_ID IN ( ' || P_A_Ids || ' )
GROUP BY (thltCt.A_ID)';

SQL Error: ORA-00984: column not allowed here 00984. 00000 - "column not allowed here"

i am getting error:
Error at Command Line : 45 Column : 111
Error report -
SQL Error: ORA-00984: column not allowed here
00984. 00000 - "column not allowed here"
query:
Insert into BL_DIFF_QUERY_ANL (TABLE_NAME,ISSUSE,ISSUE_CATEGORY,ISSUE_ID,DB_QUERY)
values ('BILL','TOTAL_BILLED_ADJUST_MISMATCH','TOTAL_BILLED_ADJUST',2,
'DECLARE
V_DIFF_AMT_chr varchar2(20);
V_DIFF_AMT NUMBER(9,2);
V_SUM_ACTV_AMT NUMBER(9,2);
V_SUM_ACTV_TAXES_AMT NUMBER(9,2);
V_COUNT NUMBER(1);
v_val_Done_by varchar(25) ;
v_ban number(10) := 339339856;
v_comments varchar(20);
v_success varchar(10) := ''SUCCESS'';
v_yesnoind varchar(1) := ''Y'';
v_immediate_Adj varchar (30) := ''IMMEDIATE_ADJUSTMENT'';
v_issue_Desc varcahr(50) := ''TOTAL_BILLED_ADJUST_MISMATCH'';
BEGIN
DBMS_OUTPUT.Put_line (''BAN: '' || :1 || '' ACTV_BILL_SEQ_NO : '' || :2 || '' SUBSCRIBER: '' || :3
|| ''COLUMN_NAME : '' || :4 || '' COLUMN_DATA: '' || :5 || ''DIFF_DATA : '' || :6 || '' SOC: '' ||
:7 || '' FEATURE_CODE: '' || :8 || '' FTR_REVENUE_CODE: '' || :9 || '' PRIOD_CVRG_ST_DATE: '' || :10
|| '' PRIOD_CVRG_ND_DATE: '' || :11 || '' ACTV_REASON_CODE: '' || :12 || ''BALANCE_IMPACT_CODE: ''
|| :13 || '' SOURCE_APPL_CODE : '' || :14 || '' DISCOUNT_CD: '' || :15 || '' BILL_MEDIA : '' || :16
|| '' BILL_FORMAT : '' || :17 || '' PRODUCT_TYPE: '' || :18 || '' FTR_TYPE '' || :19 || '' VAL_ID ''
|| :20 );
select ''comments_''|| :20 into v_comments from dual;
select ''val_done_by_''|| :20 into v_VAL_DONE_BY from dual;
SELECT COUNT (distinct tax_ind ) into V_COUNT FROM SERVICE_AGREEMENT WHERE BAN = :1 and SERVICE_TYPE
= ''P'' and EXPIRATION_DATE is NULL and tax_ind = ''TI'';
DBMS_OUTPUT.Put_line (V_COUNT);
if (V_COUNT = 0)
then
SELECT ABS ( (nvl(sum(t1.ACTV_AMT),0) - nvl(sum(t2.ACTV_AMT),0)) + (nvl(sum(t1.TAX_CITY_CUST_AMT),0)
- nvl(sum(t2.TAX_CITY_CUST_AMT),0)) + (nvl(sum(t1.TAX_COUNTY_CUST_AMT),0)
nvl(sum(t2.TAX_COUNTY_CUST_AMT),0)) + (nvl(sum(t1.TAX_STATE_CUST_AMT),0) -
nvl(sum(t2.TAX_STATE_CUST_AMT),0)) + (nvl(sum(t1.TAX_FEDERAL_AMT),0) -
nvl(sum(t2.TAX_FEDERAL_AMT),0)) + (nvl(sum(t1.TAX_ROAMING_AMT),0) - nvl(sum(t2.TAX_ROAMING_AMT),0))
) into V_SUM_ACTV_TAXES_AMT
from QATAPP50.ADJUSTMENT t1 ,QATAPP55.ADJUSTMENT#abc_test t2 where t1.BAN = :1 and t2.ban=t1.ban and
t1.ACTV_BILL_SEQ_NO = :2 and t2.ACTV_BILL_SEQ_NO = t1.ACTV_BILL_SEQ_NO
and t1.balance_impact_code = ''I'' and t2.balance_impact_code = t1.balance_impact_code and
nvl(t1.CHARGE_SEQ_NO,0) = nvl(t2.CHARGE_SEQ_NO,0);
else
SELECT ABS( nvl(sum(t1.ACTV_AMT),0) - nvl(sum(t2.ACTV_AMT),0) ) into V_SUM_ACTV_AMT
from QATAPP50.ADJUSTMENT t1 ,QATAPP55.ADJUSTMENT#abc_test t2 where t1.BAN = :1
and t2.ban=t1.ban and t1.ACTV_BILL_SEQ_NO = :2 and t2.ACTV_BILL_SEQ_NO = t1.ACTV_BILL_SEQ_NO
and t1.balance_impact_code = ''I'' and t2.balance_impact_code = t1.balance_impact_code
and nvl(t1.CHARGE_SEQ_NO,0) = nvl(t2.CHARGE_SEQ_NO,0);
end if;
DBMS_OUTPUT.Put_line (V_SUM_ACTV_TAXES_AMT);
DBMS_OUTPUT.Put_line (V_SUM_ACTV_AMT);
if ( V_SUM_ACTV_AMT = V_DIFF_AMT or V_SUM_ACTV_TAXES_AMT = V_DIFF_AMT )
then
execute immediate ''UPDATE BL_DIFF_CATEGORY SET VALIDATION_STS = :2' ||','|| v_VAL_DONE_BY || '='||
:3 ||','|| v_comments || '= :4 where BAN = :1 and
DIFF_TYPE = :5
'' using v_issue_Desc,v_immediate_Adj,v_yesnoind,v_success,v_ban;
COMMIT;
END IF;
END;'
);
going by the line and column no,the error is at v_comments in execute immediate statement.
basically i want dynamic column name for v_VAL_DONE_BY and v_Comments.
the same execute immediate is working in standalone pl sql block. seems some problem with use of quotes in execute statement
could you please help?
Are you running this insert statement in a PL/SQL block? You are using variables in your query that SQL would take for column names (that don't exist) and raise the error. If you are running this code in PL/SQL, then you must of course declare the variables and do something about the bind variable :3 as mentioned by BJones in the comments to your request.
SQL:
Insert into BL_DIFF_QUERY_ANL (TABLE_NAME,ISSUSE,ISSUE_CATEGORY,ISSUE_ID,DB_QUERY)
values ('BILL','TOTAL_BILLED_ADJUST_MISMATCH','TOTAL_BILLED_ADJUST',2,
...
execute immediate ''UPDATE BL_DIFF_CATEGORY SET VALIDATION_STS = :2,' ||
:v_VAL_DONE_BY ||
'='||
:3 ||
','||
:v_comments ||
'= :4 where BAN = :1 and
...
);
PL/SQL
declare
v_VAL_DONE_BY varchar2(4000);
v_comments varchar2(4000);
begin
Insert into BL_DIFF_QUERY_ANL (TABLE_NAME,ISSUSE,ISSUE_CATEGORY,ISSUE_ID,DB_QUERY)
values ('BILL','TOTAL_BILLED_ADJUST_MISMATCH','TOTAL_BILLED_ADJUST',2,
...
execute immediate ''UPDATE BL_DIFF_CATEGORY SET VALIDATION_STS = :2,' ||
v_VAL_DONE_BY ||
'= :3 ,'||
v_comments ||
'= :4 where BAN = :1 and
...
);
end;
Update: SQL again
Looking closer at the variable names you are using, I've come to the conclusion that the variables shall be part of the SQL string that you are inserting:
...
execute immediate ''UPDATE BL_DIFF_CATEGORY SET VALIDATION_STS = :2, '' ||
v_VAL_DONE_BY || '' = :3, '' || v_comments || '' = :4 where BAN = :1 and DIFF_TYPE = :5''
using v_issue_Desc,v_immediate_Adj,v_yesnoind,v_success,v_ban;
COMMIT;
END IF;
END;'
);
Explantion
You want to insert code into your table. This code is a PL/SQL script with variables and strings. This is a small example for such PL/SQL code:
declare
v_number integer := 5;
begin
dbms_output.put_line('The number is: ' || v_number);
end;
You want to insert this as a string. But if you merely put single quotes at the start and the end as in
insert into mytable (mycode) values
(
'declare
v_number integer := 5;
begin
dbms_output.put_line('Number ' || v_number);
end;'
);
this doesn't work, because the code itself contains single quotes. The DBMS reads the string from the starting quote to the next quote it finds, in this example right before Number. So the DBMS sees a string followed by the word Number and doesn't know what to do with this. Number looks like a column name, but a column name doesn't belong there. (You can see from the syntax highlighting that the DBMS sees two strings with the woprd Number in between.)
The solution is to mask all single quotes with another single quote (i.e. replace ' with '' in your editor):
insert into mytable (mycode) values
(
'declare
v_number integer := 5;
begin
dbms_output.put_line(''Number '' || v_number);
end;'
);
All single quote pairs are seen as part of the string now representing a single quote within. The DBMS sees a single string that it can insert into the table now. (You can see from the syntax highlighting that this is one string now.)
In your code you used single quote pairs in some situations but not in all. Maybe you did this just manually instead of search & replace in an editor and thus missed some occurrences.

Oracle PLSQL Write output query on a file

I m trying to generate the Data Insert statments of a table uding this program.
When I execute the program, I have this statement in the myfile.txt, while I expect to get the list of insert statments
SELECT
'INSERT INTO TAB_PARAMS (ID,CODE,VALUE,ENV,COMMENT) VALUES ('
|| id
|| ','
|| ''''
|| code
|| ''''
|| ','
|| ''''
|| value
|| ''''
|| ','
|| ''''
|| env
|| ''''
|| ','
|| ''''
|| comment
|| ''''
|| ');' AS "insert.sql"
FROM
tab_params;
The programm I use is:
SET SERVEROUTPUT ON;
DECLARE
CURSOR column_names_cur
IS
SELECT column_name
FROM user_tab_columns
WHERE table_name = UPPER('&&1')
ORDER BY column_id ;
CURSOR col_select_cur
IS
SELECT DECODE(data_type ,
'DATE' , ''''||'TO_DATE('||''''||'||'||''''||''''||''''||''''||'||'||'TO_CHAR(' ,
'NUMBER', NULL,
'VARCHAR2', ''''''''''||'||' ,
'XMLTYPE', ''''||'xmltype('||''''||'||'||''''||''''||''''||''''||'||')
||column_name
||DECODE (data_type ,
'DATE' , ','||''''||'dd-mon-yyyy hh24:mi:ss'||''''||')'||'||'||''''||''''||''''||''''||'||'||''''||','||''''||'||'||''''||''''||''''||''''||'||'||''''||'dd-mon-yyyy hh24:mi:ss'||''''||'||'||''''||''''||''''||''''||'||'||''''||')'||'''' ,
'NUMBER', NULL ,
'VARCHAR2' , '||'||''''||''''||''''||'''' ,
'XMLTYPE', '||'||''''||''''||''''||''''||'||'||''''||')'||'''' ) sel
FROM user_tab_columns
WHERE table_name = UPPER('&&1')
ORDER BY column_id ;
w_sql_start VARCHAR2(4000) ;
w_sql VARCHAR2(4000) ;
w_ok BOOLEAN ;
w_file_handle utl_file.file_type ;
w_err_text VARCHAR2(500) ;
w_col_sep VARCHAR2(400) := '||'',''||' ;
BEGIN
-- obtain the start of the sql statement
FOR column_names_rec IN column_names_cur
LOOP
w_sql_start := w_sql_start ||column_names_rec.column_name || ',' ;
DBMS_OUTPUT.put_line('00'||w_sql_start);
END LOOP ;
DBMS_OUTPUT.put_line('01'||w_sql_start);
w_sql_start := 'SELECT ''INSERT INTO &&1 ('|| RTRIM (w_sql_start,',') ||') VALUES (' ||''''||'||';
DBMS_OUTPUT.put_line('02'||w_sql_start);
-- obtain individual columns
FOR col_select_rec IN col_select_cur
LOOP
w_sql := w_sql || col_select_rec.sel ||w_col_sep ;
DBMS_OUTPUT.put_line('A0'||w_sql);
END LOOP ;
IF w_sql IS NOT NULL
THEN
w_sql := SUBSTR(w_sql, 1, LENGTH(w_sql)-7) ;
w_sql := w_sql_start || w_sql ||'||'||''''||');'||''''||' AS "insert.sql" FROM &&1 ;' ;
DBMS_OUTPUT.put_line('A1'||w_sql);
ELSE
w_sql := 'SELECT ''Table &&1 not found.'' AS "insert.sql" FROM DUAL; ' ;
END IF ;
-- write the select statement which will genearte the insert statement to a file on the unix box
--w_file_handle := utl_file.fopen ('/app/webrep/webreports', -- directory ,
w_file_handle := utl_file.fopen ('ALERT_DIR', -- directory ,
'myfile.txt' , -- filename ,
'W') ;
utl_file.putf(w_file_handle ,
'%s' ,
w_sql) ;
utl_file.fclose(w_file_handle) ;
END ;
/
I m on oracle 11gR2
You are writing the SELECT statement into the file instead of the values returned from select.
So, I suggest you do the following.
Include the following in your DECLARE block.
ref_cur sys_refcursor;
v_insert_query VARCHAR2(1000);
And instead of simply writing this,
utl_file.putf(w_file_handle ,
'%s' ,
w_sql) ;
Fetch the inserts within w_sql in a loop using REF CURSOR
OPEN ref_cur FOR w_sql;
LOOP;
FETCH ref_cur INTO v_insert_query;
EXIT WHEN ref_cur%NOTFOUND;
utl_file.putf(w_file_handle ,
'%s' ,
v_insert_query) ;
END LOOP;

Porting from Oracle to Postgres

Earlier our hosted environments were on Oracle but now we have recently switched to postgres. Porting the tables I believe was successfully since it has been working correct for a long time. I have been actually struggling with porting a procedure but is not working for some reason. I went through the documentation part of the ora2pg but I am not able to crack, which I believe is the last piece of the puzzle.
I've started with this one, which looks like this in Oracle:
create or replace
procedure c_audit(anonymous in boolean, aud_level IN varchar)
AUTHID CURRENT_USER IS
script varchar2(32000);
acc_select varchar(100);
open_cursor integer;
returnval integer;
p_id integer;
a_id integer;
p_name varchar2(100);
a_name varchar2(100);
v_count integer;
c_count integer;
doc_count integer;
curr_user varchar(100);
begin
for i in (select a.a_name a_name, a.a_id, p.name p_name, p.id p_id, ds.username username
from c_account a
inner join c_pro p on a.a_id = p.a_id
inner join c_dat_ds_xref x on p.id = x.p_id
inner join c_data ds on x.id_datasource = ds.id
inner join c_conntypes ct on x.id_conntype = ct.id_conntype
where ct.typeid = 'CAPTURE'
order by a.a_name, p.name)
LOOP
curr_user := i.username;
IF anonymous = true
THEN
acc_select := 'select ' || '''' || i.a_id || '''' || ' a_id,' || '''' || i.p_id || '''' || ' p_id';
ELSE
acc_select := 'select ' || '''' || i.a_name || '''' || ' a_name,' || '''' || i.p_name || '''' || ' p_name';
END IF;
IF upper(aud_level) = 'VERBATIM'
THEN
script:= acc_select || '
, count(distinct d.document_id) docCount
, sum(case when v.document_id is null or v.verbatim_type_value = ''NO_VERBATIM_TEXT'' then 0 else 1 end) VerbCount
, sum(case when v.document_id is null then (select to_number(prop_value) verbSize
from c_properties
where prop_name = ''METERING.STRUCT.ONLY.CHARGE''
and id_pro = 0) else v.credits end) CreditCount
from ' || i.username || '.p_document d
left outer join (
select vi.document_id, t.verbatim_type_value
, case when dbms_lob.substr(vi.extracted_original,8) = ''<cbnull>''
or t.verbatim_type_value = ''NO_VERBATIM_TEXT''
then coalesce(s2.strucCredit, .25)
else ceil(vi.extracted_original_size/coalesce(s.verbSize, 2048)) end credits
from ' || i.username || '.p_verbatim vi
left outer join ' || i.username || '.pd_verbatim_type t on vi.verbatim_type_id = t.verbatim_type_id
, (
select to_number(prop_value) verbSize
from c_properties
where prop_name = ''METERING.MAXSIZE.VERBATIM''
and id_pro = 0
) s
, (
select to_number(prop_value) strucCredit
from c_properties
where prop_name = ''METERING.STRUCT.ONLY.CHARGE''
and id_pro = 0
) s2
) v on d.document_id = v.document_id';
ELSE
IF upper(aud_level) = 'DOCUMENT'
THEN
script:= acc_select || '
, count(distinct a.document_id) docCount
, sum(credits) creditCount
from (
select d.document_id, ceil(sum(v.extracted_original_size)/coalesce(s.verbSize,2048)) credits
from ' || i.username || '.p_document d
inner join ' || i.username || '.p_verbatim v on d.document_id = v.document_id
inner join ' || i.username || '.pd_verbatim_type t on v.verbatim_type_id = t.verbatim_type_id
, (
select to_number(prop_value) verbSize
from c_properties
where prop_name = ''METERING.MAXSIZE.VERBATIM''
and id_pro = 0
) s
where t.verbatim_type_value <> ''NO_VERBATIM_TEXT''
and dbms_lob.substr(v.extracted_original,8) <> ''<cbnull>''
group by d.document_id, s.verbSize
union
select d.document_id, coalesce(s2.strucCredit, .25)
from ' || i.username || '.p_document d
, (
select to_number(prop_value) strucCredit
from c_properties
where prop_name = ''METERING.STRUCT.ONLY.CHARGE''
and id_pro = 0
) s2
where d.document_id not in (select distinct v.document_id from ' || i.username || '.p_verbatim v)
union
select distinct d.document_id, coalesce(s2.strucCredit, .25)
from ' || i.username || '.p_document d
inner join ' || i.username || '.p_verbatim v on d.document_id = v.document_id
inner join ' || i.username || '.pd_verbatim_type t on v.verbatim_type_id = t.verbatim_type_id
, (
select to_number(prop_value) strucCredit
from c_properties
where prop_name = ''METERING.STRUCT.ONLY.CHARGE''
and id_pro = 0
) s2
where (t.verbatim_type_value = ''NO_VERBATIM_TEXT''
or dbms_lob.substr(v.extracted_original,8) = ''<cbnull>'')
) a';
ELSE
dbms_output.put_line('Invalid choice for audit level, no audit generated');
exit;
END IF;
END IF;
begin
open_cursor := dbms_sql.open_cursor;
DBMS_SQL.PARSE(open_cursor, script,
DBMS_SQL.NATIVE);
IF anonymous = true then
dbms_sql.define_column(open_cursor,1,a_id);
dbms_sql.define_column(open_cursor,2,p_id);
else
dbms_sql.define_column(open_cursor,1,a_name,100);
dbms_sql.define_column(open_cursor,2,p_name,100);
end if;
dbms_sql.define_column(open_cursor,3,doc_count);
dbms_sql.define_column(open_cursor,4,v_count);
IF upper(aud_level) = 'VERBATIM' then
dbms_sql.define_column(open_cursor,5,c_count);
end if;
returnval := DBMS_SQL.EXECUTE(open_cursor);
loop
if dbms_sql.fetch_rows(open_cursor) > 0 then
IF anonymous = true then
dbms_sql.column_value(open_cursor,1,a_id);
dbms_sql.column_value(open_cursor,2,p_id);
dbms_sql.column_value(open_cursor,3,doc_count);
dbms_sql.column_value(open_cursor,4,v_count);
IF upper(aud_level) = 'VERBATIM' then
dbms_sql.column_value(open_cursor,5,c_count);
dbms_output.put_line(a_id || ',' || p_id || ',' || doc_count || ',' || v_count || ',' || c_count);
else
dbms_output.put_line(a_id || ',' || p_id || ',' || doc_count || ',' || v_count);
end if;
else
dbms_sql.column_value(open_cursor,1,a_name);
dbms_sql.column_value(open_cursor,2,p_name);
dbms_sql.column_value(open_cursor,3,doc_count);
dbms_sql.column_value(open_cursor,4,v_count);
IF upper(aud_level) = 'VERBATIM' then
dbms_sql.column_value(open_cursor,5,c_count);
dbms_output.put_line(a_name || ',' || p_name || ',' || doc_count || ',' || v_count || ',' || c_count);
else
dbms_output.put_line(a_name || ',' || p_name || ',' || doc_count || ',' || v_count);
end if;
end if;
else
exit;
end if;
end loop;
exception
when others then
--dbms_output.put_line('Error occured. Please check if the current user has Select access to table ' || curr_user || '.p_document ' || curr_user || '.p_verbatim ' || curr_user || '.pd_verbatim_type');
dbms_output.put_line('Error occured. Please login as ' || curr_user || ' and run the following:');
dbms_output.put_line('GRANT SELECT ON ' || curr_user || '.P_DOCUMENT to ' || user ||';');
dbms_output.put_line('GRANT SELECT ON ' || curr_user || '.P_VERBATIM to ' || user ||';');
dbms_output.put_line('GRANT SELECT ON ' || curr_user || '.pd_verbatim_type to ' || user ||';');
end;
end loop;
end;
Does this procedure appears correct with respect to syntax?
CREATE OR REPLACE FUNCTION c_audit(anonymous boolean, aud_level text) RETURNS VOID AS $body$
DECLARE
script text;
acc_select text;
returnval integer;
p_id integer;
a_id integer;
i record;
p_name text;
a_name text;
v_count integer;
c_count integer;
doc_count integer;
curr_user text;
BEGIN
for i in (SELECT a.a_name a_name, a.a_id, p.name p_name, p.id p_id, ds.username username
from c_account a
inner join c_pro p on a.a_id = p.a_id
inner join c_dat_ds_xref x on p.id = x.p_id
inner join c_data ds on x.id_datasource = ds.id
inner join c_conntypes ct on x.id_conntype = ct.id_conntype
where ct.typeid = 'CAPTURE'
order by a.a_name, p.name)
LOOP
curr_user := i.username;
IF anonymous = true
THEN
acc_select := 'SELECT ' || '''' || i.a_id || '''' || ' AccountID,' || '''' || i.p_id || '''' || ' ProjectID';
ELSE
acc_select := 'SELECT ' || '''' || i.a_name || '''' || ' AccountName,' || '''' || i.p_name || '''' || ' ProjectName';
END IF;
IF upper(aud_level) = 'VERBATIM'
THEN
script:= acc_select || '
, count(distinct d.document_id) docCount
, sum(case when coalesce(CAST(v.document_id AS text), '') = '' or v.verbatim_type_value = ''NO_VERBATIM_TEXT'' then 0 else 1 end) VerbCount
, sum(case when coalesce(CAST(v.document_id AS text), '') = '' then (SELECT to_number(prop_value,''9999.99'') verbSize
from c_properties
where prop_name = ''METERING.STRUCT.ONLY.CHARGE''
and id_project = 0) else v.credits end) CreditCount
from ' || i.username || '.p_document d
left outer join (
SELECT vi.document_id, t.verbatim_type_value
, case when substr(vi.extracted_original,8) = ''<cbnull>''
or t.verbatim_type_value = ''NO_VERBATIM_TEXT''
then coalesce(s2.strucCredit, .25)
else ceil(vi.extracted_original_size/coalesce(s.verbSize, 2048)) end credits
from ' || i.username || '.p_verbatim vi
left outer join ' || i.username || '.pd_verbatim_type t on vi.verbatim_type_id = t.verbatim_type_id
, (
select to_number(prop_value,''9999.99'') verbSize
from c_properties
where prop_name = ''METERING.MAXSIZE.VERBATIM''
and id_project = 0
) s
, (
select to_number(prop_value,''9999.99'') strucCredit
from c_properties
where prop_name = ''METERING.STRUCT.ONLY.CHARGE''
and id_project = 0
) s2
) v on d.document_id = v.document_id';
SELECT format(script) into script;
ELSE IF upper(aud_level) = 'DOCUMENT'
THEN
script:= acc_select || '
, count(distinct a.document_id) docCount
, sum(credits) creditCount
from (
SELECT d.document_id, ceil(sum(v.extracted_original_size)/coalesce(s.verbSize,2048)) credits
from ' || i.username || '.p_document d
inner join ' || i.username || '.p_verbatim v on d.document_id = v.document_id
inner join ' || i.username || '.pd_verbatim_type t on v.verbatim_type_id = t.verbatim_type_id
, (
SELECT to_number(prop_value,''9999.99'') verbSize
from c_properties
where prop_name = ''METERING.MAXSIZE.VERBATIM''
and id_project = 0
) s
where t.verbatim_type_value <> ''NO_VERBATIM_TEXT''
and substr(v.extracted_original,8) <> ''<cbnull>''
group by d.document_id, s.verbSize
union
select d.document_id, coalesce(s2.strucCredit, .25)
from ' || i.username || '.p_document d
, (
select to_number(prop_value,''9999.99'') strucCredit
from c_properties
where prop_name = ''METERING.STRUCT.ONLY.CHARGE''
and id_project = 0
) s2
where d.document_id not in (select distinct v.document_id from ' || i.username || '.p_verbatim v)
union
select distinct d.document_id, coalesce(s2.strucCredit, .25)
from ' || i.username || '.p_document d
inner join ' || i.username || '.p_verbatim v on d.document_id = v.document_id
inner join ' || i.username || '.pd_verbatim_type t on v.verbatim_type_id = t.verbatim_type_id
, (
select to_number(prop_value,''9999.99'') strucCredit
from c_properties
where prop_name = ''METERING.STRUCT.ONLY.CHARGE''
and id_project = 0
) s2
where (t.verbatim_type_value = ''NO_VERBATIM_TEXT''
or substr(v.extracted_original,8) = ''<cbnull>'')
) a';
SELECT format(script) into script;
ELSE
SELECT format(script) into script;
exit;
END IF;
END IF;
BEGIN
IF anonymous = true
THEN
IF upper(aud_level) = 'VERBATIM'
THEN
EXECUTE script into a_id, p_id, doc_count, v_count, c_count;
ELSE
EXECUTE script into a_id, p_id, doc_count, c_count;
END IF;
ELSE
IF upper(aud_level) = 'VERBATIM'
THEN
EXECUTE script into a_name, p_name, doc_count, v_count, c_count;
ELSE
EXECUTE script into a_name, p_name, doc_count, c_count;
END IF;
END IF;
GET DIAGNOSTICS returnval := ROW_COUNT;
LOOP
IF returnval > 0
THEN
IF anonymous = true
THEN
IF upper(aud_level) = 'VERBATIM'
THEN
SELECT format ('Information %s, %s, %s, %s, %s', a_id, p_id, doc_count, v_count, c_count);
ELSE
SELECT format ('Information %s, %s, %s, %s', a_id, p_id, doc_count, c_count);
END IF;
ELSE
IF upper(aud_level) = 'VERBATIM'
THEN
SELECT format ('Information %s, %s, %s, %s, %s', a_name, p_name, doc_count, v_count, c_count);
ELSE
SELECT format ('Information %s, %s, %s, %s', a_name, p_name, doc_count, c_count);
END IF;
END IF;
ELSE
EXIT;
END IF;
END LOOP;
EXCEPTION
WHEN others THEN
PERFORM format('Error occured. Please login as %s, %s' , curr_user , ' and run the following:');
PERFORM format('GRANT SELECT ON %s.P_DOCUMENT to %s', curr_user, user);
PERFORM format('GRANT SELECT ON %s.P_VERBATIM to %s', curr_user, user);
PERFORM format('GRANT SELECT ON %s.pd_verbatim_type to %s', curr_user, user);
END;
END LOOP;
END;
$body$
LANGUAGE PLPGSQL
;
ALTER FUNCTION cb_audit(boolean, text) OWNER TO USER;
-- REVOKE ALL ON FUNCTION cb_audit FROM PUBLIC;
The error for failure I get is -
ERROR: too many parameters specified for RAISE
Where: PL/pgSQL function "cb_audit" line 145 at RAISE
I found this to be a good link which I used as a reference
I believe that for porting DBMS_OUTPUT.PUT_LINE, RAISE NOTICE should be the right way. I had encountered another error which was for format of the - to_number(prop_value,'9999.99') which appears right as per the syntax mentioned here but then for some reason when I switched to to_number(prop_value,''9999.99''), I did not get the error but not sure why that should be or even if it should work correctly.
The version of Postgres -
PostgreSQL 9.1.10 on x86_64-unknown-linux-gnu, compiled by gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-3), 64-bit
Edited:
I actually attempted to modify this function based on the suggestions from Patrick but due to some reason it is not displaying anything on the screen. I added format() at the end of every script to display the script as but it just executed the code and displays c_audit and null. Though I execute the individual subsql and they do return expected counts and results. Am I missing anything?
You are using two types of string "building": the concatenation || operator and the % placeholder. Both are perfectly legal, but use of the format() function is preferred because the code is cleaner and PG guards against things like SQL-injection behind the scenes.
The error you are getting is that one of your RAISE NOTICE commands has a number of % different from the arguments you supply; forgive me for not looking that up but line 145 is hard to find here on SO. In general, you should rewrite all of them like so:
RAISE NOTICE format('GRANT SELECT ON %I.pd_verbatim_type to %I', curr_user, user);
The %I placeholder takes a SQL identifier as input: it can not be NULL and it will be properly quoted to avoid SQL-injection and keyword collisions. (From your code I take it that curr_user is a schema name and user is a role name, both SQL identifiers.)
Note also that the PERFORM statement in a PL/pgSQL function is a SELECT statement that does not return data, but it is executed to examine a side effect, such as asserting that some data exists. As a consequence, there is no PERFORM privilege, use GRANT SELECT instead.
When quoting in PostgreSQL, SQL-identifiers use double quotes, while string literal values use single quotes. The to_number() function definitely requires single quotes.
A few more points to improve your code:
(1) The two sub-selects in the dynamic query like this one...
'select to_number(prop_value,''9999.99'') strucCredit
from c_properties
where prop_name = ''METERING.STRUCT.ONLY.CHARGE''
and id_project = 0'
... are STABLE: you always get the same result. Instead of leaving them in the dynamic SQL, create two variables and put the results in them before you do anything else:
DECLARE
...
verbSize numeric;
strucCredit numeric;
BEGIN
SELECT to_number(prop_value,'9999.99') INTO verbSize
FROM c_properties
WHERE prop_name = 'METERING.MAXSIZE.VERBATIM' AND id_pro = 0;
SELECT to_number(prop_value,'9999.99') INTO strucCredit
FROM c_properties
WHERE prop_name = 'METERING.STRUCT.ONLY.CHARGE' AND id_pro = 0;
...
Then use the varibale verbSize and strucCredit in your dynamic SQL. Like this you do these queries just once, instead of a few times for every iteration.
(2) All of the dynamic queries need a GROUP BY 1, 2 clause.
(3) The clause CASE WHEN coalesce(CAST(v.document_id AS text), '') = '' ... should be written like CASE WHEN v.document_id IS NULL ..., assuming that v.document_id can not be an empty string.
(4) You changed your RAISE NOTICE statements to SELECT format(...). That latter form produces no output, use RAISE NOTICE format(...) instead.
(5) Rewrite your dynamic SQL to use the format() function too.

Strange behaviours with oracle nested cursors

Below is stored procedure I have written which used nested cursor.
create or replace
PROCEDURE SP_RUN_EMPLOYEE_UPDATES
(
IN_DATE IN VARCHAr2
)
IS
update_sql varchar2(4000);
employee_id BI_EMPLOYEE_UPDATE.employee_id%TYPE;
effective_date date ;
created_by number;
created_on date;
comments varchar2(4000);
CURSOR
employees
IS
SELECT distinct(employee_id) FROM BI_EMPLOYEE_UPDATE WHERE EFFECTIVE_DATE = to_date(IN_DATE,'dd-mm-yy') AND EXECUTED = 'N' AND ACTIVITY_ID = '0';
CURSOR
e_updates
IS
SELECT * FROM BI_EMPLOYEE_UPDATE WHERE EFFECTIVE_DATE = to_date(IN_DATE,'dd-mm-yy') AND EXECUTED = 'N' AND ACTIVITY_ID = '0' and employee_id = employee_id ;
BEGIN
OPEN employees;
LOOP
effective_date := '';
created_by := '';
created_on := '';
comments := '';
employee_id := '';
FETCH employees into employee_id;
EXIT WHEN employees%NOTFOUND;
update_sql := 'UPDATE BI_EMPLOYEE SET ';
FOR e_update in e_updates
LOOP
select comments, effective_date , changed_by, changed_on into comments, effective_date , created_by, created_on
from bi_employee_update where EMPLOYEE_UPDATE_ID = e_update.EMPLOYEE_UPDATE_ID;
update_sql := update_sql || e_update.column_name || ' = ''' || e_update.new_value || ''' , ' ;
UPDATE BI_EMPLOYEE_UPDATE
SET
EXECUTED = 'Y'
WHERE
EMPLOYEE_UPDATE_ID = e_update.EMPLOYEE_UPDATE_ID ;
END LOOP;
update_sql := update_sql || ' comments = ''' || comments || ''', updated_by = ''' || created_by || ''', updated_on = ''' || created_on || ''', effective_date = ''' || effective_date || '''';
update_sql := update_sql || ' WHERE emp_id = ' || employee_id ;
dbms_output.put_line('KKKK '||update_sql);
execute immediate update_sql ;
END LOOP;
CLOSE employees;
END;
The problem is in the second cursor where I get the data of all the previous cursors combined.
e.g. if first iteration shoud return a, second should return b. But in actual first iteration returns a, b and second also returns a,b.
Below is the dynamic query generated which is exactly same.
1st iteration
EXPECTED (CORRECT):
UPDATE BI_EMPLOYEE SET EMPLOYEE_ID = '1111111111111' , PP_NUMBER = '22222222222' ,
CORPORATE_TITLE_ID = '2' , comments = 'c11', updated_by = '361',
updated_on = '12-SEP-12', effective_date = '25-SEP-12' WHERE emp_id = 18010
ACTUAL (WRONG):
UPDATE BI_EMPLOYEE SET EMPLOYEE_ID = '1111111111111' , PP_NUMBER = '22222222222' ,
CORPORATE_TITLE_ID = '2' , LASTNAME = 'Ll22 edited ' , OFFSHORE_ONSHORE = '1' ,
ONSHORE_REGION = '1' , ONSHORE_DESK_MANAGER = 'henrry ' ,
comments = 'cc 33 33', updated_by = '361', updated_on = '12-SEP-12',
effective_date = '25-SEP-12' WHERE emp_id = 18010
2nd iteration
EXPECTED (CORRECT):
UPDATE BI_EMPLOYEE SET LASTNAME = 'Ll22 edited ' , OFFSHORE_ONSHORE = '1' ,
ONSHORE_REGION = '1' , ONSHORE_DESK_MANAGER = 'henrry ' ,
comments = 'cc 33 33', updated_by = '361', updated_on = '12-SEP-12',
effective_date = '25-SEP-12' WHERE emp_id = 18009
ACTUAL (WRONG):
UPDATE BI_EMPLOYEE SET EMPLOYEE_ID = '1111111111111' , PP_NUMBER = '22222222222' ,
CORPORATE_TITLE_ID = '2' , LASTNAME = 'Ll22 edited ' ,
OFFSHORE_ONSHORE = '1' , ONSHORE_REGION = '1' ,
ONSHORE_DESK_MANAGER = 'henrry ' , comments = 'cc 33 33',
updated_by = '361', updated_on = '12-SEP-12',
effective_date = '25-SEP-12'
WHERE emp_id = 18009
Why is this happening?
As mentioned in a comment on your previous question, your second cursor is not restricted to the employee found by the first cursor because you have no link between them. Where you have:
and employee_id = employee_id
... both of those refer to the table column so it doesn't act as a filter at all. You've given your local variable the same name, which confuses things enough, but it's out of scope anyway - this cursor has no visibility of the variable value set in the main body of the procedure.
You need to do something like:
CREATE OR REPLACE PROCEDURE sp_run_employee_updates (p_date IN DATE) IS
update_sql varchar2(4000);
first_update boolean;
CURSOR c_employees IS
SELECT DISTINCT employee_id
FROM bi_employee_update
WHERE effective_date = p_date
AND executed = 'N'
AND activity_id = '0';
CURSOR c_updates(cp_employee_id bi_employee_update.employee_id%TYPE) IS
SELECT *
FROM bi_employee_update
WHERE effective_date = p_date
AND executed = 'N'
AND activity_id = '0'
AND employee_id = cp_employee_id
FOR UPDATE;
BEGIN
-- loop around all employees with pending records
FOR r_employee IN c_employees LOOP
-- reset the update_sql variable to its base
update_sql := 'UPDATE BI_EMPLOYEE SET ';
-- reset the flag so we only add the comments etc. on the first record
first_update := true;
-- loop around all pending records for this employee
FOR r_update IN c_updates(r_employee.employee_id) LOOP
-- add the comments etc., only for the first update we see
if first_update then
update_sql := update_sql
|| ' comments = ''' || r_update.comments || ''','
|| ' updated_by = ''' || r_update.changed_by || ''','
|| ' updated_on = ''' || r_update.changed_on || ''','
|| ' effective_date = ''' || r_update.effective_date || '''';
first_update := false;
end if;
-- add the field/value from this record to the variable
update_sql := update_sql || ', '
|| r_update.column_name || ' = ''' || r_update.new_value || '''' ;
-- mark this update as executed
UPDATE bi_employee_update
SET executed = 'Y'
WHERE CURRENT OF c_updates;
END LOOP;
-- apply this update to the bi_employee record
update_sql := update_sql || ' WHERE emp_id = ' || r_employee.employee_id;
DBMS_OUTPUT.PUT_LINE(update_sql);
EXECUTE IMMEDIATE update_sql;
END LOOP;
END sp_run_employee_updates;
The important difference, really, is that the second cursor now has a parameter, and the employee ID from the first cursor is passed as that parameter.
Also, IN_DATE is declared as a date, so you don't need to pass it through TO_DATE(). There are going to be implicit date conversions in other places (effective dates etc.) because you're treating them as strings, but as long as they don't have time components this probably won't break anything as it should be consistent within the procedure.

Resources