Select INTO with binding parameters not working with execute immediate - oracle

I have the following query that returns 1 when executed:
SELECT COUNT(*) FROM TABLE_NAME WHERE Column1='x' AND Column2='y';
In my PL/SQL block, I need to evaluate the condition above in order to execute the business logic. Below is a simplified version:
DECLARE
column1 VARCHAR(20):='x';
column2 VARCHAR(20):='y';
rows_no NUMBER :=0;
sql_query VARCHAR2(200) :='SELECT COUNT(*) FROM TABLE_NAME WHERE Column1=:1 AND Column2=:2';
BEGIN
EXECUTE IMMEDIATE sql_query INTO rows_no USING column1, column2;
DBMS_OUTPUT.PUT_LINE('ROWS NO: '|| rows_no);
END;
The result from the execution of the PL/SQL block is 0 that is different from the result when the query is executed that is 1. I think I abiding by the rules of the binding parameter to query select into in the PL/SQL. I will appreciate any help or guide.
Best Regards,
Rando.
P.S
When I make the modification below :
DECLARE
column1 VARCHAR(20):='x';
column2 VARCHAR(20):='y';
rows_no NUMBER :=0;
sql_query VARCHAR2(200) :='SELECT COUNT(*) FROM TABLE_NAME WHERE Column1=''x'' AND Column2=''y''';
BEGIN
EXECUTE IMMEDIATE sql_query INTO rows_no;
DBMS_OUTPUT.PUT_LINE('ROWS NO: '|| rows_no);
END;
The result is: 1
I have a loop that reads from an excel file and inserts it into the database table(Table_name). The result above is needed to prevent the insertion of dublicates records. The commit is issued at the end of the procedure.
That means that if a record is added from the procedure it remains uncommitted till the procedure finishes.
I doubt this is a problem, uncommitted inserts are not accessible from other connections but in the current connection, they should be accessible. The following has all the simplified logic of the plsql block:
DECLARE;
--declaration variables
BEGIN
LOOP
-- fetching information from excel file
EXECUTE IMMEDIATE sql_query INTO rows_no;
DBMS_OUTPUT.PUT_LINE('ROWS NO: '|| rows_no);
IF rows_no=0 THEN
-- insert the information read from excel in database table
rows_inserted:=rows_inserted+1;
END IF;
END LOOP;
IF rows_inserted>0 THEN
COMMIT;
DBMS_OUTPUT.PUT_LINE('ROWS INSERTED: '||rows_inserted);
ELSE
DBMS_OUTPUT.PUT_LINE('No rows were inserted');
END IF;
END

The problem is most probably outside from Oracle, please try to re-test following script
(The output is shown as comment)
create table tab (column1 varchar2(1), column2 varchar2(1));
set SERVEROUTPUT ON
DECLARE
column1 VARCHAR(20):='x';
column2 VARCHAR(20):='y';
rows_no NUMBER :=0;
sql_query VARCHAR2(200) :='SELECT COUNT(*) FROM TAB WHERE Column1=:1 AND Column2=:2';
BEGIN
EXECUTE IMMEDIATE sql_query INTO rows_no USING column1, column2;
DBMS_OUTPUT.PUT_LINE('ROWS NO: '|| rows_no);
END;
/
-- ROWS NO: 0
insert into tab (column1, column2) values ('x','y');
DECLARE
column1 VARCHAR(20):='x';
column2 VARCHAR(20):='y';
rows_no NUMBER :=0;
sql_query VARCHAR2(200) :='SELECT COUNT(*) FROM TAB WHERE Column1=:1 AND Column2=:2';
BEGIN
EXECUTE IMMEDIATE sql_query INTO rows_no USING column1, column2;
DBMS_OUTPUT.PUT_LINE('ROWS NO: '|| rows_no);
END;
/
-- ROWS NO: 1

Related

Put Column Name in Variable and use it in output statement

Here is What i actually wanted to do, Fetch Data From a Table without knowing any columns but i.j.Column_Name gives an error, so i wanted to put it in a variable. After reading the comments i think it's not possible
DECLARE
CURSOR C1 IS SELECT * FROM Table_Name;
CURSOR C2 IS SELECT Table_Name,Column_Name FROM user_tab_columns
WHERE data_type='VARCHAR2';
v_table Varchar2(256);
v_Col varchar2(200);
BEGIN
FOR i in C1 LOOP
FOR j in (SELECT Column_Name FROM user_tab_columns WHERE
Table_Name='Table_Name') LOOP
dbms_output.put_line(i.j.Column_Name);
END LOOP;
END LOOP;
END;
/
No, There is no Column Named v_Col
You can't refer to a field in a record (which is what the cursor loop is giving you) dynamically. If you need to do flexibly then you can use dbms_sql (possibly adapting this approach), but in the scenario you've shown you could use dynamic SQl to only get the column you want in the cursor:
-- dummy data
create table table_name (id number, column_name varchar2(10), other_col date);
insert into table_name values (1, 'Test 1', sysdate);
insert into table_name values (2, 'Test 2', sysdate);
DECLARE
CURSOR C1 IS SELECT * FROM Table_Name;
v_Cur sys_refcursor;
v_Col varchar2(200);
v_Val varchar2(4000);
BEGIN
v_Col:= 'Column_Name';
OPEN v_Cur for 'SELECT ' || v_Col || ' FROM Table_Name';
LOOP
FETCH v_Cur INTO v_Val;
EXIT WHEN v_Cur%notfound;
dbms_output.put_line(v_val);
END LOOP;
END;
/
Test 1
Test 2
PL/SQL procedure successfully completed.
The downside of this is that whatever the data type of the target column is, you have to implicitly convert it to a string; but you would be doing that in the dbms_output call anyway. So if you change the column you want to print:
v_Col:= 'Other_Col';
then the output from my dummy data would be:
2018-08-23
2018-08-23
PL/SQL procedure successfully completed.
where the date value is being implicitly formatted as a string using my current NLS session settings.
You could get more advanced by checking the data type in user_tab_columns and changing the dynamic query and/or the fetch and handling, but it isn't clear what you really need to do.

replace values in all columns of a table with oracle

I would like to replace all the cells of a table that match a specific word. I wrote this query:
UPDATE table_name
SET column_name=REPLACE(column_name
,'string_to_be_replaced'
, 'string_replaced')
What will the procedure that will replace the values for all the columns of table_name not only one column as in the code above?
It is something that I will have to do it againg and again to update some tables.
Thanks
Here is some test data:
SQL> select * from t23;
ID NAME JOB
---------- -------------------- --------------------
10 JACK JANITOR
20 JAN TUTOR
30 MOHAN JAZZ DANCER
40 JOAN MECHANIC
SQL>
I want to replace all instances of 'JA' with 'MO'. This means I need to update NAME and JOB. Obviously I could write an UPDATE statement but I can also generate one using the magic of the data dictionary:
SQL> select column_name, data_type
2 from user_tab_cols
3 where table_name = 'T23';
COLUMN_NAME DATA_TYPE
------------------------------ ----------
ID NUMBER
NAME VARCHAR2
JOB VARCHAR2
SQL>
This seems like a one-off task, for which I need an anonymous PL/SQL block rather than a permanent procedure. So here is a script, saved as gen_upd_stmt.sql.
declare
stmt varchar2(32767);
target_string varchar2(20) := 'JA';
replace_string varchar2(20) := 'MO';
begin
stmt := 'update t23 set ';
for lrec in ( select column_name
, row_number() over (order by column_id) as id
from user_tab_cols
where table_name = 'T23'
and data_type = 'VARCHAR2'
)
loop
if lrec.id > 1 then
stmt := stmt || ',';
end if;
stmt := stmt || lrec.column_name || '=replace('
|| lrec.column_name || ', ''' || target_string
|| ''',''' || replace_string
|| ''')';
end loop;
-- uncomment for debugging
-- dbms_output.put_line(stmt);
execute immediate stmt;
dbms_output.put_line('rows updated = '|| to_char(sql%rowcount));
end;
/
Note that generating dynamic SQL is a gnarly process, because syntax errors are thrown at run time rather than compile time. Escaping quotes can be particularly pestilential. It's a good idea to display the generated statement to make debugging easier.
Also, I restricted the targeted columns to those with the correct datatype. This isn't strictly necessary, as replace() will handle type casting for us (in most cases). But it's more efficient with big tables to exclude columns we know won't match.
Anyway, let's roll!
SQL> set serveroutput on
SQL> #gen_upd_stmt
rows updated = 4
PL/SQL procedure successfully completed.
SQL>
As expected all four rows are updated but not all are changed:
SQL> select * from t23;
ID NAME JOB
---------- -------------------- --------------------
10 MOCK MONITOR
20 MON TUTOR
30 MOHAN MOZZ DANCER
40 JOAN MECHANIC
SQL>
For completeness the generated statement was this:
update t23 set NAME=replace(NAME, 'JA','MO'),JOB=replace(JOB, 'JA','MO')
With a larger table or more complicated requirement I would probably introduce line breaks with chr(13)||chr(10) to make the generated code more readable (for debugging).
You can try something like this. Rest update it as per your requirement.
DECLARE
L_statement VARCHAR2(4000) := 'UPDATE :table_name SET ';
CURSOR c_get_cols IS
SELECT column_name
FROM dba_tab_cols
WHERE table_name = :table_name;
TYPE Cur_tab IS TABLE OF c_get_cols%ROWTYPE;
L_tab Cur_tab;
BEGIN
OPEN c_get_cols;
FETCH C_get_cols INTO L_tab;
CLOSE C_get_cols;
FOR i IN 1..L_tab.COUNT
LOOP
L_statement := L_statement || L_tab(i).column_name || ' = REPLACE(column_name, :string_to_be_replaced, :string_replaced)';
IF i != L_tab.COUNT
THEN
L_statement := L_statement || ',';
END IF;
END LOOP;
EXECUTE IMMEDIATE L_statement;
END;
/
I tried it using cursor: (replace owner and table_name with respective values)
DECLARE
COL_NAME ALL_TAB_COLUMNS.COLUMN_NAME%TYPE;
string_to_be_replaced VARCHAR2(20) ;
string_replaced VARCHAR2 (20) ;
exc_invalid_id EXCEPTION;
PRAGMA EXCEPTION_INIT(exc_invalid_id, -904);
CURSOR c1 IS
SELECT COLUMN_NAME FROM ALL_TAB_COLUMNS WHERE owner=<owner_name> AND TABLE_NAME=<table_name> ;
BEGIN
string_to_be_replaced :='';
string_replaced :='';
OPEN C1;
LOOP
FETCH C1 INTO COL_NAME ;
EXIT WHEN C1%NOTFOUND;
EXECUTE immediate('UPDATE <owner_name>.<table_name> SET '||col_name||'=REPLACE('||col_name||','''||string_to_be_replaced||''','''||string_replaced||''')');
END LOOP;
CLOSE C1;
END;

PL/SQL execute immediate parameter with cursor

CREATE OR REPLACE PROCEDURE ZAD_CZWARTE AS
row_num number;
sql_stmnt varchar2(200);
cursor kursor is
select table_name from user_tables;
tab_name varchar2(200);
BEGIN
FOR c_kur IN kursor LOOP
tab_name := c_kur.table_name;
--dbms_output.put_line(tab_name);
sql_stmnt :='SELECT COUNT(*) FROM :1';
execute immediate sql_stmnt into row_num using tab_name;
DBMS_OUTPUT.PUT_LINE(tab_name||' : '||row_num);
END LOOP;
END zad_czwarte;
Theres a problem with execute immediate line. Anybody knows whats the problem? everything seems just fine for me. The task is to output table names of the user with number of rows in them.
You can't bind physical table as bind variable. Your command must be sql_stmnt :='SELECT COUNT(*) FROM ' || tab_name; and then execute immediate sql_stmnt into row_num;

Oracle : Create Table as Select statement and Select query on created table in Single Stored Procedure

I want Stored Procedure which create Temporary table using Create Table ... Select ... Statement.
And then select the records from same created table.
And finally drop that created table...
I want all these functionality in same stored procedure.
I have create the following stored procedure. But i got the below error
PL/SQL: ORA-00942: table or view does not exist
This one is my Procedure
DECLARE
TEMP_TBL VARCHAR2(4000);
TBL_NAME VARCHAR2(200) := 'ABC_TEST';
BEGIN
TEMP_TBL := 'CREATE TABLE MY_TAB_COL AS(SELECT TABLE_NAME,COLUMN_NAME,DATA_TYPE,to_lob(DATA_DEFAULT) AS DATA_DEFAULT,NULLABLE FROM ALL_TAB_COLS WHERE TABLE_NAME=''' || TBL_NAME || ''')';
DBMS_OUTPUT.PUT_LINE(TEMP_TBL);
EXECUTE IMMEDIATE TEMP_TBL;
FOR DD_COLUMNS IN
(SELECT TABLE_NAME FROM MY_TAB_COL)
LOOP
DBMS_OUTPUT.PUT_LINE('DD_COLUMNS.TABLE_NAME');
END LOOP;
END;
I'm not sure why you want scenario like that, and not reccomend to use this code in production. I'm pretty sure in the 99% of the cases, this approach can be written in other way. But you can try the code bellow(Everything in your procedure should use dynamic sql)
declare
TBL_NAME varchar2(200) := 'ABC_TEST';
lv_Sql varchar2(4000);
begin
lv_Sql := 'CREATE TABLE MY_TAB_COL AS(SELECT TABLE_NAME,COLUMN_NAME,DATA_TYPE,to_lob(DATA_DEFAULT) AS DATA_DEFAULT,NULLABLE FROM ALL_TAB_COLS WHERE TABLE_NAME=''' ||
TBL_NAME || ''')';
DBMS_OUTPUT.PUT_LINE(lv_Sql);
execute immediate lv_Sql;
lv_Sql := 'begin
for DD_COLUMNS in (select TABLE_NAME from MY_TAB_COL) loop
DBMS_OUTPUT.PUT_LINE(DD_COLUMNS.TABLE_NAME);
end loop;
end;';
DBMS_OUTPUT.PUT_LINE(lv_Sql);
execute immediate lv_Sql;
for R in (select *
from user_objects
where object_name = 'MY_TAB_COL'
and object_type = 'TABLE') loop
DBMS_OUTPUT.PUT_LINE('drop table ' || R.Object_Name);
execute immediate 'drop table ' || R.Object_Name;
end loop;
end;

Get the count of each table

I was trying to create a procedure to fetch the total count of rows of all tables corresponding to a schema.
I am proceeding with a cursor which store the total list of tables and and the same is iterate further. Even though the functionality is not tested.The procedure creation compiled with the following errors.
create or replace
PROCEDURE PROC_TABLE_COUNT
AS
table_count NUMBER;
CURSOR total_tables
IS
SELECT table_name FROM dba_tables WHERE owner = 'OWNER_NAME';
BEGIN
FOR i IN total_tables
LOOP
SELECT COUNT (*) INTO table_count FROM dba_tables db where db.table_name = i.table_name;
END LOOP;
END PROC_TABLE_COUNT;
1)Error(7,6): PL/SQL: SQL Statement ignored
2)Error(7,33): PL/SQL: ORA-00942: table or view does not exist
3)Error(11,1): PL/SQL: SQL Statement ignored
4)Error(11,76): PL/SQL: ORA-00904: "I"."TABLE_NAME": invalid identifier
5)Error(11,76): PLS-00364: loop index variable 'I' use is invalid
Question 1:
Is the error 2(at dba_tables) is due to the grant being denied? By right clicking on the procedure name ,I tried to assign the privilege to debug and execute. But still the error persists.
Question 2:
Regarding the invalid identifier. Why is this error coming?
UPDATE:
As per one of the valuable comment I have changed the query and the compile error is gone. Now there is an issue in the logic.
create or replace procedure proc_tab_count as
table_count NUMBER;
CURSOR total_tables
IS
SELECT table_name FROM user_tables;
BEGIN
FOR i IN total_tables
LOOP
SELECT COUNT (*) INTO table_count FROM user_tables WHERE db.table_name = i.table_name; --Wrong logic here
DBMS_OUTPUT.put_line(i.table_name||'-COUNT:'||table_count);
END LOOP;
end proc_tab_count;
output is coming like:
Table1 -COUNT:1
Table2 -COUNT:1
Table3 -COUNT:1
Table3 -COUNT:1
Table4 -COUNT:1
Table5 -COUNT:1
Guess, you want to count rows in all your tables, and we need a dynamic SQL here.
EXCEUTE IMMEDIATE is used to frame the dynamic SQL making the OWNER.TABLE_NAME dynamic.
create or replace procedure proc_tab_count as
table_count NUMBER;
CURSOR total_tables
IS
SELECT table_name FROM user_tables;
BEGIN
FOR i IN total_tables
LOOP
/* Handle Exceptions */
BEGIN
EXECUTE IMMEDIATE 'SELECT COUNT (*) FROM '|| i.table_name INTO table_count;
DBMS_OUTPUT.put_line(i.table_name||'-COUNT:'||table_count);
EXCEPTION WHEN OTHERS
THEN
DBMS_OUTPUT.put_line('Error while Querying '||i.table_name||'-Error:'||SQLERRM);
END;
END LOOP;
end proc_tab_count;
I'm not so sure that the accepted answer works. Does user_tables contain owner? Otherwise, it's spot on.
Anyway. Another approach using SYS_REFCURSOR:
CREATE OR REPLACE PROCEDURE proc_table_count
CURSOR total_tables IS
SELECT table_name
FROM user_tables
ORDER BY table_name;
table_count NUMBER;
v_sql VARCHAR2(100);
v_cursor SYS_REFCURSOR;
BEGIN
FOR i IN total_tables
LOOP
BEGIN
v_sql := 'SELECT COUNT (*) FROM '||i.table_name;
OPEN v_cursor FOR v_sql;
FETCH v_cursor INTO table_count;
CLOSE v_cursor;
DBMS_OUTPUT.put_line(i.table_name||'-COUNT:'||table_count);
EXCEPTION WHEN OTHERS
THEN
DBMS_OUTPUT.put_line('Error while Querying '||i.table_name||'-Error:'||SQLERRM);
END;
END LOOP;
end proc_tab_count;

Resources