Dynamic sql compiled but can't execute - sql

I am trying to display the duplicate records using dynamic sql(execute immediate). I am getting 'An identifier with more than 30 characters was specified' error. What am I doing wrong with the dynamic sql?
CREATE OR REPLACE PROCEDURE FIND_DUP(P_TABLE IN VARCHAR2, P_COLUMN IN VARCHAR2)
AS
stmt_txt varchar2(4000);
BEGIN
stmt_txt:= 'select'
||p_column
|| 'from'
||p_table
|| 'group by'
||p_column
||'having count(*)>1';
execute immediate stmt_txt;
end;
/
EXECUTE FIND_DUP('EMPLOYEES','FIRST_NAME');

You're missing some spaces in your query.
stmt_txt:= 'select '
||p_column
|| ' from '
||p_table
|| ' group by '
||p_column
||' having count(*)>1';
Without the spaces your query would end up as selectFIRST_NAMEfromEMPLOYEESgroup byFIRST_NAMEhaving count(*)>1, which to Oracle looks like an identifier with more than 30 characters.

Hi the first thing whih should be kept in mind while using dynamic sql is ALWAYS print the sql generated as it will give you the idea what query exactly you are running. This code might help you to solve your query as you have done almost everything right only you were missing with some spaces.
CREATE OR REPLACE
PROCEDURE FIND_DUP(
P_TABLE IN VARCHAR2,
P_COLUMN IN VARCHAR2)
AS
stmt_txt VARCHAR2(4000);
BEGIN
stmt_txt:= 'select' ||' ' ||p_column ||' ' ||'from' ||' ' ||p_table;
EXECUTE immediate stmt_txt;
DBMS_OUTPUT.PUT_LINE(STMT_TXT);
END;
For the output
BEGIN
FIND_DUP('DUAL','SYSTIMESTAMP');
END;
---------------------------------------------------------------------------
OUTPUT
select SYSTIMESTAMP from DUAL
Statement processed.
0.01 seconds
---------------------------------------------------------------------------

Related

How to pass table name as a parameter in update procedure in Oracle?

I am new to Oracle so please sorry the question that seems to be very easy for you.
I need to get the following procedure with UPDATE query with replace function
CREATE OR REPLACE PROCEDURE proc_replace_space_1
(
p_table user_tables.table_name%TYPE,
p_search IN varchar2,
p_replace IN varchar2
)
IS
BEGIN
EXECUTE IMMEDIATE
'update ' || p_table ||
'set docnum = replace(docnum, :2, :3 )'
USING p_search, p_replace;
END;
This procedure removes all spaces.
But when I call it
BEGIN
proc_replace_space_1('cm_risk.fct_loans_temp', ' ', '');
END;
I've got the following error
SQL Error [971] [42000]: ORA-00971: missing SET keyword
ORA-06512: at "CM_RISK.PROC_REPLACE_SPACE_1", line 9
ORA-06512: at line 2
How can I modify my code to handle the problems?
Thank you.
Dynamic SQL is hard because it turns compilation errors into runtime errors. So I urge you to acquire the good habit of assembling your dynamic SQL as string variables which you can persist to a log table if you have such a thing (and if you don't it would be another good habit to acquire) or display using dbms_output.put_line.
So your procedure would look like this:
CREATE OR REPLACE PROCEDURE proc_replace_space_1
(
p_table user_tables.table_name%TYPE,
p_search IN varchar2,
p_replace IN varchar2
)
IS
l_sql varchar2(32767);
BEGIN
l_sql := 'update ' || p_table ||
'set docnum = replace(docnum, :2, :3 )';
EXECUTE IMMEDIATE
l_stmt
USING p_search, p_replace;
exception
when others then
dbms_output.put_line(l_sql);
raise;
END;
This approach allows you to see the actual SQL your procedure executed. Probably you'll be able to spot the syntax error immediately (in this case it's the missing space between table name and set). Otherwise you can try to run the statement for yourself and see what the SQL compiler highlights.
NB: depending on your environment you may need to enable DBMS_OUTPUT before you can see the message.
You just need to add a space before set. Currently your table name is appended to set keyword and it is assuming it as a table name i.e MyTableSet
CREATE OR REPLACE PROCEDURE proc_replace_space_1
(
p_table user_tables.table_name%TYPE,
p_search IN varchar2,
p_replace IN varchar2
)
IS
BEGIN
EXECUTE IMMEDIATE
'update ' || p_table ||
' set docnum = replace(docnum, :2, :3 )'
USING p_search, p_replace;
END;

Passing table name and column name dynamically to PL/SQL Stored procedure

I am trying to pass table name and column name to a stored procedure in oracle , but it gives me following error: table or view does not exist
Below is the code:
create or replace procedure jz_dynamic_sql_statement
(p_table_name in varchar2,
p_col1_name in varchar2,
p_check_result out integer)
as
v_error_cd est_runtime_error_log.error_cd%type;
v_error_msg est_runtime_error_log.error_msg%type;
v_sql varchar2(1024);
v_result number(10);
begin
v_result := 0;
v_sql := 'select count(*) from ' || p_table_name ||' WHERE COLUMNNAME=' || p_col1_name;
execute immediate v_sql into v_result;
p_check_result := v_result;
end;
If the error coming back says the table does not exist then that means the table you pass in does not exist or the user that the procedure runs under cannot access it.
You could add a dbms_output.put_line statement to display the query that you are building and then try running it yourself, before you attempt the execute immediate. Then you know what errors you need to fix.
dbms_output.put_line('query : '||v_sql);
Be sure to turn on dbms_output.
Also, from what it looks like you are trying to do, you will need to pass the column name AND column value. Unless the tables you are querying will ALWAYS have the column name "COLUMNNAME".
Try this:
v_sql := 'select count(*) from ' || p_table_name ||' WHERE COLUMNNAME=''' || p_col1_name|| '''';

Cursor loaded with dynamic SQL

I got a problem with a cursor that I load with dynamic SQL in procedure.
My query contains a date and I have this error :
ORA-00932: inconsistent datatypes ; expected: DATE ; got: NUMBER
Here is my procedure :
create or replace procedure EMP_CURSOR (
p_date in date,
p_schema in varchar2
) is
c_emp sys_refcursor;
begin
open c_emp for
'select ID, NAME
from ' || DBMS_ASSERT.schema_name(p_schema) || '.EMP
where DATE_MAJ >= ' || p_date;
EMP (c_emp);
exception
when others then
DBMS_OUTPUT.put_line(SQLERRM);
end;
And this is how I call it :
exec EMP_CURSOR(to_date('01/01/2015','dd/MM/yyyy'),'TEST');
I don't know how to pass a date for a dynamic query.
When I removed the dynamic part and I put the schema name in the query, it works fine.
Oracle is implicitly converting p_date to a string according to your NLS_DATE_FORMAT, because you're concatenating it to a string; you need to use bind variables, per the documentation:
open c_emp for
'select ID, NAME
from ' || DBMS_ASSERT.schema_name(p_schema) || '.EMP
where DATE_MAJ >= :1' using p_date;
This also gives you a lot more protection from SQL injection.

Strange ORA-06502: PL/SQL: numeric or value error

I'm looping through the table definitions in a database and extracting the DDL in order to be able to create 'golden data' sets of table duplicates for software testing purposes. I've got the below code which extracts the DDL just fine up to one table where it throws a ORA-06502 error. The thing is, I've defined the variable that the DDL is being returned to as a CLOB, which should be big enough. Here's the code:
create or replace
procedure gettabledescription as
current_date_time varchar2(32);
sql_statement varchar2(200);
ddl_return clob;
new_ddl clob;
BEGIN
DBMS_OUTPUT.ENABLE;
current_date_time:= to_char(sysdate,'MM/DD/YYYY - HH24:MI:SS');
dbms_output.put_line(current_date_time);
for cursor_rec in (select * from user_objects where object_type='TABLE' and object_name not like 'TM%' and object_name not like 'TZ_%' and object_name not like 'EXT_%' and object_name not like 'RPT_%' and object_name not like 'ETL_%') loop
sql_statement := 'select dbms_metadata.get_ddl(''TABLE'',' || '''' || cursor_rec.object_name || '''' || ') from dual';
execute immediate sql_statement into ddl_return;
**error thrown here** new_ddl := replace(ddl_return,TO_CHAR(cursor_rec.object_name),'QA_' || TO_CHAR(cursor_rec.object_name));
dbms_output.put_line(new_ddl);
end loop;
END GETTABLEDESCRIPTION;
I don't understand why I'm getting that error.
On Oracle 10gR2, When I call DBMS_OUTPUT.PUT_LINE with a varchar2 or clob longer than 8191 bytes, I receive an ORA-06502 error. The solution would be to loop through the clob, pulling out lines and calling put_line once for each.

writing a generic procedure in oracle

i want to write procedure which accents name of 2 tables as arguments and then compare the number or rows of the 2.
Also i want to each field of the 2 columns.The row which has a missmatch shold be
moved to another error table.
Can anyone give a PL/SQL procedure for doing this.
I want to achive this in oracle 9
Pablos example wont work, the idea is right though.
Something like this do it.
create or replace PROCEDURE COMPARE_ROW_COUNT(T1 IN VARCHAR2, T2 IN VARCHAR2) AS
v_r1 number;
v_r2 number;
v_sql1 varchar2(200);
v_sql2 varchar2(200);
BEGIN
v_sql1 := 'select count(1) from ' || T1;
v_sql2 := 'select count(1) from ' || T2;
EXECUTE IMMEDIATE v_sql1 into v_r1;
EXECUTE IMMEDIATE v_sql2 into v_r2;
dbms_output.put_line(T1 || ' count = ' || v_r1 || ', ' || T2 || ' count = ' || v_r2);
END;
DBMS_SQL is your friend for such operations.
You can use dynamic sql in PL/SQL. EXECUTE IMMEDIATE is your friend.
So, if you take two table names and trying to compare their row counts, you would do something like:
CREATE OR REPLACE PROCEDURE COMPARE_ROW_COUNT(T1 IN VARCHAR2(200), T2 IN VARCHAR2(200)) AS
v_cursor integer;
v_r1 integer;
v_r2 integer;
v_sql varchar2(200);
BEGIN
v_sql := "select count(1) into :1 from " || T1;
EXECUTE IMMEDIATE v_sql USING v_r1;
v_sql := "select count(1) into :1 from " || T2;
EXECUTE IMMEDIATE v_sql USING v_r2;
-- compare v_r1 and v_r2
END;
Not 100% sure about PL/SQL syntax. It's been a while since the last time I coded in great PL/SQL!
You can achieve same results with similar approach using DBMS_SQL. Syntax is a little bit more complicated though.
I am just posting here to note that all answers gravitate around dynamic SQL, and do not turn the attention to the implied problems using it.
Consider passing the following string as first or second parameter:
dual where rownum = 0 intersect
SELECT 0 FROM dual WHERE exists (select 1 from user_sys_privs where UPPER(privilege) = 'DROP USER')
I'll leave it to that.
To answer your question - Oracle actually stores these values in the data dictionary, so if you have access to it:
CREATE OR REPLACE PROCEDURE COMPARE_ROW_COUNT(T1 IN VARCHAR2, T2 IN VARCHAR2) AS
v_text varchar2(1000);
BEGIN
select listagg(owner || ' ' || table_name || ' count = ' || num_rows, ',')
into v_text
from all_tables --user, all or dba tables depends on requirements
where table_name in (T1, T2);
dbms_output.put_line(v_text);
exception
when others then raise; -- Put anything here, as long as you have an exception block
END COMPARE_ROW_COUNT;