ROWID as Parameters in PL/SQL - oracle

I am trying to create a helper stored proc to save on repeated code.
I wrote the following stored procedure that takes the table name, status_id, and ROWID.
PROCEDURE sp_update_stage_status(p_table_name IN VARCHAR2,
p_status_id IN NUMBER,
p_rowid IN ROWID)
AS
BEGIN
execute immediate 'UPDATE ' || p_table_name
|| ' SET STATUS_ID = ' || p_status_id
|| ' WHERE ROWID = ' || p_rowid;
END;
However whenever I execute it I get the following.
ORA-00904: "AAATQEAAEAAAAHEAAB": invalid identifier
ORA-06512: at "OBR_DEV.PKG_COMMON", line 32
ORA-06512: at "OBR_DEV.PKG_DIRECTORY", line 449
What am I doing wrong here?

You're dropping the contents of rowid in directly without quoting it.
Your query became WHERE ROWID = AAATQEAAEAAAAHEAAB which is comparing the rowid column to the AAATQEAAEAAAAHEAAB column.
It should be WHERE ROWID = 'AAATQEAAEAAAAHEAAB'. Add some quotes to your dynamic SQL and you should be okay.
Or better yet, use bind variables and don't worry about quoting:
EXECUTE IMMEDIATE
'UPDATE ' || p_table_name || ' SET status_id = :status WHERE rowid = :seek_rowid'
USING p_status_id, p_rowid;

You are declaring p_rowid as rowid type, you must declare p_rowid as varchar2(18).
PROCEDURE sp_update_stage_status(
p_table_name IN VARCHAR2,
p_status_id IN NUMBER,
p_rowid IN VARCHAR2(18)
) AS
BEGIN
execute immediate 'UPDATE ' || p_table_name || ' SET STATUS_ID = ' || p_status_id || ' WHERE ROWID = ' || p_rowid;
END;

Related

Procedure to check table for duplicates - Oracle PL/SQL

Very new to SQL in general.
Have seen a few examples on how to declare table as variable in PL/SQL, however, none of them seem to do what I need.
The procedure is quite simple, check for duplicate unique numbers in a table, eg:
select unique_id,
count(unique_id) as count_unique
from table_name
having count(unique_id)>1
group by unique_id
I would like to create a procedure that can be called and dynamically change the _name and the unique_id.
Something like:
declare
table_name is table:= table_1
unique_id varchar2(100):= unique_1
begin
select unique_id,
count(unique_id) as count_unique
from table_name
having count(unique_id)>1
group by unique_id
end;
/
If you want to change the table at runtime, you'd need dynamic SQL which means that you'd need to assemble the SQL statement you want in a string variable and execute that string. If you have a procedure, you'd need that procedure to do something with the results of the query. My guess is that you want to return a cursor.
Note that I'm not doing anything to validate the table and column names to avoid SQL injection attacks. You'd probably want to use dbms_assert to validate the input rather than blindly trusting the caller.
create or replace procedure get_duplicates( p_table_name in varchar2,
p_column_name in varchar2,
p_rc out sys_refcursor )
as
l_sql varchar2(1000);
begin
l_sql := ' select ' || p_column_name || ', ' ||
' count(' || p_column_name || ') as unique_count ' ||
' from ' || p_table_name ||
' group by ' || p_column_name ||
' having count(' || p_column_name || ') > 1';
dbms_output.put_line( l_sql );
open p_rc for l_sql;
end;

how to provide multiple bind variable for execute immediate inside pl/sql block

I have a table which contains the metadata of the table. The task is to periodically delete a specific set of tables, provided the information for where condition and how many days the data is retained are present.If a user needs to delete a data on daily basis, he simply enter his table name in audit log. The procedure will do the rest. The example is shown below.
Table structure:
CREATE TABLE delete_tbl_list (
id NUMBER NOT NULL,
table_name VARCHAR2(100) NOT NULL,
column_name VARCHAR2(100) NOT NULL,
day_retented NUMBER NOT NULL,
where_clause VARCHAR2(2000)
);
the day_retended is the number which will tell on how many days the data can hold.
select * from delete_tbl_list
ID TABLE_NAME COLUMN_NAME DAY_RETENTED WHERE_CLAUSE
---------- -----------------------------------------------
1 audit_log log_TS 60
So if i need to delete a table taking log_ts(timestamp) as column with 60days period as retention. The table in query needs to do
delete * from audit_log where log_ts<systimestamp -60
Now i need to do it using bulk delete and more dynamic and hence i wrote the procedure below,
create procedure dynamic_mass_delete as
TYPE tbl_rec_rowid IS TABLE OF ROWID INDEX BY PLS_INTEGER;
lv_del_exec_rec tbl_rec_rowid;
v_limit PLS_INTEGER := 10000;
m_date date:=sysdate;
total_records_deleted number:=0;
l_where delete_tbl_list.where_clause%type;
l_sql varchar2(2000);
TYPE ref_cur_type IS REF CURSOR;
delete_content ref_cur_type;
BEGIN
for i in (select table_name,COLUMN_NAME,DAY_RETENTED,WHERE_CLAUSE from delete_tbl_list) loop
DBMS_OUTPUT.PUT_LINE('tablename..'||i.table_name);
l_where:='';
m_date:=m_date-i.day_retented;
if i.where_clause is not null then
l_where:=' and '||i.where_clause;
end if;
OPEN delete_content FOR 'SELECT rowid from ' || i.table_name ||' where '|| i.COLUMN_NAME || ' <= to_timestamp('''||m_date||''')'||l_where;
LOOP
total_records_deleted := 0;
FETCH delete_content BULK COLLECT INTO lv_del_exec_rec LIMIT v_limit;
FORALL j IN lv_del_exec_rec.first..lv_del_exec_rec.last
execute immediate 'DELETE FROM :1 where rowid=:2 'using i.table_name,lv_del_exec_rec(j);
total_records_deleted := total_records_deleted + SQL%rowcount;
DBMS_OUTPUT.PUT_LINE('Delete count..'||total_records_deleted);
EXIT WHEN delete_content%notfound;
END LOOP;
CLOSE delete_content;
end loop;
EXCEPTION
when others then
DBMS_OUTPUT.PUT_LINE('Error-->'||SQLERRM);
END;
/
Now i getting error in the delete query stating invalid table name, i was not able to write dbms_output inside for all statment. Is it possible to use multiple bind variable inside a pl/sql procedure.
The error which i get is ,
Error-->ORA-00942: table or view does not exist
PL/SQL procedure successfully completed.
The table very much exists, but it is throwing error, i was not able to print inside the forall block too.
Switch to
execute immediate 'DELETE FROM ' || i.table_name ||' where rowid = ' || lv_del_exec_rec(j);
You can simplify your code as follows:
BEGIN
FOR TBL_DETAILS IN (
SELECT
TABLE_NAME,
COLUMN_NAME,
DAY_RETENTED,
WHERE_CLAUSE
FROM
DELETE_TBL_LIST
) LOOP
DBMS_OUTPUT.PUT_LINE('tablename..' || TBL_DETAILS.TABLE_NAME);
EXECUTE IMMEDIATE 'DELETE FROM '
|| TBL_DETAILS.TABLE_NAME
|| ' WHERE '
|| TBL_DETAILS.COLUMN_NAME
|| ' < SYSTIMESTAMP - '
|| TBL_DETAILS.DAY_RETENTED
|| CASE
WHEN TBL_DETAILS.WHERE_CLAUSE IS NOT NULL THEN ' AND ' || TBL_DETAILS.WHERE_CLAUSE
END;
DBMS_OUTPUT.PUT_LINE('Delete count..' || SQL%ROWCOUNT);
END LOOP;
END;
/
Hope, This will help you in creating simpler code.
Cheers!!

How do I get the data from the column_name? Oracle PL/SQL

How do I get the data(i.e rows) from the column_name I retrieved from SQL statement? (Completely new to PL/SQL).
Here is my code:
create or replace procedure com_coll_cur
as
type comColcur is ref cursor;
com_col_cur comColcur;
sql_stmt varchar2(4000);
type newtab_field is table of comColcur%TYPE;
begin
sql_stmt :=
'select column_name from all_tab_cols where table_name in (''TAB1'', ''TAB2'') ' ||
'group by column_name having count(*)>1';
open com_col_cur for sql_stmt;
loop
fetch com_col_cur into newtab_field;
exit when com_col_cur%NOTFOUND;
end loop;
close com_col_cur;
end;
What I'm trying to do here is first find the common columns between the two tables. This part only grabs column_name but I also want the data in that common columns. So I used cursor to "point" that common column_name and used loop(fetch) to get the data inside that common column_name. Finally, I want to create new table with this common columns only with their data.
I am new to everything here and any help will be appreciate it.
You don't understand how works cursors and fetch.
Fetch get the data from the cursor, so in your procedure example you get only column names, not the data in the columns. To get data you need another cursor - select from the target table or use the dynamic sql.
This is a code that do what you describe. It is not clear to me how you want to store data from two tables - subsequently or in another manner. Let's assume that we store them subsequently. Also this code suggests than columns with the same names have the same datatypes. Part of this code (to make datatype) I get from another stackoverflow post to save time to write it:
How do I get column datatype in Oracle with PL-SQL with low privileges?
dbms_output.put_line - used to print sql statements that we create
declare
cSql varchar2(4000);
cCols varchar2(4000);
cNewTableName varchar2(30) := 'AABBCC';
cTb1 varchar2(30) := 'TAB1';
cTb2 varchar2(30) := 'TAB2';
begin
for hc in (
select T.column_name, T.typ
from
(
select column_name,
data_type||
case when data_precision is not null and nvl(data_scale,0)>0 then '('||data_precision||','||data_scale||')'
when data_precision is not null and nvl(data_scale,0)=0 then '('||data_precision||')'
when data_precision is null and data_scale is not null then '(*,'||data_scale||')'
when char_length>0 then '('||char_length|| case char_used
when 'B' then ' Byte'
when 'C' then ' Char'
else null
end||')'
end||decode(nullable, 'N', ' NOT NULL') typ
from all_tab_cols
where table_name in (cTb1, cTb2) ) T
group by T.column_name, T.typ having count(*) > 1)
loop
cSql := cSql || case when cSql is null then null else ',' end || hc.column_name || ' ' || hc.typ;
cCols := cCols || case when cCols is null then null else ',' end || hc.column_name;
end loop;
if (cSql is not null) then
-- First drop table if it exists
for hc in (select * from all_objects where object_type = 'TABLE' and object_name = cNewTableName)
loop
execute immediate 'drop table ' || hc.object_name;
end loop;
-- create table
cSql := 'create table ' || cNewTableName || '(' || cSql || ')';
dbms_output.put_line(cSql);
execute immediate cSql;
-- insert data
cSql := 'insert into ' || cNewTableName || '(' || cCols || ') select ' || cCols || ' from ' || cTb1;
dbms_output.put_line(cSql);
execute immediate cSql;
cSql := 'insert into ' || cNewTableName || '(' || cCols || ') select ' || cCols || ' from ' || cTb2;
dbms_output.put_line (cSql);
execute immediate cSql;
end if;
end;

ORA-01403: No Data found WHY?

I have declared the following procedure:
CREATE OR REPLACE PROCEDURE MODIFY_NOT_NULL(
v_tbName IN VARCHAR2,
v_cName IN VARCHAR2,
v_defaultValue IN VARCHAR2 )
IS
v_is_null VARCHAR2(1);
BEGIN
SELECT nullable INTO v_is_null
FROM USER_TAB_COLUMNS
WHERE TABLE_NAME = v_tbName
AND COLUMN_NAME = v_cName;
IF v_is_null = 'Y' THEN
EXECUTE IMMEDIATE ('ALTER TABLE ' || v_tbName
|| ' MODIFY (' || v_cName
|| ' DEFAULT ' || v_defaultValue
|| ' NOT NULL )');
END IF;
END;
However when I execute my code:
BEGIN
modify_not_null('TABLE_NAME', 'COLUMN_NAME ' ,'0');
END;
/
I am getting a
"ORA-01403: No Data Found"
This exception will be usually thrown if the "SELECT INTO" statement does not return any value, however I will always get a value when I execute this:
Select nullable
from USER_TAB_COLUMNS
WHERE table_name = 'TABLE_NAME'
AND column_name = 'COLUMN_NAME';
When I execute the code above, I get "N" or "Y" as a result. So I always get a result. I don't know why this exception is thrown
Your call contains a trailing space:
modify_not_null('TABLE_NAME', 'COLUMN_NAME ' ,'0');
^
So proc throws no data found because 'COLUMN_NAME ' != 'COLUMN_NAME'
Use upper(trim(v_cName)) to prevent typos causing errors. Apply on all parameters.
You are passing v_defaultValue param to column name.
Change procedure to
SELECT nullable INTO v_is_null
FROM USER_TAB_COLUMNS
WHERE TABLE_NAME = v_tbName AND COLUMN_NAME = v_cName ;
Before your SELECT .... INTO, you have to make sure that there is something to select. Because depending on what user you are, and what parameters you give, there may be no data in your table.
A simple way would be to have a COUNT at the beginning before the SELECT:
CREATE OR REPLACE PROCEDURE MODIFY_NOT_NULL(
v_tbName IN VARCHAR2,
v_cName IN VARCHAR2,
v_defaultValue IN VARCHAR2 )
IS
v_is_null VARCHAR2(1);
v_count number;
BEGIN
-- added select count
SELECT count(1) INTO v_count FROM USER_TAB_COLUMNS WHERE TABLE_NAME = trim(v_tbName) AND COLUMN_NAME = trim(v_cName);
-- added if v_count=1
if v_count = 1 then
SELECT nullable INTO v_is_null FROM USER_TAB_COLUMNS WHERE TABLE_NAME = trim(v_tbName) AND COLUMN_NAME = trim(v_cName);
IF v_is_null = 'Y' THEN
EXECUTE IMMEDIATE ('ALTER TABLE ' || v_tbName || ' MODIFY (' || v_cName || ' DEFAULT ' || v_defaultValue || ' NOT NULL )');
END IF;
-- added
end if;
END;
/
Share and enjoy
stay classy :)
create or replace procedure modify_not_null(v_tbName in varchar2,
v_cName in varchar2,
v_defaultValue in varchar2) is
cursor c_tbl(cp_tbname in varchar2,
cp_cname in varchar2) is
select nullable
from user_tab_columns
where table_name = upper(cp_tbname)
and column_name = upper(cp_cname);
l_tbl c_tbl%rowtype;
begin
open c_tbl(cp_tbname => v_tbName,
cp_cname => v_cName);
fetch c_tbl into l_tbl;
close c_tbl;
if l_tbl.nullable = 'Y' then
execute immediate 'alter table ' || v_tbName || ' modify (' || v_cName ||
' default ' || v_defaultValue || ' not null )';
end if;
exception
when others then
raise_application_error(-20000, dbms_utility.format_error_stack);
end modify_not_null;

Need help with PL SQL Query [Execute Immediate]

I have this query
EXECUTE IMMEDIATE 'UPDATE ' || dest || ' SET COUNTRY_CODE = :v1 WHERE col_id = :v2 returning rowid into :out'
USING l_vc_CountryCode, l_vc_ColId
returning into l_vc_rowid;
the l_vc_rowid is defined as varchar2(10);
I am trying to google, but couldn't find the problem.
Use sql%rowcount to determine how many rows were affected by the update.
With a working PL/SQL function:
create table tq84_update_test (
country_code number,
col_id varchar2(10)
);
create or replace
function tq84_update_func (dest in varchar2,
col_id in varchar2,
country_code in number
)
return varchar2
as
begin
execute immediate
'update ' || dest || ' set country_code = :v1 ' ||
'where col_id = :v2'
using country_code, col_id;
return sql%rowcount || ' rows updated';
end tq84_update_func;
/
insert into tq84_update_test values (4,'Italy');
exec dbms_output.put_line(tq84_update_func('tq84_update_test', 'foo', 2));
exec dbms_output.put_line(tq84_update_func('tq84_update_test', 'Italy', 9));
select * from tq84_update_test;

Resources