escaping '.' and using a variable in PL/SQL - oracle

I am a beginner when it comes to PL/SQL coding. I am trying to do some simple administrative work using PL/SQL. Can someone point out what I am doing wrong?
Here is my code:
prompt Enter a value for the Schema that you just refreshed:
define owner=&1
BEGIN
FOR x_rec IN (
SELECT table_name FROM dba_tables WHERE owner='&&owner'
)
LOOP
EXECUTE IMMEDIATE ''GRANT SELECT ON ' ||&&owner|| '.''||x_rec.table_name||'' to ' ||&&owner|| '_RO_ROLE'';
END LOOP;
END;

You have too many quotes, for one thing. SQL*Plus uses a '.' to optionally terminate a substitution variable. If you want an actual dot as well, just use two.
Try this:
accept owner char prompt "Enter the name of the schema that you have just refreshed: "
begin
for r in (
select table_name from dba_tables where owner = upper('&&owner')
)
loop
dbms_output.put_line('grant select on &&owner..' || r.table_name || ' to &&owner._ro_role');
execute immediate 'grant select on &&owner..' || r.table_name || ' to &&owner._ro_role';
end loop;
end;
/

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

Cursor Operation in Netezza

CREATE OR REPLACE PROCEDURE SP_NEW_PROCEDURE1( )
RETURNS REFTABLE(employees)
LANGUAGE NZPLSQL AS
BEGIN_PROC
DECLARE
l_conditions varchar(1000);
p_rec RECORD;
BEGIN
FOR P_REC IN select empid, mgrid, empname, salary from employees where mgrid = 7
LOOP
l_conditions := 'insert into ' ||
REFTABLENAME ||
' VALUES (' ||
P_REC.EMPID ||
',' ||
P_REC.MGRID ||
',' ||
P_REC.EMPNAME ||
',' ||
P_REC.SALARY ||
' ) ; ' ;
execute immediate l_conditions;
l_conditions := ' ';
END LOOP;
RETURN REFTABLE;
END;
END_PROC;
When I run this:
select SP_NEW_PROCEDURE1()
I get the errors:
ERROR [01000] NOTICE: Error occurred while executing PL/pgSQL function SP_NEW_PROCEDURE1
ERROR [01000] NOTICE: line 24 at execute statement
ERROR [42S22] ERROR: Attribute 'DAN' not found
Can someone help whats wrong ...thanks
This has nothing do with the cursor itself, and everything to do with how you are building your dynamical SQL string.
When building dynamic SQL in a Netezza stored procedure, you can use the quote_ident and quote_literal helper functions to let the system know whether you are passing it a literal, or whether you are passing it an identifier. There is an example in the online documentation here. Essentially all they do is figure out the escaped quotation notation needed.
Since you are trying to put the values stored in the columns of your P_REC record into the VALUES part of an insert statement, you could use quote_literal like this:
CREATE OR REPLACE PROCEDURE SP_NEW_PROCEDURE1( )
RETURNS REFTABLE(employees)
LANGUAGE NZPLSQL AS
BEGIN_PROC
DECLARE
l_conditions varchar(1000);
p_rec RECORD;
BEGIN
FOR P_REC IN select empid, mgrid, empname, salary from employees where mgrid = 7
LOOP
l_conditions := 'insert into ' ||
REFTABLENAME ||
' VALUES (' ||
quote_literal(P_REC.EMPID) ||
',' ||
quote_literal(P_REC.MGRID) ||
',' ||
quote_literal(P_REC.EMPNAME) ||
',' ||
quote_literal(P_REC.SALARY ) ||
' ) ; ' ;
execute immediate l_conditions;
l_conditions := ' ';
END LOOP;
RETURN REFTABLE;
END;
END_PROC;
That being said, using a cursor to loop over records to insert a row one at a time is horribly inefficient in an MPP database like Netezza. Assuming this question is a follow-on from your question about an alternative to a recursive CTE to explore hierarchies, there's nothing wrong with looping in general, but try to avoid doing it record by record. Here is a version that will exploit the MPP nature of the system. For the record, if you are going to return your result set to a REFTABLE, then your only choice is Dynamic SQL.
CREATE OR REPLACE PROCEDURE SP_NEW_PROCEDURE1( )
RETURNS REFTABLE(employees)
LANGUAGE NZPLSQL AS
BEGIN_PROC
DECLARE
l_conditions varchar(1000);
p_rec RECORD;
BEGIN
-- FOR P_REC IN select empid, mgrid, empname, salary from employees where mgrid = 7
-- LOOP
l_conditions := 'insert into ' ||
REFTABLENAME ||
' select empid, mgrid, empname, salary from employees where mgrid = 7 ; ' ;
execute immediate l_conditions;
l_conditions := ' ';
-- END LOOP;
RETURN REFTABLE;
END;
END_PROC;
I suspect that you are building a query that is meant to insert a literal 'DAN' but which does not include the required quote marks, hence it is referencing DAN -- the optimiser is therefore trying to find an attribute of that name.
So the fix is to include the quotation marks when you build the SQL insert statement, or (preferably) to just use static SQL to insert the values instead of execute immediate.
When in doubt, always look at the data, as this would probably have been obvious to you if you inspected the value of l_conditions.

oracle procedure giving ORA-00905: missing keyword

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.

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