I'm trying to send with webservice POST method a dozens of records to other side but problem is that other side get only one raw of data.
I put cursor and in begin section open cursor and make loop.
I think the problem is somewhere in part UTL_HTTP.read_raw. I have to add something additional or modify function.
Where is the catch, where I'm wrong?
create or replace FUNCTION "TST" return clob is
l_http_request UTL_HTTP.req;
l_http_response UTL_HTTP.resp;
l_buffer_size NUMBER(10) := 20000;
l_line_size NUMBER(10) := 50;
l_lines_count NUMBER(10) := 20;
l_string_request VARCHAR2(32767);
l_line VARCHAR2(128);
l_substring_msg VARCHAR2(20000);
l_raw_data RAW(20000);
l_clob_response CLOB ;
v_clob clob;
url varchar2(200) := 'http://tst';
cursor c_get_prihod is
select IDORIG , IDSUSTAVA , DATUM , SIFAGENCIJE , SIFDRZAVE ,
SIFOSUSL , IZNOSNETO , IZNOSBRUTO , IZNOSPOREZA , SIFHOTELA
from HRS_LASERLINE_PRIHOD
where datum = pms_p.business_date
and sifhotela = pms_p.resort
;
begin
for a in c_get_prihod loop
l_string_request:=
'{
IdOrig:"'||a.IDORIG||'",
IdSustava:"'||a.IDSUSTAVA||'",
Datum:"'||a.DATUM||'",
SifAgencije:"'||a.SIFAGENCIJE||'",
SifDrzave:"'||a.SIFDRZAVE||'",
SifOsUsl:"'||a.SIFOSUSL||'",
IznosNeto:"'||a.IZNOSNETO||'",
IznosBruto:"'||a.IZNOSBRUTO||'",
IznosPoreza:"'||a.IZNOSPOREZA||'",
SifHotela:"'||a.SIFHOTELA||'",
}}'
;
end loop;
l_http_request := UTL_HTTP.begin_request(url , method => 'POST', http_version => 'HTTP/1.1');
utl_http.set_header(l_http_request, 'user-agent', 'mozilla/4.0');
utl_http.set_header(l_http_request, 'content-type', 'application/json');
utl_http.set_header(l_http_request, 'Content-Length', length(l_string_request));
BEGIN
<<request_loop>>
FOR i IN 0..CEIL(LENGTH(l_string_request) / l_buffer_size) - 1 LOOP
l_substring_msg := SUBSTR(l_string_request, i * l_buffer_size + 1, l_buffer_size);
BEGIN
l_raw_data := utl_raw.cast_to_raw(l_substring_msg);
UTL_HTTP.write_raw(r => l_http_request, data => l_raw_data);
EXCEPTION
WHEN NO_DATA_FOUND THEN
EXIT request_loop;
END;
END LOOP request_loop;
l_http_response := UTL_HTTP.get_response(l_http_request);
BEGIN
LOOP
UTL_HTTP.read_raw(l_http_response, l_raw_data, l_buffer_size);
l_clob_response := l_clob_response || UTL_RAW.cast_to_varchar2(l_raw_data);
END LOOP response_loop;
EXCEPTION
WHEN UTL_HTTP.end_of_body THEN
UTL_HTTP.end_response(l_http_response);
END;
<<print_response>>
FOR i IN 0..CEIL(LENGTH(l_clob_response) / l_line_size) - 1
LOOP
l_line := SUBSTR(l_clob_response, i * l_line_size + 1, l_line_size);
EXIT WHEN i > l_lines_count - 1;
END LOOP print_response;
v_clob:= l_clob_response;
return v_clob ;
exception
when utl_http.end_of_body then
utl_http.end_response(l_http_response);
end;
end TST;
Your cursor query might find dozens of rows, but you're doing this:
for a in c_get_prihod loop
l_string_request:= ... ;
end loop;
Each time around that loop you're replacing the entire contents of l_string_request, so however many rows you get and how many times you go around the loop, you'll always end up with that containing only the last row returned by that query. So you only send that single, final, record to the web service.
Assuming the receiving service is expecting an array of records, you might want something like:
begin
l_string_request := '[ ';
for a in c_get_prihod loop
if a.FLAG > 1 then
l_string_request := l_string_request || ', ';
end if;
l_string_request := l_string_request || '{ ';
l_string_request := l_string_request || '"IdOrig": "' || a.IDORIG || '", ';
l_string_request := l_string_request || '"IdSustava": "' || a.IDSUSTAVA ||' ", ';
l_string_request := l_string_request || '"Datum": "' || a.DATUM || '", ';
l_string_request := l_string_request || '"SifAgencije": "' || a.SIFAGENCIJE || '", ';
l_string_request := l_string_request || '"SifDrzave": "' || a.SIFDRZAVE || '", ';
l_string_request := l_string_request || '"SifOsUsl": "' || a.SIFOSUSL || '", ';
l_string_request := l_string_request || '"IznosNeto": "' || a.IZNOSNETO || '", ';
l_string_request := l_string_request || '"IznosBruto": "' || a.IZNOSBRUTO || '", ';
l_string_request := l_string_request || '"IznosPoreza": "' || a.IZNOSPOREZA || '", ';
l_string_request := l_string_request || '"SifHotela": "' || a.SIFHOTELA || '"';
l_string_request := l_string_request || ' }';
end loop;
l_string_request := l_string_request || ' ]';
dbms_output.put_line(l_string_request);
...
where flag is an extra column added to the cursor result as e.g. , ROWNUM AS FLAG - just as a mechanism to know if you are on the first row or a subsequent row (and so need as comma between array elements).
The dbms_output lets you see the entire generated value, which you can manually inspect and/or pass through a JSON validator to check it is what you (and more importantly the receiving service) expect to see. You can still add newlines during the JSON construction for readability, though the service won't care about those.
Depending on how many rows you could have you might need l_string_request to be a CLOB instead of a varchar2. I'd also suggest you explicitly format date/timestamp values, and handle numbers properly if you have any of those.
After modification according to your advice I'm getting this error:
ORA-06502: PL/SQL: numeric or value error ORA-06512: at "OPERA_MARRIOTT.HRS_LASERLINE_BI_PRIHOD", line 87
You would get this if l_clob_response is empty, so perhaps your read loop isn't getting what you expect now. (Which may well be because the request is now invalid JSON, or doesn't match what the service expects). After the UTL_HTTP.get_response() call, see what l_http_response.status_code and l_http_response.reason_phrase show. The status codes are in the documentation.
Related
I'm trying to write a PL/SQL script that searches the entire database for a string and report the tables and columns it finds it in. It looks like this:
DECLARE
ncount NUMBER;
vwhere VARCHAR2(1000) := '';
vsearchstr VARCHAR2(1000) := 'search string here';
vresult VARCHAR2(10000) := 'result: ';
vtab VARCHAR2(1000) := '';
vcol VARCHAR2(1000) := '';
BEGIN
FOR k IN (SELECT a.table_name, a.column_name FROM all_tab_columns a WHERE a.data_type LIKE '%VARCHAR%')
LOOP
vtab := k.table_name;
vcol := k.column_name;
vwhere := ' where ' || vcolumnname || ' = :vsearchstr ';
EXECUTE IMMEDIATE 'select count(1) from ' || vtab || vwhere INTO ncount USING vsearctstr;
IF (ncount > 0)
THEN
vresult := CONCAT(vresult, vcol || ' ' || vtab || ', ');
END IF;
END LOOP;
IF (LENGTH(vresult) > 1)
THEN
dbms_output.put_line(vresult);
ELSE
dbms_output.put_line('not found');
END IF;
END;
When I run it, I get the following error:
It is essentially saying it doesn't recognize the table vtab in the line EXECUTE IMMEDIATE 'select count(1) from ' || vtab || vwhere INTO ncount USING vsearctstr.
So in order to see which table it's complaining about, I added the following exception block to the end of the script:
EXCEPTION
WHEN OTHERS
THEN
BEGIN
dbms_output.put_line('exception: ' || vtab);
END;
It tells me the table name is IND$.
I'm not sure what this table (or view) is and it doesn't look relevant anyway.
So my question is two fold: 1) If it is fetching IND$ in the FOR loop from k.table_name (which in turn is from all_tab_columns), why does it say it doesn't exist in the select query? 2) I'm not sure what IND$ is but I'm pretty sure I don't need to search it; So is there a way to limit my search to only relevant tables (not views)? By 'relevant', I mean tables that we created to store data for our application (as opposed to system tables or user tables, etc.).
Thanks very much.
all_tab_columns lists columns in all tables that your current schema has access to. In general, if you're going to use ALL_... views to build dynamic SQL, you should incorporate the OWNER column as well since it may return rows for tables in other schemas.
If you really only want to consider tables in your current schema, use user_tab_columns instead.
If you want to get more, or less, particular than that about which tables are 'relevant', then you'll probably need to hardcode specific rules into your query.
FYI: IND$ is part of the Oracle data dictionary and is in the SYS schema. (Although, it's always possible someone has created a table with that name in some other schema.) It's unusual that your application schema would have direct access to this.
If you know how to skip tables you don't want, do it - you might filter by OWNER, maybe table name, etc.
If you don't want to bother, include inner begin-exception-end block into the loop so that it skips errors (either silently, or display errors, or store them into some table). Here's one option:
DECLARE
ncount NUMBER;
vwhere VARCHAR2(1000) := '';
vsearchstr VARCHAR2(1000) := 'search string here';
vresult VARCHAR2(10000) := 'result: ';
vtab VARCHAR2(1000) := '';
vcol VARCHAR2(1000) := '';
BEGIN
FOR k IN (SELECT a.table_name, a.column_name FROM all_tab_columns a WHERE a.data_type LIKE '%VARCHAR%')
LOOP
BEGIN
vtab := k.table_name;
vcol := k.column_name;
vwhere := ' where ' || vcolumnname || ' = :vsearchstr ';
EXECUTE IMMEDIATE 'select count(1) from ' || vtab || vwhere INTO ncount USING vsearctstr;
IF (ncount > 0)
THEN
vresult := CONCAT(vresult, vcol || ' ' || vtab || ', ');
END IF;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(vtab ||': '|| sqlerrm);
END;
END LOOP;
IF (LENGTH(vresult) > 1)
THEN
dbms_output.put_line(vresult);
ELSE
dbms_output.put_line('not found');
END IF;
END;
You need to:
Also get the owner from all_tab_columns
Use quoted identifiers around the owner, table_name and column_name so that the query does not fail for identifiers in different cases or with special characters.
Handle errors (i.e. when you do not have the permissions to SELECT from a table).
To make it faster, you can:
Filter the columns to only those that are long enough to contain the search string.
You can do that using:
DECLARE
ncount NUMBER;
vsearchstr VARCHAR2(1000) := 'search string here';
vresult VARCHAR2(10000);
BEGIN
FOR k IN (SELECT owner,
table_name,
column_name
FROM all_tab_columns
WHERE data_type LIKE '%VARCHAR%'
AND data_length >= LENGTH(vsearchstr))
LOOP
BEGIN
EXECUTE IMMEDIATE
'select count(1)
from "' || k.owner || '"."' || k.table_name || '"
where "' || k.column_name || '" = :1'
INTO ncount
USING vsearchstr;
IF (ncount > 0)
THEN
vresult := vresult || ','
|| k.owner || '.' || k.table_name || '.' || k.column_name;
END IF;
EXCEPTION
WHEN OTHERS THEN
NULL;
END;
END LOOP;
IF (LENGTH(vresult) > 1)
THEN
dbms_output.put_line(vresult);
ELSE
dbms_output.put_line('not found');
END IF;
END;
/
db<>fiddle here
Here is the final query (which works):
DECLARE
ncount NUMBER;
vwhere VARCHAR2(1000) := '';
vsearchstr VARCHAR2(1000) := '%your string here%';
vresult VARCHAR2(10000) := '';
vtab VARCHAR2(1000) := '';
vcol VARCHAR2(1000) := '';
BEGIN
FOR k IN (SELECT table_name, column_name
FROM all_tab_columns
WHERE data_type LIKE '%VARCHAR%'
AND data_length >= LENGTH(vsearchstr)
AND table_name NOT IN (SELECT view_name FROM all_views))
LOOP
BEGIN
vtab := k.table_name;
vcol := k.column_name;
vwhere := ' where ' || vcol || ' like :vsearchstr ';
EXECUTE IMMEDIATE 'select count(1) from OWNER.' || vtab || vwhere INTO ncount USING vsearchstr;
IF (ncount > 0)
THEN
vresult := CONCAT(vresult, 'table: ' || vtab || ', column: ' || vcol || chr(13) || chr(10));
END IF;
EXCEPTION
WHEN OTHERS THEN NULL;
END;
END LOOP;
IF (LENGTH(vresult) > 0)
THEN
dbms_output.put_line(vresult);
ELSE
dbms_output.put_line('string not found');
END IF;
END;
I took Littlefoot's advice and added an exception block to catch the exception when the table is not found and just continue with the loop. I also took MT0's advice and checked the length of the column in the select query. I also included the owner in the select query.
This works. Thanks to everyone who gave their advice.
How do i pass a {Base64 encoded content} value in a json object. the file that i want to send is a blob type in my Oracle Table.
FOLLOWING snippets of my code
Cursor csr_con_attach is
select file_name documentName,file_blob ,
to_char(created_date,'DD-MON-YYYY')||'T'||to_char(created_date,'HH24:MI:SS') documentDate
From contact.con_attachments
WHERE con_header_id_fk = 2770;
e.g.
For rec_con_attach in csr_con_attach
Loop
v_obj_build :=
json_object_t
('{'
|| '"documentName"' || ':"' || rec_con_attach.documentName || '",'
|| '"blobData"' || ':"' || rec_con_attach.file-blob ||'",'
|| '"documentNotes"' || ':"' || 'Test' ||'",'
|| '"documentDate"' || ':"' || rec_con_attach.documentDate
||'"'||
'}');
end loop;
When i compile this I get an error "wrong number or type of arguments in call to '||" . The rest API that i am calling expects a {Base64 encoded content} for blobData.
I also tried converting blob file to base64encode(using procedure below), but then i get JSONT syntax error ORA-06512 sys.JDOM_T
Declare
l_step pls_integer := 22500; -- make sure you set a multiple of 3 not higher than 24573
l_clob clob;
begin
for i in 0 .. trunc((dbms_lob.getlength(i_blob) - 1 )/l_step)
loop
dbms_output.put_line(i);
l_clob := utl_raw.cast_to_varchar2(utl_encode.base64_encode(dbms_lob.substr(i_blob, l_step, i * l_step + 1)));
end loop;
io_clob := l_clob;
end;
Any help would be appreciated
Thanks
I'm still a relatively newbe when it comes to PL/SQL.
Using Oracle 12c on Linux RHEL 6.8, the following shell script will attempt to activate all RI constraints in a collection of tables, and if they fail with parent key failures, it will dump the first 100 rows (or less) of the offending data. Or at least that is the goal. Since the script deals mostly with system tables on 12c (with only a small user table list that is unique to my installation), I'm including the whole thing exactly from my environment.
The main work occurs in the exception handling where the system tables are queried for the constraint, and user queries are formed from those data.
As a extra goal, the output is rather messy and I want to clean it up, but first it has to work :)
The output / error I get for my tables is the following:
ERROR Handling here for table NRNG_MTC_VST Constraint Name:
SYS_C0011790 Final SQL = SELECT DISTINCT NRNG_MTC_VST.LOG_CRT_DT ,
NRNG_MTC_VST.NRRNG_MTC_LG_ID FROM ODB_PRIMARY.NRNG_MTC_VST WHERE NOT
EXISTS (SELECT 1 FROM ODB_PRIMARY.NRNG_MTC_LOG WHERE
NRNG_MTC_VST.LOG_CRT_DT = NRNG_MTC_LOG.LOG_CRT_DT AND
NRNG_MTC_VST.NRRNG_MTC_LG_ID = NRNG_MTC_LOG.NRRNG_MTC_LG_ID) FETCH
FIRST 100 rows only
---xxx End SQL DECLARE
* ERROR at line 1: ORA-01001: invalid cursor ORA-06512: at line 111 ORA-02298: cannot validate (ODB_PRIMARY.SYS_C0011790) - parent keys
not found
The output SQL from my print_line is correct, and would work if pasted directly into a SQLDeveloper session. There is just something silly about how the cursor is defined I don't understand.
The full text of the script. BYW, if you see other bonehead changes that should be made unrelated to the error, please suggest them as well.
cd $OGGHOME/scripts
export ORACLE_SID=odbod07 $ORACLE_HOME/bin/sqlplus <<-EOF / as sysdba
alter session set container=p01_odbod07;
set echo on set feedback on
set heading off
set serveroutput on size 10000
DECLARE finalsql varchar2(2048);
part1sql varchar2(1024) ;
part2sql varchar2(1024) := ' ';
cownername varchar2(1024);
ctablename varchar2(1024);
pownername varchar2(1024);
ptablename varchar2(1024);
cnt number := 0;
-- Weak cursor defs
my_cursor sys_refcursor;
BEGIN FOR i in (
select owner, table_name, constraint_name
from dba_constraints
where constraint_type = 'R'
and status = 'DISABLED'
and owner = 'ODB_PRIMARY'
and TABLE_NAME in
-- enter user tables with RI constraints here
('RRNG_MTC_STN_CPLY',
'NRNG_MTC_VST_MTRL_USG',
'NRNG_MTC_VST',
'CAR_CORE',
'NRNG_MTC_LOG'))
-- end user table definitions, rest of code should rely only on system tables
LOOP BEGIN
dbms_output.put_line('alter table '||i.owner|| '.' ||
i.table_name || ' enable constraint '||i.constraint_name);
execute immediate 'alter table '||i.owner|| '.' ||
i.table_name || ' enable constraint '||i.constraint_name;
EXCEPTION
-- exception handling - dump offending data
WHEN OTHERS THEN -- take all exceptions for now
dbms_output.put_line ('ERROR Handling here for table ' ||
i.table_name || ' Constraint Name: ' ||i.constraint_name);
finalsql := 'SELECT DISTINCT ';
part1sql := '';
part2sql := ' ';
cnt := 0;
for constraint in (
SELECT ucc1.OWNER as childowner,
ucc1.TABLE_NAME as childtable,
ucc1.column_name as childcolumn,
ucc2.OWNER as parentowner,
ucc2.TABLE_NAME as parenttable,
ucc2.column_name as parentcolumn,
utc1.data_type as childdatatype,
utc1.data_length as childdatalen
FROM all_constraints uc ,
all_cons_columns ucc1 ,
all_cons_columns ucc2,
all_tab_columns utc1
WHERE
uc.constraint_name = ucc1.constraint_name
AND uc.r_constraint_name = ucc2.constraint_name
AND ucc1.POSITION = ucc2.POSITION
AND ucc1.table_name = utc1.table_name
AND ucc1.column_name = utc1.column_name
AND uc.constraint_type = 'R'
AND uc.constraint_name = i.constraint_name
ORDER BY ucc1.TABLE_NAME , uc.constraint_name)
loop
cownername := constraint.childowner;
ctablename := constraint.childtable;
pownername := constraint.parentowner;
ptablename := constraint.parenttable;
if cnt > 0 then
part1sql := part1sql || ' , ';
part2sql := part2sql || ' AND ';
end if;
part1sql := part1sql || constraint.childtable ||
'.'||constraint.childcolumn || ' ';
part2sql := part2sql || constraint.childtable || '.'
|| constraint.childcolumn || ' = '
|| constraint.parenttable || '.' ||
constraint.parentcolumn;
cnt := cnt + 1;
end loop;
finalsql := finalsql || part1sql ||
' FROM ' || ' ' || cownername ||
'.' || ctablename ||
' WHERE NOT EXISTS (SELECT 1 FROM ' ||
pownername || '.' || ptablename ||
' WHERE ' || part2sql || ') FETCH FIRST 100 rows only';
dbms_output.put_line ('Final SQL = ' || finalsql);
dbms_output.put_line ('---xxx End SQL');
open my_cursor for finalsql;
dbms_sql.return_result(my_cursor);
close my_cursor;
-- EXECUTE IMMEDIATE finalsql;
END;
end loop; end;
/
EOF
Many thanks for any help provided.
Brian
Just to narrow this down to a simple test case, I think this is the error you are seeing:
declare
my_cursor sys_refcursor;
begin
open my_cursor for 'select ''Hello, world'' as message from dual';
dbms_sql.return_result(my_cursor);
close my_cursor; -- << Remove this line
end;
/
ERROR at line 1:
ORA-01001: invalid cursor
ORA-06512: at line 6
This is because you attempted to close the cursor when you have already passed it to dbms_sql for processing. Remove the line with close my_cursor.
declare
my_cursor sys_refcursor;
begin
open my_cursor for 'select ''Hello, world'' as message from dual';
dbms_sql.return_result(my_cursor);
end;
/
PL/SQL procedure successfully completed.
ResultSet #1
MESSAGE
------------
Hello, world
1 row selected.
I had same kind of issue when i tried to print Ref_cursor directly. Then i created a Record type variable and then fetched field values in that variable and then i used DBMS_OUTPUT for that record type variable.
Please see if below code and scenario can help you-
set serveroutput on;
declare
v_sql varchar2(1000);
v_cursor sys_refcursor;
type myrec is record(col1 varchar2(100),col2 varchar2(1000));
rec myrec;
begin
v_sql:='select name,status from t_employee where user_id in (''C001117'',''C001122'')';
open v_cursor for v_sql;
loop
fetch v_cursor
into rec;
exit when v_cursor%notfound;
dbms_output.put_line( rec.col1||':status '||rec.col2 );
end loop;
end;
/
The following is my semi-complete script. Given a table list, it will attempt to activate the RI Constraints, and if they fail it will print out the FK data records in the child table that prevent it from being applied.
The hardest part of this project was the fact that the FKs can be any number of columns and of any type, so the print the results of the select in this case was very tricky (IMO).
Thanks for the help people provided.
cd $OGGHOME/scripts
. ./functions.sh
$ORACLE_HOME/bin/sqlplus ${ORACLE_USERID}/${ORACLE_PASSWORD}#${ORACLE_SID} << EOF
set echo on
set feedback on
set heading off
set serveroutput on size unlimit
DECLARE
finalsql varchar2(2048);
part1sql varchar2(1024) ;
part2sql varchar2(1024) := ' ';
cownername varchar2(1024);
ctablename varchar2(1024);
pownername varchar2(1024);
ptablename varchar2(1024);
cnt number := 0;
desc_tab dbms_sql.desc_tab;
col_count INTEGER;
cursor_name INTEGER;
-- Weak cursor defs
my_cursor sys_refcursor;
col1 varchar2(50);
d number;
j number;
lineout varchar2(2048);
plineout varchar2(2048);
rows number;
eCount number := 0;
BEGIN
FOR i in (
select owner, table_name, constraint_name
from dba_constraints
where constraint_type = 'R'
and status = 'DISABLED'
and owner = '$DBSCHEMA'
and TABLE_NAME in (
'RRNG_MTC_STN_CPLY',
'NRNG_MTC_VST_MTRL_USG',
'NRNG_MTC_VST',
'MTC_TSK_HRHY'))
LOOP
BEGIN
dbms_output.put_line ('.');
dbms_output.put_line ('=====================================');
dbms_output.put('alter table '||i.owner|| '.' || i.table_name || ' enable constraint '||i.constraint_name);
execute immediate 'alter table '||i.owner|| '.' || i.table_name || ' enable constraint '||i.constraint_name;
dbms_output.put_line (' ... SUCCESS');
EXCEPTION -- exception handling - dump offending data
WHEN OTHERS THEN
eCount := eCount + 1;
dbms_output.put_line (' ... FAILED. Constraint Name: ' || i.constraint_name);
finalsql := 'SELECT DISTINCT ';
part1sql := '';
part2sql := ' ';
cnt := 0;
for constraint in (
SELECT ucc1.OWNER as childowner,
ucc1.TABLE_NAME as childtable,
ucc1.column_name as childcolumn,
ucc2.OWNER as parentowner,
ucc2.TABLE_NAME as parenttable,
ucc2.column_name as parentcolumn,
utc1.data_type as childdatatype,
utc1.data_length as childdatalen
FROM all_constraints uc ,
all_cons_columns ucc1 ,
all_cons_columns ucc2,
all_tab_columns utc1
WHERE
uc.constraint_name = ucc1.constraint_name
AND uc.r_constraint_name = ucc2.constraint_name
AND ucc1.POSITION = ucc2.POSITION
AND ucc1.table_name = utc1.table_name
AND ucc1.column_name = utc1.column_name
AND uc.constraint_type = 'R'
AND uc.constraint_name = i.constraint_name
ORDER BY ucc1.TABLE_NAME ,
uc.constraint_name)
loop
cownername := constraint.childowner;
ctablename := constraint.childtable;
pownername := constraint.parentowner;
ptablename := constraint.parenttable;
if cnt > 0 then
part1sql := part1sql || ' , ';
part2sql := part2sql || ' AND ';
end if;
part1sql := part1sql || constraint.childtable || '.' || constraint.childcolumn || ' ';
part2sql := part2sql || constraint.childtable || '.' || constraint.childcolumn || ' = '
|| constraint.parenttable || '.' || constraint.parentcolumn;
cnt := cnt + 1;
end loop;
finalsql := finalsql || part1sql || ' FROM ' || ' ' || cownername || '.' || ctablename || ' WHERE NOT EXISTS (SELECT 1 FROM ' ||
pownername || '.' || ptablename || ' WHERE ' || part2sql || ') FETCH FIRST 100 rows only';
dbms_output.put_line ('Final SQL = (' || finalsql || ')');
-- dbms_output.put_line ('---xxx End SQL');
lineout := 'Child Table: ' || ctablename || '(';
plineout := 'Parent Table: ' || ptablename;
cursor_name := dbms_sql.open_cursor;
dbms_sql.PARSE (cursor_name, finalsql, DBMS_SQL.NATIVE);
d := dbms_sql.execute (cursor_name);
dbms_sql.describe_columns (cursor_name, col_count, desc_tab);
for j in 1..col_count
LOOP
DBMS_SQL.DEFINE_COLUMN (cursor_name, j, col1, 30);
lineout := lineout || desc_tab(j).col_name || ' , ';
-- plineout := plineout || constraint.parentcolumn || ' ';
-- dbms_output.put_line ('Column 1: ' || j || ' is ' || desc_tab(j).col_name || ' type '
-- || desc_tab(j).col_type);
END LOOP j;
lineout := lineout || ')';
-- plineout := plineout || ')';
dbms_output.put_line (lineout);
dbms_output.put_line (plineout);
lineout := NULL;
for j in 1..col_count
LOOP
if j > 1 then
lineout := lineout || ' ';
end if;
lineout := lineout || desc_tab(j).col_name;
END LOOP;
dbms_output.put_line (lineout);
dbms_output.put_line ('----------------------------------------');
LOOP
rows := dbms_sql.fetch_rows (cursor_name);
EXIT WHEN rows = 0;
lineout := NULL;
for j in 1..col_count
LOOP
dbms_sql.column_value (cursor_name, j, col1);
if j > 1 then
lineout := ltrim(lineout || ' ' || col1);
else
lineout := col1;
END IF;
END LOOP;
dbms_output.put_line (lineout);
END LOOP;
dbms_sql.close_cursor (cursor_name);
END;
end loop;
end;
/
EOF
your FETCH FIRST 100 rows only would seem to be out of place.
This is part of the BULK COLLECT clause in a SELECT statement in PL/SQL; as far as I know, it is not part of a SQL statement you can pass into a cursor like this
This is resulting in the cursor statement being invalid
I have a stored procedure within Oracle that dynamically creates triggers for all of my tables (since they trigger the same stored procedure), by constructing SQL code and executing it with Oracle's PL/SQL EXECUTE command (although I am using SQL Commander for testing). How does one correctly create an if statement within a trigger? Below is my code, which gets syntax error on the line with there IF statement starts.
CREATE OR REPLACE TRIGGER "DATABASE"."TABLE_TRIGGER"
AFTER INSERT OR DELETE OR UPDATE ON TABLE
FOR EACH ROW
DECLARE
v_op VARCHAR2(20);
BEGIN
IF INSERTING THEN
v_op := 'INSERT';
ELSIF UPDATING THEN
v_op := 'UPDATE';
ELSE
v_op := 'DELETE';
END IF;
PKG.STORED_PROC(v_op, ' - MY_COLUMN: ' || :NEW.MY_COLUMN || '', 'MY_TABLE');
END;
This is a sample of the code I used to generate the SQL code.
PROCEDURE CREATE_TRIGGERS(
IN_TABLE_OMIT IN VARCHAR2, -- IN_TABLE_OMIT is a list of tables to not create a trigger on, as CSV
OUT_RESULT OUT VARCHAR2)
AS
v_table_results SYS_REFCURSOR;
v_column_results SYS_REFCURSOR;
v_template_head VARCHAR2(512);
v_template_body VARCHAR2(512);
v_template_operation VARCHAR(512);
v_template_column VARCHAR2(128);
v_template_data VARCHAR2(1024);
v_template_foot VARCHAR2(512);
v_result VARCHAR2(1024);
BEGIN
-- Create a template for each trigger
v_template_head := 'CREATE OR REPLACE TRIGGER "MY_DB"."MY_TABLE_TRIGGER"' || chr(10);
v_template_head := v_template_head || 'AFTER INSERT OR DELETE OR UPDATE ON MY_TABLE' || chr(10);
v_template_head := v_template_head || 'FOR EACH ROW' || chr(10) || 'DECLARE' || chr(10) || 'v_op VARCHAR2(20);' || chr(10) || 'BEGIN' || chr(10) || chr(10);
v_template_operation := 'IF INSERTING THEN v_op := ''INSERT''; ELSIF UPDATING THEN v_op := ''UPDATE''; ELSE v_op := ''DELETE''; END IF;' || chr(10);
v_template_body := 'MY_DB.STORED_PROC(' || chr(10) || 'v_op,' || chr(10);
v_template_column := ''' - MY_COLUMN: '' || :NEW.MY_COLUMN || ';
v_template_foot := '''MY_TABLE'');' || chr(10) || chr(10) || 'END;';
-- Insert double quotes into template
-- Loop through each table
FOR LOOP_TABLE IN (SELECT TABLE_NAME FROM USER_TABLES ORDER BY TABLE_NAME) LOOP
IF INSTR(IN_TABLE_OMIT, LOOP_TABLE.TABLE_NAME) != 0 OR INSTR('AUDIT_TABLE', LOOP_TABLE.TABLE_NAME) != 0 THEN
FOR LOOP_COLUMN IN (SELECT COLUMN_NAME FROM ALL_TAB_COLUMNS WHERE TABLE_NAME = LOOP_TABLE.TABLE_NAME) LOOP
v_template_data := v_template_data || REPLACE(v_template_column, 'MY_COLUMN', LOOP_COLUMN.COLUMN_NAME);
END LOOP;
v_template_head := REPLACE(v_template_head, 'MY_TABLE', LOOP_TABLE.TABLE_NAME);
v_template_data := v_template_data || ''''',' || chr(10);
v_template_foot := REPLACE(v_template_foot, 'MY_TABLE', LOOP_TABLE.TABLE_NAME);
v_result := v_template_head || v_template_operation || v_template_body || v_template_data || v_template_foot;
v_template_data := '';
OUT_RESULT := chr(10) || v_result;
ELSE
dbms_output.put_line('Searched for ' || LOOP_TABLE.TABLE_NAME || ' in ' || IN_TABLE_OMIT);
END IF;
END LOOP;
END;
The trigger was successfully created when removing all semi-colons from the generated trigger statement.
I have a problem, I am creating an CLOB variable with the contents of a query in oracle to email to users, the problem is that it does email as .csv but with no contents. I can not find the problems:
CREATE OR REPLACE PROCEDURE trackekr(cursor1 IN OUT SYS_REFCURSOR)
AS
v_connection UTL_SMTP.connection;
v_clob CLOB := EMPTY_CLOB();
v_len INTEGER;
v_index INTEGER;
c_mime_boundary CONSTANT VARCHAR2(256) := 'the boundary can be almost anything';
rec NUMBER(10, 0) := 0;
d_id NUMBER(10, 0) := 0;
customer VARCHAR2(20);
wife VARCHAR2(20);
date_rec DATE;
special_h VARCHAR2(20);
g_amount NUMBER(10, 0) := 0;
credit_amount NUMBER(10, 0) := 0;
a_number VARCHAR2(20);
a__name VARCHAR2(20);
BEGIN
OPEN cursor1 FOR
SELECT rec,
d_id,
customer,
wife,
date_rec,
special h,
g_amount
FROM (your query here);
WHILE cursor1%NOTFOUND
LOOP
FETCH cursor1
INTO rec,
d_id,
customer,
wife,
date_rec,
special_h,
g_amount,
credit_amount,
a_number,
a__name;
v_clob :=
v_clob
|| rec
|| ','
|| d_id
|| ','
|| customer
|| ','
|| wife
|| ','
|| date_rec
|| ','
|| special_h
|| ','
|| g_amount
|| ','
|| credit_amount
|| ','
|| a_number
|| ','
|| a__name
|| UTL_TCP.crlf;
END LOOP;
-- UTL
v_connection := UTL_SMTP.open_connection(mailhost, 25);
SMTP server name or ip address
UTL_SMTP.helo(v_connection, mail.exchange.mydomain.com);
UTL_SMTP.mail(v_connection, 'mylogin.Exchange.mydomain.com');
UTL_SMTP.rcpt(v_connection, 'mylogin.Exchange.mydomain.com');
UTL_SMTP.open_data(v_connection);
UTL_SMTP.write_data(v_connection, 'From: ' || 'mylogin.Exchange.mydomain.com' || UTL_TCP.crlf);
UTL_SMTP.write_data(v_connection, 'To: ' || 'mylogin.Exchange.mydomain.com' || UTL_TCP.crlf);
UTL_SMTP.write_data(v_connection, 'Subject: test subject' || UTL_TCP.crlf);
UTL_SMTP.write_data(v_connection, 'MIME-Version: 1.0' || UTL_TCP.crlf);
UTL_SMTP.write_data(
v_connection,
'Content-Type: multipart/mixed; boundary="' || c_mime_boundary || '"' || UTL_TCP.crlf
);
UTL_SMTP.write_data(v_connection, UTL_TCP.crlf);
UTL_SMTP.write_data(
v_connection,
'This is a multi-part message in MIME format.' || UTL_TCP.crlf
);
UTL_SMTP.write_data(v_connection, '--' || c_mime_boundary || UTL_TCP.crlf);
UTL_SMTP.write_data(v_connection, 'Content-Type: text/plain' || UTL_TCP.crlf);
-- Set up attachment header
UTL_SMTP.write_data(
v_connection,
'Content-Disposition: attachment; filename="' || 'FIRSTFILE.csv' || '"' || UTL_TCP.crlf
);
UTL_SMTP.write_data(v_connection, UTL_TCP.crlf);
-- Write attachment contents
v_len := DBMS_LOB.getlength(v_clob);
v_index := 1;
WHILE v_index <= v_len
LOOP
UTL_SMTP.write_data(v_connection, DBMS_LOB.SUBSTR(v_clob, 32000, v_index));
v_index := v_index + 32000;
END LOOP;
-- End attachment
UTL_SMTP.write_data(v_connection, UTL_TCP.crlf);
UTL_SMTP.write_data(v_connection, '--' || c_mime_boundary || '--' || UTL_TCP.crlf);
UTL_SMTP.close_data(v_connection);
UTL_SMTP.quit(v_connection);
END;
As I said, it emails a .csv file but empty.
Note this part in your code:
WHILE cursor1%NOTFOUND
Your loop will never be executed for non-empty dataset. Use this instead:
WHILE cursor1%FOUND
Or even better use implicit cursor:
FOR cursor1 in
(SELECT rec,
d_id,
customer,
wife,
date_rec,
special_h,
g_amount,
credit_amount,
a_number,
a__name
FROM (your query here))
LOOP
v_clob :=
v_clob
|| cursor1.rec
|| ','
|| cursor1.d_id
|| ','
|| cursor1.customer
|| ','
|| cursor1.wife
|| ','
|| cursor1.date_rec
|| ','
|| cursor1.special_h
|| ','
|| cursor1.g_amount
|| ','
|| cursor1.credit_amount
|| ','
|| cursor1.a_number
|| ','
|| cursor1.a__name
|| UTL_TCP.crlf;
END LOOP;
The example below, write the query to clob and then to blob to put in a file
You just need to use the first step, until the line 87
SELECT dbms_lob.getlength(l_clob) INTO len FROM dual;
DECLARE
l_output utl_file.file_type;
c1 INTEGER DEFAULT dbms_sql.open_cursor;
l_columnvalue VARCHAR2(4000);
l_status INTEGER;
l_colcnt NUMBER := 0;
l_separator VARCHAR2(30);
l_desctbl dbms_sql.desc_tab;
l_flag NUMBER;
l_dir VARCHAR2(50);
l_arq VARCHAR2(100);
l_clob CLOB;
l_query VARCHAR2(50);
vstart NUMBER := 1;
bytelen NUMBER := 32000;
len NUMBER;
my_vr RAW(32000);
x NUMBER;
dest_offset INTEGER;
src_offset INTEGER;
blob_csid NUMBER;
lang_context INTEGER;
blb BLOB;
warning INTEGER;
BEGIN
l_query := 'select * from safx07_v where rownum <=100';
SELECT 'safx07' || to_char(SYSDATE, 'DDMMYY_HH24MISS') || '.txt'
INTO l_arq
FROM dual;
l_dir := 'ORACLE_EXP';
l_output := utl_file.fopen(l_dir, l_arq, 'wb');
--l_output := utl_file.fopen('ORACLE_EXP', 'filename.txt', 'wb', 32760);
--l_separator := separator;
IF l_separator = '' OR l_separator IS NULL THEN
l_separator := chr(9);
END IF;
dbms_sql.parse(c1, l_query, dbms_sql.native);
dbms_sql.describe_columns(c1, l_colcnt, l_desctbl);
dbms_lob.createtemporary(l_clob, TRUE,DBMS_LOB.call);
/*
FOR cr IN (l_query) LOOP
dbms_lob.writeappend(l_clob, length(cr.txt), cr.txt);
END LOOP;*/
FOR i IN 1 .. l_colcnt LOOP
dbms_output.put_line(l_desctbl(i).col_name || l_separator);
dbms_output.put_line(length(l_desctbl(i).col_name || l_separator));
/* utl_file.put(l_output, l_desctbl(i).col_name || l_separator);
dbms_sql.define_column(c1, i, l_columnvalue, 4000);*/
dbms_lob.writeappend(l_clob,
length(l_desctbl(i).col_name || l_separator),
l_desctbl(i).col_name || l_separator);
dbms_sql.define_column(c1, i, l_columnvalue, 4000);
END LOOP;
--utl_file.new_line(l_output);
l_status := dbms_sql.execute(c1);
dbms_lob.writeappend(l_clob, length(chr(10)), chr(10));
WHILE (dbms_sql.fetch_rows(c1) > 0) LOOP
FOR i IN 1 .. l_colcnt LOOP
/* dbms_sql.column_value(c1, i, l_columnvalue);
utl_file.put(l_output, l_columnvalue || l_separator);*/
dbms_sql.column_value(c1, i, l_columnvalue);
dbms_lob.writeappend(l_clob, length(l_columnvalue || l_separator),
l_columnvalue || l_separator);
END LOOP;
dbms_lob.writeappend(l_clob, length(chr(10)), chr(10));
END LOOP;
--utl_file.fclose(l_output);
SELECT dbms_lob.getlength(l_clob) INTO len FROM dual;
dbms_lob.createtemporary(blb, FALSE);
dest_offset := 1;
src_offset := 1;
lang_context := 0;
-- convert to a BLOB here:
dbms_lob.converttoblob(blb, l_clob, dbms_lob.getlength(l_clob),
dest_offset, src_offset, 0, lang_context, warning);
-- if small enough for a single write
IF len < 32760 THEN
utl_file.put_raw(l_output, blb);
utl_file.fflush(l_output);
ELSE
-- write in pieces
vstart := 1;
WHILE vstart < len AND bytelen > 0 LOOP
dbms_lob.read(blb, bytelen, vstart, my_vr);
utl_file.put_raw(l_output, my_vr);
utl_file.fflush(l_output);
-- set the start position for the next cut
vstart := vstart + bytelen;
-- set the end position if less than 32000 bytes
x := x - bytelen;
IF x < 32000 THEN
bytelen := x;
END IF;
END LOOP;
END IF;
dbms_sql.close_cursor(c1);
utl_file.fclose(l_output);
EXCEPTION
WHEN OTHERS THEN
dbms_sql.close_cursor(c1);
utl_file.fclose(l_output);
RAISE;
END;