oracle procedure giving ORA-00905: missing keyword - oracle

I'm trying to create a generic procedure to synchronize sequences.
I want to call the procedure and pass name of table, column and sequence but my procedure won't run due to an error.
Procedure:
CREATE OR REPLACE PROCEDURE INCREMENT_SEQ(table_name in varchar2 , id_column in varchar2 , sequence_name in varchar2)
AS
current_value number;
seq_val number := -1;
begin
EXECUTE IMMEDIATE 'select max(' || table_name || '.' || id_column || ') into current_value from ' || table_name ;
WHILE current_value >= seq_val
LOOP
EXECUTE IMMEDIATE 'select ' || sequence_name || '.nextval into seq_val from dual';
end loop;
end;
when I run the script I'm having the following error:
Error at line 2
ORA-00905: missing keyword
ORA-06512: at "TGC100_DEV.INCREMENT_SEQ", line 6
ORA-06512: at line 1
Script Terminated on line 16.
But I have no idea how to solve. Any advice would be helpfull.

You should put INTO clause outside the query:
CREATE OR REPLACE PROCEDURE INCREMENT_SEQ(table_name in varchar2 , id_column in varchar2 , sequence_name in varchar2)
AS
current_value number;
seq_val number := -1;
begin
EXECUTE IMMEDIATE 'select max(' || table_name || '.' || id_column || ') from ' || table_name into current_value;
WHILE current_value >= seq_val
LOOP
EXECUTE IMMEDIATE 'select ' || sequence_name || '.nextval from dual' into seq_val;
end loop;
end;

EXECUTE IMMEDIATE 'select max(' || table_name || '.' || id_column || ') into current_value from ' || table_name ;
It is syntactically incorrect. The INTO clause should be outside of EXECUTE IMMEDIATE statement.
Something like,
EXECUTE IMMEDIATE 'your SQL statement' INTO variable USING value;
UPDATE It is better to have the dynamic SQL as a variable to avoid confusions with so many single quotes and concatenation in the EXECUTE IMMEDIATE statement itself.
The other answer by Aramilo was posted before my answer, but I got confused to see the INTO clause already outside the statement.
For developers, it is always a good practice to first check the dynamic SQL using DBMS OUTPUT before actually executing it. Thus, it saves a lot of time to debug the whole bunch of PL/SQL code. Once confirmed that the dynamic SQL formed is correct, remove the DBMS_OUTPUT and execute the PL/SQL code.

Related

Not able to iterate through values output from select statement

I am stuck not able to iterate through values output from select statement.
My code :
CREATE OR replace PROCEDURE getdetails ( v_tb in VARCHAR2 )
AS
BEGIN
FOR i IN
(
SELECT table_name
FROM all_tables
WHERE table_name LIKE ''''
|| v_tb
|| '%'
|| '''' )
LOOP
DBMS_OUTPUT.PUT_LINE(i);
/*
My logic will be coming that I cannot share ...
*/
END LOOP;
END;
Calling
BEGIN
Getdetails('DEMO');
END;
Issue : The select statement is not getting initialized with v_tb
SELECT table_name
FROM all_tables
WHERE table_name LIKE ''''
|| v_tb
|| '%'
|| ''''
Any solution is much appreciated !!!
When I compile your code I get:
14/8 PL/SQL: Statement ignored
14/8 PLS-00306: wrong number or types of arguments in call to 'PUT_LINE'
Errors: check compiler log
The reason is that "i" cannot be used as argument in dbms_output because it isn't a scalar value, it is a record variable. The solution is to reference the actual column that is selected in the statement
DBMS_OUTPUT.PUT_LINE(i.table_name);
The reason that the statement never returns any rows is that the quotes are double escaped. Escape a quote with a 2nd quote. 4 quotes to escape a single quote is too much in a plain pl/sql statement.
So, putting it all together gives:
CREATE OR replace PROCEDURE getdetails ( v_tb in VARCHAR2 )
AS
BEGIN
FOR i IN
(
SELECT table_name
FROM all_tables
WHERE table_name LIKE ''
|| v_tb
|| '%'
|| '' )
LOOP
DBMS_OUTPUT.PUT_LINE(i.table_name);
/*
My logic will be coming that I cannot share ...
*/
END LOOP;
END;
/
Procedure GETDETAILS compiled
set serveroutput on size 999999
clear screen
BEGIN
Getdetails('BOO');
END;
/
BOOK
BOOK_TITLE
PL/SQL procedure successfully completed.
/
If you need quotes in your string, you should write the SQL query as follows:
SELECT table_name
FROM all_tables
WHERE table_name LIKE ''''
|| v_tb
|| '%'
|| '''some text'''
some text will be concatenated as 'some text'

Execute SQL from cursor value

(Just to preempt - I know how hacky and flawed this approach is. It's just going to be a personal use thing to make a certain task easier.)
For this example, I want to create two dynamic SQL insert statements which insert the first two results from ALL_TABLES into GT_DUMMY, padded with an input value.
CREATE GLOBAL TEMPORARY TABLE GT_DUMMY
( Test_Field VARCHAR(100)
);
CREATE OR REPLACE PROCEDURE LOAD_GT_DUMMY
( Insert_String VARCHAR
)
IS
BEGIN
FOR col IN
( SELECT 'INSERT INTO GT_DUMMY VALUES(' || CHR(39) || Insert_String || Table_Name || CHR(39) || ');' AS insertStatement
FROM ALL_TABLES
WHERE ROWNUM <= 2
) LOOP
DBMS_OUTPUT.put_line(col.insertStatement);
-- Result of EXEC LOAD_GT_DUMMY('SOMETHING'); :
-- INSERT INTO GT_DUMMY VALUES('SOMETHINGDUAL');
-- INSERT INTO GT_DUMMY VALUES('SOMETHINGSYSTEM_PRIVILEGE_MAP');
-- This command fails when
EXECUTE IMMEDIATE col.insertStatement;
END LOOP;
END;
The inserts are well formed and will execute if I just run them standalone, but the EXECUTE IMMEDIATE col.insertStatement; command is not working. The procedure compiles, but when I try to run
EXEC LOAD_GT_DUMMY('SOMETHING');
I get an error
ORA-00933: SQL command not properly ended
ORA-06512: at "MY_SCHEMA.LOAD_GT_DUMMY", line 14
ORA-06512: at line 1
Any thoughts? Is my syntax off?
Best.
Remove the terminating semi-colon; dynamic SQL doesn't like it.
SELECT 'INSERT INTO GT_DUMMY VALU... <snip> ... || CHR(39) || ');'
^
|
here
Try with INTO v_result in the end of EXECUTE statement
EXECUTE IMMEDIATE col.insertStatement INTO v_result;
P.S. don't forget to declare v_result variable
You can check similar example down here
Remove the colon in sql statement and execute it will work
FOR col IN
( SELECT 'INSERT INTO GT_DUMMY VALUES(' || CHR(39) || Insert_String || Table_Name || CHR(39) || ')' AS insertStatement
FROM ALL_TABLES
WHERE ROWNUM <= 2
) LOOP

Execute Immediate Statement issue - Variable field name and table name

Hi there I am trying to build a function to reset a sequence to synch with table ID's which have gotten out of synch with the sequence. Function is as follows:
create or replace
FUNCTION P_FNC_SEQUENCERESET(sourceTable IN VARCHAR2, idField IN VARCHAR2, seqname VARCHAR2) RETURN NUMBER
IS
ln NUMBER;
ib NUMBER;
maxId NUMBER;
newValue NUMBER;
diffValue NUMBER;
interimValue NUMBER;
sqlStmt VARCHAR2(2000);
BEGIN
-- Get the maximum of the id field
EXECUTE IMMEDIATE 'SELECT MAX(' || idField || ') INTO ' || maxId || ' FROM ' || sourceTable;
...code continues...
My understanding of the EXECUTE IMMEDIATE statement leads me to believe that this should be possible, however when executed I get this error:
ORA-00936: missing expression
ORA-06512: at "PSALERT_ADMIN.P_FNC_SEQUENCERESET", line 16
ORA-06512: at line 11
It would need to look something like this:
EXECUTE IMMEDIATE 'SELECT ' || idField ||' FROM ' || sourceTable into maxid;
the keyword "into" is not part of the string that is dynamically executed, but it is part of the syntax of the "execute immediate" statement
https://docs.oracle.com/cd/B19306_01/appdev.102/b14261/executeimmediate_statement.htm

Generating Dynamic SQL in Oracle

I have a problem with my plsql code and try almost everything. Now i'm loosing my mind and power to solve my problem :)
The case is that I want to search all tables schema for specific string assigned to variable v_ss and print it to DBMS_OUTPUT. I know that there are ready-made solutions for that kind of cases, but I wanted to code it by myself. Below code gives me an error that "table does not exist" in my v_stmt. I assume that this select does not recognize rec.column_name, but why?
Here is my code:
DECLARE
v_stmt VARCHAR2(1000);
v_ss VARCHAR2(30) := 'Argentina';
v_own ALL_TAB_COLUMNS.OWNER%TYPE;
v_tab_nam ALL_TAB_COLUMNS.TABLE_NAME%TYPE;
v_col_nam ALL_TAB_COLUMNS.COLUMN_NAME%TYPE;
CURSOR cur_asc IS
SELECT t.owner, t.table_name, t.column_name
FROM SYS.ALL_TAB_COLUMNS t
WHERE t.OWNER LIKE 'HR'
AND t.DATA_TYPE LIKE 'VARCHAR2';
BEGIN
FOR rec IN cur_asc LOOP
v_stmt := 'SELECT rec.owner, rec.table_name, rec.column_name FROM rec.table_name WHERE rec.column_name LIKE :1';
EXECUTE IMMEDIATE v_stmt INTO v_own, v_tab_nam, v_col_nam USING v_ss;
DBMS_OUTPUT.put_line(v_own || ':' || v_tab_nam || ':' || v_col_nam);
END LOOP;
END;
/
Error report -
ORA-00942: table or view does not exist
ORA-06512: at line 19
00942. 00000 - "table or view does not exist"
*Cause:
*Action:
Could You give me an explanation for my bug and how to fix it? Thanks in advance
The dynamic statement is executed in a context that cannot see your PL/SQL variables, so when it runs rec.table_name etc. are interpreted as SQL-level objects - which don't exist.
You have to concatenate the variable values into the dynamic statement for the from and where clause; you can do the same in the select list (though they'd need to be wrapped in escaped single-quotes as they are strings), or use bind variables there:
v_stmt := 'SELECT :owner, :table_name, :column_name FROM '
|| rec.table_name || ' WHERE ' || rec.column_name || ' LIKE :ss';
EXECUTE IMMEDIATE v_stmt INTO v_own, v_tab_nam, v_col_nam
USING rec.owner, rec.table_name, rec.column_name, v_ss;
You can't use bind variables for object identifiers, hence needing the concatenation for those parts.
Unless you run this as the HR user, in which case you could be using user_tables instead of all_tables, you also need to specify the schema in the query (As Tony and Lalit mentioned); either hard-coding HR or using the queried owner:
v_stmt := 'SELECT :owner, :table_name, :column_name FROM '
|| rec.owner || '.' || rec.table_name
|| ' WHERE ' || rec.column_name || ' LIKE :ss';
This is going to error for all the tables that don't contain exactly one matching value though - if the dynamic select gets zero rows, or more than one row. But that's a separate issue.
v_stmt := 'SELECT rec.owner, rec.table_name, rec.column_name FROM rec.table_name WHERE rec.column_name LIKE :1';
Your dynamic sql statement is malformed. If you enclose the variables between single-quotation marks then they are treated as literals and not variables any more.
You must prefix the schema before the table_name else you must run the script while you are connected as HR user.
v_stmt := 'SELECT '''||rec.owner||''', '''|| rec.table_name||''', '
||rec.column_name||' FROM '||rec.owner||'.'
||rec.table_name||' WHERE '||rec.column_name||' LIKE :1';
Always remember, whenever working with dynamic statements, always use DBMS_OUTPUT to first verify the actual SQL being generated. This is the best way to debug dynamic queries.
You need to handle NO_DATA_FOUND exception because it will throw error for all those tables which do not have any match for the filter you are using.
SQL> DECLARE
2 v_stmt VARCHAR2(1000);
3 v_ss VARCHAR2(30) := 'Argentina';
4 v_own ALL_TAB_COLUMNS.OWNER%TYPE;
5 v_tab_nam ALL_TAB_COLUMNS.TABLE_NAME%TYPE;
6 v_col_nam ALL_TAB_COLUMNS.COLUMN_NAME%TYPE;
7 CURSOR cur_asc
8 IS
9 SELECT t.owner,
10 t.table_name,
11 t.column_name
12 FROM SYS.ALL_TAB_COLUMNS t
13 WHERE t.OWNER LIKE 'HR'
14 AND t.DATA_TYPE LIKE 'VARCHAR2';
15 BEGIN
16 FOR rec IN cur_asc
17 LOOP
18 v_stmt := 'SELECT '''||rec.owner||''', '''|| rec.table_name||''', '
19 || rec.column_name||' FROM '||rec.owner||'.'
20 || rec.table_name||' WHERE '||rec.column_name||' LIKE :1';
21 BEGIN
22 EXECUTE IMMEDIATE v_stmt INTO v_own,
23 v_tab_nam,
24 v_col_nam USING v_ss;
25 DBMS_OUTPUT.put_line(v_own || ':' || v_tab_nam || ':' || v_col_nam);
26 EXCEPTION
27 WHEN NO_DATA_FOUND THEN
28 NULL;
29 END;
30 END LOOP;
31 END;
32 /
HR:COUNTRIES:Argentina
PL/SQL procedure successfully completed.
There are 2 issues here:
You need to concatenate the table and column names into the dynamic SQL because 'SELECT rec.owner...' is trying to select a column called owner from a table with alias rec, it does not reference your for loop record.
Since there could be no matching rows or many matching rows in a table, you cannot use select into, you need to use a cursor.
try this:
DECLARE
v_stmt VARCHAR2(1000);
v_ss VARCHAR2(30) := 'Argentina';
v_own ALL_TAB_COLUMNS.OWNER%TYPE;
v_tab_nam ALL_TAB_COLUMNS.TABLE_NAME%TYPE;
v_col_nam ALL_TAB_COLUMNS.COLUMN_NAME%TYPE;
CURSOR cur_asc IS
SELECT t.owner, t.table_name, t.column_name
FROM ALL_TAB_COLUMNS t
WHERE t.DATA_TYPE LIKE 'VARCHAR2'
AND ROWNUM < 10;
c SYS_REFCURSOR;
BEGIN
FOR rec IN cur_asc LOOP
v_stmt := 'SELECT ''' || rec.owner || ''',''' || rec.table_name || ''', ''' || rec.column_name || ''' FROM ' || rec.table_name || ' WHERE ' || rec.column_name || ' LIKE :1';
OPEN c FOR v_stmt USING v_ss;
LOOP
FETCH c INTO v_own, v_tab_nam, v_col_nam;
EXIT WHEN c%NOTFOUND;
DBMS_OUTPUT.put_line(v_own || ':' || v_tab_nam || ':' || v_col_nam);
END LOOP;
CLOSE c;
END LOOP;
END;
/

Execute For Each Table in PLSQL

I want to the the number of records in all tables that match a specific name criteria. Here is the SQL I built
Declare SQLStatement VARCHAR (8000) :='';
BEGIN
SELECT 'SELECT COUNT (*) FROM ' || Table_Name || ';'
INTO SQLStatement
FROM All_Tables
WHERE 1=1
AND UPPER (Table_Name) LIKE UPPER ('MSRS%');
IF SQLStatement <> '' THEN
EXECUTE IMMEDIATE SQLStatement;
END IF;
END;
/
But I get the following error:
Error at line 1
ORA-01422: exact fetch returns more than requested number of rows
ORA-06512: at line 3
Script Terminated on line 1.
How do I modify this so that it runs for all matching tables?
Update:
Based on an answer received, I tried the following but I do not get anything in the DBMS_OUTPUT
declare
cnt number;
begin
for r in (select table_name from all_tables) loop
dbms_output.put_line('select count(*) from CDR.' || r.table_name);
end loop;
end;
/
declare
cnt number;
begin
for r in (select owner, table_name from all_tables
where upper(table_name) like ('%MSRS%')) loop
execute immediate 'select count(*) from "'
|| r.owner || '"."'
|| r.table_name || '"'
into cnt;
dbms_output.put_line(r.owner || '.' || r.table_name || ': ' || cnt);
end loop;
end;
/
If you're selecting from all_tables you cannot count on having been given the grants necessary to select from the table name. You should therefore check for the ORA-00942: table or view does not exist error thrown.
As to the cause for your error: You get this error because the select statement returns a result set with more than one row (one for each table) and you cannot assign such a result set to a varchar2.
By the way, make sure you enable dbms_output with SET SERVEROUT ON before executing this block.

Resources