When executing an SQL statement in PLSQL with
DBMS_SQL.EXECUTE('insert into tablename VALUES(aNumber)');
It gives an ASCII character error.
And when inserting it directly it succeeds and inserts it.
In PL/SQL you could write the INSERT statement directly.
DECLARE
tablevalue varchar2(200);
BEGIN
tablevalue := 'Hello World!';
INSERT INTO tablename
VALUES (tablevalue);
END;
Your statement fails because that is not the way DBMS_SQL.EXECUTE works. Check out the documentation and the example: http://docs.oracle.com/cd/B28359_01/appdev.111/b28419/d_sql.htm#BABBFFFJ
According to the example given in the reference documentation you should do it like this (first you prepare the statement, then bind the variable and then run it).
CREATE OR REPLACE PROCEDURE demo(tablevalue IN varchar2) AS
cursor_name INTEGER;
rows_processed INTEGER;
BEGIN
cursor_name := dbms_sql.open_cursor;
DBMS_SQL.PARSE(cursor_name, 'INSERT INTO tablename VALUES(:x)',
DBMS_SQL.NATIVE);
DBMS_SQL.BIND_VARIABLE(cursor_name, ':x', tablevalue);
rows_processed := DBMS_SQL.EXECUTE(cursor_name);
DBMS_SQL.CLOSE_CURSOR(cursor_name);
EXCEPTION
WHEN OTHERS THEN
DBMS_SQL.CLOSE_CURSOR(cursor_name);
raise;
END;
You use it then like this
exec demo('something');
Hope it helps
Related
I'm currently using this code to generate an XLS file from a query in Oracle PL/SQL saving a file with HTML tags using this code.
CREATE OR REPLACE PROCEDURE GENERATE_XLS(querie VARCHAR2) IS
v_file VARCHAR2 (20) := 'TEST-EXCEL.xls';
v_directory VARCHAR2 (60) := 'C:\';
p_write LIB_FILES.file_type;
v_sql varchar2(32767) := 'SELECT LASTNAME, NAME, ID
FROM DUAL';
header VARCHAR(1000) := '<html> <head><center><B><U> TEST XLS </U></B></center>
<table></tr><tr><th>LASTNAME</th><th>NAME</th><th>ID</th></tr>';
BEGIN
p_write := LIB_FILES.fopen (v_directory, v_file, 'W');
LIB_FILES.put_line (p_write, header);
FOR REGISTRY IN querie LOOP
LIB_FILES.put_line (p_write,'<tr><td>'||REGISTRY.LASTNAME||'</td><td>'||REGISTRY.NAME||'</td><td>'||REGISTRY.ID||'</td></tr>');
END LOOP;
LIB_FILES.put_line (p_write,'</table>');
LIB_FILES.fflush (p_write);
LIB_FILES.fclose (p_write);
END;
This is working right now, but I need to add to this procedure a parameter, which is an SQL query, and then generate the file based on the query
I'm currently use this code to get the columns name
CREATE OR REPLACE PROCEDURE GET_COLUMNS IS
v_cursor_id integer;
v_col_cnt integer;
v_columns dbms_sql.desc_tab;
v_sql varchar2(3000) :='SELECT LASTNAME, NAME, ID FROM DUAL';
header VARCHAR(1000):='<html> <head><center><B><U> TEST XLS </U></B></center>
<table></tr><tr>';
begin
v_cursor_id := dbms_sql.open_cursor;
dbms_sql.parse(v_cursor_id, v_sql, dbms_sql.native);
dbms_sql.describe_columns(v_cursor_id, v_col_cnt, v_columns);
for i in 1 .. v_columns.count loop
header := header || '<th>'|| v_columns(i).col_name ||'</th>';
end loop;
header := header || '</tr>';
dbms_sql.close_cursor(v_cursor_id);
exception when others then
dbms_sql.close_cursor(v_cursor_id);
raise;
end;
But here I'm facing the same issue, I need to make this procedures works with dynamic queries coming from a parameter instead of declaring the query in the procedure.
How can I achieve this?
I don't have oracle installed , as of my knowledge I gave these details ... check and verify this
CREATE OR REPLACE PROCEDURE GET_COLUMNS (p_sql IN VARCHAR2 , p_header IN VARCHAR2)
IS
v_cursor_id integer;
v_col_cnt integer;
v_columns dbms_sql.desc_tab;
v_sql varchar2(3000) := p_sql;
header VARCHAR(1000):= p_header;
begin
v_cursor_id := dbms_sql.open_cursor;
dbms_sql.parse(v_cursor_id, v_sql, dbms_sql.native);
dbms_sql.describe_columns(v_cursor_id, v_col_cnt, v_columns);
for i in 1 .. v_columns.count loop
header := header || '<th>'|| v_columns(i).col_name ||'</th>';
end loop;
header := header || '</tr>';
dbms_sql.close_cursor(v_cursor_id);
exception when others then
dbms_sql.close_cursor(v_cursor_id);
raise;
end;
To execute
Execute GET_COLUMNS ('SELECT LASTNAME, NAME, ID FROM DUAL','<html> <head><center><B><U> TEST XLS </U></B></center>
<table></tr><tr>');
If you define your query as a ref cursor instead of a string, e.g.
open your_refcursor for select * from dual;
then you can convert it to a dbms_sql cursor ID using
v_cursor_id := dbms_sql.to_cursor_number(your_refcursor);
Here is one I wrote earlier:
http://www.williamrobertson.net/documents/refcursor-to-csv.shtml
I suggest read the following article, about a package to create Excel files from Oracle:
Create an Excel-file with PL/SQL
I've been looking at the DBMS_SQL package in Oracle and trying to see if there is a way to create a view or something that users can select from to see the results of a dynamic SQL query.
I have a test procedure based on the documentation:
CREATE OR REPLACE PROCEDURE test_dyn_sql AS
cursor_name INTEGER;
rows_processed INTEGER;
l_query LONG;
BEGIN
l_query := 'SELECT SYSDATE AS the_date, ''ABC'' AS the_string, 1 AS the_int FROM dual';
cursor_name := dbms_sql.open_cursor;
DBMS_SQL.PARSE(cursor_name, l_query, DBMS_SQL.NATIVE);
rows_processed := DBMS_SQL.EXECUTE(cursor_name);
DBMS_SQL.CLOSE_CURSOR(cursor_name);
EXCEPTION
WHEN OTHERS THEN
DBMS_SQL.CLOSE_CURSOR(cursor_name);
DBMS_OUTPUT.put_line('ERROR');
END;
But this just executes the statement and does not return anything. What I'd like is a view so a user can just do a SELECT the_date FROM some_view and get the results. I will not know the names or number of columns in advance, so that's why I'm after a dynamic SQL solution.
" I will not know the names or number of columns in advance, so that's
why I'm after a dynamic SQL solution"
That's pretty hard to implement in SQL: SQL is all about the data structures, and it really expects the columns to exist up front. So you cannot build a VIEW on a mutable data structure.
You can implement a function which returns a ref cursor. This is a pointer to a data structure which can be interpreted by a client, say as a JDBC ResultSet.
Here's an example function which takes a table name and column name, assembles the query and returns its result set.
CREATE OR REPLACE FUNCTION test_dyn_sql
(tab_name in varchar2
, col_name in varchar2)
return sys_refcursor
AS
return_value sys_refcursor;
BEGIN
open return_value for
'SELECT SYSDATE AS the_date, '||col_name||' FROM '||tab_name;
return return_value;
END;
/
The output is not very elegant in SQL*Plus but you get the idea.
SQL> select test_dyn_sql ('EMP', 'NAME') from dual;
TEST_DYN_SQL('EMP','
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
THE_DATE NAME
--------- ------------------------------
03-APR-15 FOX IN SOCKS
03-APR-15 MR KNOX
03-APR-15 DAISY-HEAD MAYZIE
SQL>
I suggest you stick with Native Dynamic SQL (that is, execute immediate) as much as possible: as you can see, it's really simple compared to DBMS_SQL. Only reach for DBMS_SQL when you have some extremely complicated requirement.
I appreciate this may not be the solution form you're looking for. If that is the case please edit your question to provide more details about the problem you're trying to solve.
If you want your code to return the value of rows_processed variable use the code below which will return 0.
CREATE OR REPLACE FUNCTION test_dyn_sql(asd INTEGER) RETURN INTEGER AS
cursor_name INTEGER;
rows_processed INTEGER;
l_query LONG;
BEGIN
l_query := 'SELECT SYSDATE AS the_date, ''ABC'' AS the_string, 1 AS the_int FROM dual';
cursor_name := dbms_sql.open_cursor;
DBMS_SQL.PARSE(cursor_name, l_query, DBMS_SQL.NATIVE);
rows_processed := DBMS_SQL.EXECUTE(cursor_name);
DBMS_SQL.CLOSE_CURSOR(cursor_name);
RETURN rows_processed;
EXCEPTION
WHEN OTHERS THEN
DBMS_SQL.CLOSE_CURSOR(cursor_name);
DBMS_OUTPUT.put_line('ERROR');
RETURN 0;
END;
This is how you call the function from plsql
DECLARE
rows_precessed INTEGER;
BEGIN
rows_precessed := test_dyn_sqll(0);
DBMS_OUTPUT.put_line(rows_precessed);
END;
We have a procedure starting with following code:
CREATE OR REPLACE PROCEDURE get_id
(id_ IN OUT number, type_ IN number)
IS
PRAGMA AUTONOMOUS_TRANSACTION;
local_id number;
BEGIN
EXECUTE IMMEDIATE 'SELECT SYS_LOCAL_ID_SERIAL_SEQ.NEXTVAL into :local_id FROM dual';
...
Now if i execute this, the variable "local_id" is not filled with the next sequence value, but with null (although the sequence is raised by 1). If i change this to "... into local_id ..." i get ORA Error 1008 . What is going wrong here?
A local variable from the procedure can be bind to the query placeholder with USING [OUT][IN] clause:
local_id number;
BEGIN
EXECUTE IMMEDIATE
'SELECT SYS_LOCAL_ID_SERIAL_SEQ.NEXTVAL into :local_id FROM dual'
USING OUT local_id;
But for this query you don't need execute immediate, just do:
local_id number;
BEGIN
SELECT SYS_LOCAL_ID_SERIAL_SEQ.NEXTVAL into local_id FROM dual;
on Oracle 11g you can do it using the assignment operator:
local_id number;
BEGIN
local_id := SYS_LOCAL_ID_SERIAL_SEQ.NEXTVAL;
Change the following line
EXECUTE IMMEDIATE 'SELECT SYS_LOCAL_ID_SERIAL_SEQ.NEXTVAL into :local_id FROM dual';
to
EXECUTE IMMEDIATE 'SELECT SYS_LOCAL_ID_SERIAL_SEQ.NEXTVAL FROM dual' into local_id;
I have tested this code in Oracle 11g.
'SELECT '||RTRIM(V_XUEWC)||' - '||RTRIM(V_XUAC)||'
FROM table
WHERE ACCOUNT_NUM='||RTRIM(V_ACC)||'';
While i am trying assign this result to a variable in execute immediate.. it doesnt return anything. V_XUEWC and V_XUAC are run time variables.
Is there anyway to get the input parameter values, if any, bound to the a statement that caused an error in the database from a trigger? Regardless if the statement in SQL och a procedure/function call.
I'm trying to set up a log of errors on a schema level, which works fine. But I can't find a way to log the parameter values as well, and that would really be helpfull.
So far the trigger I'm using looks like this...
CREATE OR REPLACE TRIGGER t_error
AFTER SERVERERROR
ON SCHEMA
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
l_sql ora_name_list_t;
l_sequence NUMBER;
l_statement CLOB;
l_count NUMBER;
l_ts TIMESTAMP (6);
BEGIN
l_count := ora_sql_txt (l_sql);
IF l_count >= 1 THEN
FOR i IN 1 .. l_count LOOP
l_statement := l_statement || l_sql (i);
END LOOP;
END IF;
INSERT INTO oralog
VALUES (
seq_oralog.NEXTVAL,
dbms_utility.format_call_stack,
dbms_utility.format_error_stack,
dbms_utility.format_error_backtrace,
l_statement,
SYSTIMESTAMP
);
COMMIT;
END t_error;
/
Thanks
Trying to execute a procedure dynamically with DBMS_SQL that takes 'table of varchar' and sys_refcursor as an argument using the code below:
DECLARE
TYPE CriteriaMap IS TABLE OF VARCHAR (100)
INDEX BY VARCHAR2 (100);
o_cursor SYS_REFCURSOR;
v_cid INTEGER;
v_dummy INTEGER;
v_date_to_run DATE := SYSDATE;
v_sql_execute_proc VARCHAR2 (1024);
v_filter_criteria CriteriaMap;
BEGIN
v_sql_execute_proc :=
'begin MY_PROCEDURE(:v_date_to_run, :filter_criteria, :o_cursor); end;';
v_cid := DBMS_SQL.open_cursor;
DBMS_SQL.parse (v_cid, v_sql_execute_proc, DBMS_SQL.native);
DBMS_SQL.bind_variable (v_cid, 'v_date_to_run', v_date_to_run);
DBMS_SQL.bind_variable (v_cid, 'filter_criteria', v_filter_criteria);
DBMS_SQL.bind_variable (v_cid, 'o_cursor', o_cursor);
v_dummy := DBMS_SQL.execute (v_cid);
DBMS_SQL.close_cursor (v_cid);
END;
as the result the following error is thrown
Error report:
ORA-06550: line 14, column 3:
PLS-00306: wrong number or types of arguments in call to 'BIND_VARIABLE'
Documentation http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_sql.htm says that BIND_VARIABLE takes only a limited number of data types and 'table of varchar' and sys_refcursor are not in the list.
Is there any workaround to pass arguments to a dynamic function which data types are not in the list?
table of varchar is suppoorted through bind_array (though it has to be as per the spec in dbms_sql which is indexed by integer and not varchar2).
can you explain more on why you're using dynamic SQL in this case? as your example does not warrant dynamic SQL as the structure of the call is fixed here.
I know it's old... but if anyone encounters this - it is possible (in 11g, not sure about earlier) to convert the sys ref cursor to a cursor number, bind it, and then transform it to a refcursor after the execute - something like:
declare
vSQL varchar2(1000) := 'declare
v_rc sys_refcursor;
begin
open v_rc for select ''this is a test'' from dual;
:v_cursor_number := dbms_sql.to_cursor_number(v_rc);
end;';
v_rc sys_refcursor;
v_cursor_number NUMBER;
v_cur number;
v_result number;
vFetchValue varchar2(20);
begin
--open the cursor
v_cur := DBMS_SQL.OPEN_CURSOR;
--parse
DBMS_SQL.PARSE(v_cur, vSQL, dbms_sql.native);
--bind the cursor number
DBMS_SQL.BIND_VARIABLE(v_cur, 'v_cursor_number', v_cursor_number);
--execute
v_result := DBMS_SQL.EXECUTE(v_cur);
-- get back the value of the bind cursor number
DBMS_SQL.VARIABLE_VALUE(v_cur,'v_cursor_number', v_cursor_number);
--transform it to a standard sys_refcursor
v_rc := DBMS_SQL.TO_REFCURSOR (v_cursor_number);
--close the cursor
DBMS_SQL.CLOSE_CURSOR(v_cur);
fetch v_rc into vFetchValue;
close v_rc;
dbms_output.put_line(vFetchValue);
end;
/