How to deal with the single quote in pl/sql code - oracle

I met below error when I execute the following code.
Error report:
ORA-01722: invalid number
ORA-06512: at line 12
01722. 00000 - "invalid number"
Can you please help me to find out the root reason.
declare
v_str varchar2(100):='XY';
v_cnt number := 1;
v_text varchar2(100);
v_sysdate date := sysdate;
begin
v_text := 'select to_char(' || '''' || v_sysdate || '''' || ',''yyyy/mm/dd'') from dual';
dbms_output.put_line(v_text);
execute immediate v_text into v_str;
dbms_output.put_line(v_str);
end;

The problem is that you are trying to convert a char to a char as if the first one were a date. Notice that you're executing the following query:
select to_char('2014-03-14','yyyy/mm/dd') from dual
It doesn't make any sense. The "date" (2014-03-14) is actually a string and you can't treat that like if it were a date.
If you just want to cast v_sysdate to a varchar2, you could just do:
v_str := to_char(v_sysdate,'yyyy/mm/dd');
Or maybe you just were studying how dynamic SQL works...

Please, everyone test it before answer... problem with execute immediate..
------ so i think this is what you wanted..
declare
v_str varchar2(100):='XY';
v_cnt number := 1;
v_text varchar2(100);
v_sysdate date := sysdate; --
begin
v_text := 'select to_char(:x,''yyyy.mm.dd'') from dual';--:x dynamically built sql statement using IN OUT v_variable1, IN v_variable2;
dbms_output.put_line(v_text);
execute immediate v_text into v_str using in v_sysdate;
dbms_output.put_line(v_str);
end;
you can't just put variable into execute string and expect something ;) you need to define him to use in that place... v_sysdate goes into defined :x place...

Related

Procedure working with query as a parameter in PL/SQL

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

PL/SQL ORA-00905: missing keyword for Set Value

I searched for this error, but since it's very vague, I could not find something similar to understand where is the problem. This code is actually for an Oracle Apex application. I actually have bind variables instead of numbers 1 and 84 (which I confirm are correct values within my tables), but still got same error.
After declaring the variables, it selects a string that will be the name of a column within another table and put it V_COLUMN.
Then i dynamically build a query to get the value of this column and put it into V_VALUE and finally I return a value (which is then shown in a form textfield). Unfortunately it returns the ORA 00905.
When I tried to run the sql commands separately using known values, it runs. So I think there must be some syntax problem somewhere in the dynamic sql. Thanks for any assistance.
DECLARE
V_COLUMN VARCHAR2(50) := 'UNKNOWN';
V_VALUE VARCHAR2(50) := 0;
V_SQL VARCHAR2(500);
BEGIN
SELECT SUB_CAT_ABBREV INTO V_COLUMN FROM SUB_CATEGORY WHERE SUB_CATEGORY_ID = 1;
V_SQL := 'SELECT ' || V_COLUMN || ' INTO V_VALUE FROM PLANNED_EFFORTS WHERE PLAN_ID = 84';
EXECUTE IMMEDIATE V_SQL;
RETURN V_VALUE;
EXCEPTION
WHEN no_data_found THEN
RETURN 'No Data Found Error';
WHEN too_many_rows then
RETURN 'Too many rows';
WHEN OTHERS THEN
RETURN 'Other Error';
END;
Just get rid off your INTO clause from your dynamic SQL statement:
V_SQL := 'SELECT ' || V_COLUMN || ' FROM PLANNED_EFFORTS WHERE PLAN_ID = 84';
EXECUTE IMMEDIATE V_SQL
INTO V_VALUE
Moreover, if you expect more then one value you can use BULK COLLECT INTO and return values into some collection type:
V_SQL := 'SELECT ' || V_COLUMN || ' FROM PLANNED_EFFORTS WHERE PLAN_ID = 84;
EXECUTE IMMEDIATE V_SQL
BULK COLLECT INTO V_VALUES
where V_VALUES can be declared as:
TYPE TABLE_OF_VARCHAR2 IS TABLE OF VARCHAR2(50);
V_VALUES TABLE_OF_VARCHAR2;
and accessed in the loop as follows:
for i in V_VALUES.FIRST .. V_VALUES.LAST LOOP
-- V_VALUES(i)
END LOOP;

DBMS_SQL.TO_REFCURSOR equivalent in Oracle 10g

I have the following code :
procedure Replace(sUser in Varchar2,sNomTable in varchar2,sColonne in varchar2,sID in Varchar2,nbCharAlterer IN NUMBER) is
l_cursor NUMBER;
l_return NUMBER;
l_ref_cursor SYS_REFCURSOR;
TYPE t_tab IS TABLE OF VARCHAR2(4000);
l_tab t_tab;
l_tab_Id t_tab;
sChaine VARCHAR2(4000 CHAR);
sqlReq CONSTANT VARCHAR2(1000):= 'select ' || sId || ',' || sColonne || ' from ' || sUser || '.' || sNomTable ;
begin
--
l_cursor := DBMS_SQL.open_cursor;
DBMS_SQL.parse(l_cursor, sqlReq, DBMS_SQL.NATIVE);
l_return := DBMS_SQL.EXECUTE(l_cursor);
-- Connvert from DBMS_SQL to a REF CURSOR.
l_ref_cursor := DBMS_SQL.to_refcursor(l_cursor);
Here I am getting the following error :
pls 00302 component 'TO_REFCURSOR' must be declared
since my oracle version is 10g.
Any idea of how to do the equivalent in Oracle 10g?
Here's how you could use native dynamic sql:
PROCEDURE p_replace(suser IN VARCHAR2,
snomtable IN VARCHAR2,
scolonne IN VARCHAR2,
sid IN VARCHAR2,
nbcharalterer IN NUMBER) IS
v_validate_sid_col_name VARCHAR2(32);
v_validate_scolonne_col_name VARCHAR2(32);
v_validate_suser VARCHAR2(32);
v_validate_snomtable VARCHAR2(32);
sqlreq VARCHAR2(2000);
refcur sys_refcur;
BEGIN
-- Check the input values are valid identifiers (to avoid sql injection)
-- N.B. this does not check they are valid object names!
v_validate_sid_col_name := dbms_assert.qualified_sql_name(sid);
v_validate_scolonne_col_name := dbms_assert.qualified_sql_name(scolonne);
v_validate_suser := dbms_assert.qualified_sql_name(suser);
v_validate_snomtable := dbms_assert.qualified_sql_name(scolonne);
sqlReq := 'select ' || v_validate_sid_col_name || ',' ||
v_validate_scolonne_col_name ||
' from ' || v_validate_suser || '.' || v_validate_snomtable;
-- or maybe you want to use execute immediate to bulk collect into arrays?
OPEN refcur FOR sqlreq;
...
END p_replace;
Note that I've changed the name of the procedure since "replace" is the name of a pre-existing built-in function, and therefore not a very good name to use.
You don't mention what it is you're going to do with the results of your query, so I wasn't sure if opening a ref cursor is what you actually need, or whether bulk collecting via execute immediate would work better for you.

PL/SQL Dynamic SQl

I am trying to run the sql below and retun all the recs with the batchid, declared on top, but i keep getting error, please advice
EXPECTED RESULT:- I need all the batches with the ID declared above, but the dyamanic sql generates for it, how can this sql generate just result for it ???
CREATE OR REPLACE PROCEDURE LAITEST
IS
declare
l_owner varchar2(30) := 'XXFMSLS';
l_batch varchar2(300) := 'PL_XFER_4';
l_sql varchar2(32000);
begin
l_sql := 'select * from XXFM_FAH_EVNT_CTRL where owner = l_owner and bch_id= l_batch';
dbms_output.put_line( l_sql);
end;
/
1) l_owner and l_batch should be dynamically added to the query, the sql sent to the database should have these values substituted.
2) since these are strings, you need to add in the necessary quotes.
3) You don't need DELCARE
CREATE OR REPLACE PROCEDURE LAITEST
IS
l_owner varchar2(30) := 'XXFMSLS';
l_batch varchar2(300) := 'PL_XFER_4';
l_sql varchar2(32000);
begin
l_sql := 'select * from XXFM_FAH_EVNT_CTRL where owner = ''' || l_owner || ''' and bch_id= ''' || l_batch || '''';
dbms_output.put_line( l_sql);
end;
/
set serveroutput on;
execute LAITEST;
select * from XXFM_FAH_EVNT_CTRL where owner = 'XXFMSLS' and bch_id= 'PL_XFER_4'

Can you help me write a procedure in Oracle to spool data from a table to a CSV file?

I am writing a procedure to create a CSV file with the data in an Oracle table. I used "spool filename;" but an error is coming. Can I use spool in PL/SQL?
I think that there are better ways to implement this on Oracle 10g/11g, but this should work fine on Oracle 9i or higher:
CREATE OR REPLACE PROCEDURE prc_file_mult_column_generate(
p_file_dir VARCHAR2, -- mandatory (Oracle directory name)
p_file_name VARCHAR2, -- mandatory
p_sql_query VARCHAR2, -- Multiple column SQL SELECT statement that needs to be executed and processed
p_delimiter CHAR -- column delimiter
)
AS
l_cursor_handle INTEGER;
l_dummy NUMBER;
l_col_cnt INTEGER;
l_rec_tab DBMS_SQL.DESC_TAB;
l_current_col NUMBER(16);
l_current_line VARCHAR2(2047);
l_column_value VARCHAR2(300);
l_file_handle UTL_FILE.FILE_TYPE;
l_print_text VARCHAR2(100);
l_record_count NUMBER(16) := 0;
BEGIN
/* Open file for append*/
l_file_handle := UTL_FILE.FOPEN(p_file_dir, p_file_name, 'a', 2047); --Append Mode, 2047 chars per line max, possibly increasable
l_cursor_handle := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(l_cursor_handle, p_sql_query, DBMS_SQL.native);
l_dummy := DBMS_SQL.EXECUTE(l_cursor_handle);
/* Output column names and define them for latter retrieval of data */
DBMS_SQL.DESCRIBE_COLUMNS(l_cursor_handle, l_col_cnt, l_rec_tab); -- get column names
/* Append to file column headers */
l_current_col := l_rec_tab.FIRST;
IF (l_current_col IS NOT NULL) THEN
LOOP
DBMS_SQL.DEFINE_COLUMN(l_cursor_handle, l_current_col, l_column_value, 300);
l_print_text := l_rec_tab(l_current_col).col_name || p_delimiter;
UTL_FILE.PUT (l_file_handle, l_print_text);
l_current_col := l_rec_tab.NEXT(l_current_col);
EXIT WHEN (l_current_col IS NULL);
END LOOP;
END IF;
UTL_FILE.PUT_LINE (l_file_handle,' ');
/* Append data for each row */
LOOP
EXIT WHEN DBMS_SQL.FETCH_ROWS(l_cursor_handle) = 0; -- no more rows to be fetched
l_current_line := '';
/* Append data for each column */
FOR l_current_col IN 1..l_col_cnt LOOP
DBMS_SQL.COLUMN_VALUE (l_cursor_handle, l_current_col, l_column_value);
l_print_text := l_column_value || p_delimiter;
l_current_line := l_current_line || l_column_value || p_delimiter;
END LOOP;
l_record_count := l_record_count + 1;
UTL_FILE.PUT_LINE (l_file_handle, l_current_line);
END LOOP;
UTL_FILE.FCLOSE (l_file_handle);
DBMS_SQL.CLOSE_CURSOR(l_cursor_handle);
EXCEPTION
WHEN OTHERS THEN
-- Release resources
IF DBMS_SQL.IS_OPEN(l_cursor_handle) THEN
DBMS_SQL.CLOSE_CURSOR(l_cursor_handle);
END IF;
IF UTL_FILE.IS_OPEN (l_file_handle) THEN
UTL_FILE.FCLOSE (l_file_handle);
END IF;
--RAISE ;
DBMS_OUTPUT.PUT_LINE(DBMS_UTILITY.format_error_stack);
END;
/
spool is a sqlplus command. it cannot be used in pl/sql.
it seems that you have been trying a variety of ways to get oracle to do your formatting and file saving. why not have your program that is calling the proc do this work for you?
If you only need the data in a cvs file you can do this:
create a sql file with the query like this:
set feedback off verify off heading off pagesize 0
select field1 || ',' || field2 ... from table;
quit;
/
then call sqlplus from a terminal like this:
sqlplus -S user/password #file.sql> cvsfile.cvs
No, SPOOL is a SQL Plus command so you would have to do this in SQL Plus:
spool myfile.txt
exec myproc
spool off
You would probably also need to set some values before starting the process e.g.
set pagesize 0 linesize 1000 trimspool on
... to get the correct formatting.
Here are a couple of links you might find helpful:
A PL/SQL Tutorial and SQL*Plus User Guide (11g)
Creating an ascii file: http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:459020243348 and http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:68212348056 and http://www.oracle-developer.net/display.php?id=425 .

Resources