I'm trying to create a procedure for deleting rows how many rows inputted in special table, but I can't use table in input parameters - sql

CREATE OR REPLACE PROCEDURE delete_rows (table_name IN USER_TABLES.table_name%type, row_count in number)
IS
BEGIN
execute immediate 'delete from'||table_name||' where rowid in (select rowid from table_name fetch first Row_count rows only)';
dbms_output.put_line(sql%rowcount);
END;
/
I get this error:
PLS-00357: Table View Or Sequence reference 'EMPLOYEES' not allowed in this context

When working with dynamic SQL, it is almost ALWAYS useful to prepare statement you'll execute, review it and - once you're satisfied with it - actually execute it. Here's what your code is trying to do:
SQL> set serveroutput on
SQL> CREATE OR REPLACE PROCEDURE delete_rows
2 (table_name IN USER_TABLES.table_name%type,
3 row_count in number)
4 IS
5 l_str varchar2(200);
6 BEGIN
7 l_str := 'delete from'||table_name||' where rowid in ' ||
8 '(select rowid from table_name fetch first Row_count rows only)';
9 dbms_output.put_line(l_str);
10 -- execute immediate l_str;
11 dbms_output.put_line(sql%rowcount);
12 END;
13 /
Procedure created.
SQL> exec delete_rows('test', 2);
delete fromtest where rowid in (select rowid from table_name fetch first
Row_count rows only)
PL/SQL procedure successfully completed.
SQL>
What do you think, can you really run it? Sure you can, but it'll fail:
SQL> delete fromtest where rowid in (select rowid from table_name fetch first
2 Row_count rows only)
3 /
delete fromtest where rowid in (select rowid from table_name fetch first
*
ERROR at line 1:
ORA-00942: table or view does not exist
SQL>
Let's fix it. It is a good habit to precede parameters' names with a prefix so that you'd easily recognize them in the code; it also helps in where clause because - if you name the parameter the same as column, you'll get wrong result.
Also
, if you're worried about SQL injection, you could include call(s) to the `DBMS_ASSERT` package:
SQL> CREATE OR REPLACE PROCEDURE delete_rows
2 (par_table_name IN USER_TABLES.table_name%type,
3 par_row_count in number)
4 IS
5 l_str varchar2(200);
6 l_table_name user_tables.table_name%type;
7 BEGIN
8 l_table_name := dbms_assert.sql_object_name(par_table_name);
9
10 l_str := 'delete from ' || l_table_name ||' where rowid in ' ||
11 '(select rowid from ' || l_table_name ||
12 ' fetch first ' || par_row_count || ' rows only)';
13 dbms_output.put_line(l_str);
14 -- execute immediate l_str;
15 dbms_output.put_line(sql%rowcount);
16 END;
17 /
Procedure created.
OK; what's the new result:
SQL> exec delete_rows('test', 2);
delete from test where rowid in (select rowid from test fetch first 2 rows only)
PL/SQL procedure successfully completed.
Does this work? Yes, it does:
SQL> delete from test where rowid in (select rowid from test fetch first 2 rows only);
2 rows deleted.
SQL> rollback;
Rollback complete.
SQL>
OK; the final fix of the procedure: include execute immediate:
SQL> CREATE OR REPLACE PROCEDURE delete_rows
2 (par_table_name IN USER_TABLES.table_name%type,
3 par_row_count in number)
4 IS
5 l_str varchar2(200);
6 l_table_name user_tables.table_name%type;
7 BEGIN
8 l_table_name := dbms_assert.sql_object_name(par_table_name);
9
10 l_str := 'delete from ' || l_table_name ||' where rowid in ' ||
11 '(select rowid from ' || l_table_name ||
12 ' fetch first ' || par_row_count || ' rows only)';
13 -- dbms_output.put_line(l_str);
14 execute immediate l_str;
15 dbms_output.put_line('Deleted ' || sql%rowcount || ' row(s)');
16 END;
17 /
Procedure created.
SQL> exec delete_rows('test', 2);
Deleted 2 row(s)
PL/SQL procedure successfully completed.
SQL>

There are 2 mistakes.
You are missing a space after from.
row_count must be passed as parameter.
create or replace procedure delete_rows (table_name IN USER_TABLES.table_name%type, row_count in number)
IS
BEGIN
execute immediate 'delete from '||table_name||' where rowid in (select rowid from ' ||table_name||' fetch first '|| Row_count || ' rows only)';
dbms_output.put_line(sql%rowcount);
END;
/

Related

Filter column value while using ALL_TAB_COLUMNS

In SQL Oracle, is there a way to filter an ALL_TAB_COLUMNS SELECT statement, by the values in a specific column?
Yes; you'll need PL/SQL with dynamic SQL to do that.
Here's an example I use to search through current user's tables, check the ones that contain ENAME column which contains SCOTT string within. The result says that two tables (EMPLOYEE and EMP) contain one row with such a value.
Adjust it to your needs.
SQL> SET SERVEROUTPUT ON
SQL> DECLARE
2 l_str VARCHAR2(500);
3 l_cnt NUMBER := 0;
4 BEGIN
5 FOR cur_r IN (SELECT u.table_name, u.column_name
6 FROM user_tab_columns u, user_tables t
7 WHERE u.table_name = t.table_name
8 AND u.column_name = 'ENAME'
9 )
10 LOOP
11 l_str := 'SELECT COUNT(*) FROM ' || cur_r.table_name ||
12 ' WHERE ' || cur_r.column_name || ' like (''%SCOTT%'')';
13
14 EXECUTE IMMEDIATE (l_str) INTO l_cnt;
15
16 IF l_cnt > 0 THEN
17 dbms_output.put_line(l_cnt ||' : ' || cur_r.table_name);
18 END IF;
19 END LOOP;
20 END;
21 /
1 : EMPLOYEE
1 : EMP
PL/SQL procedure successfully completed.
SQL>

Need to iterate the result and then execute immediate each result using pl/sql

So I have this query:
select 'alter index '||a.index_owner||'.'||a.index_name|| ' rebuild partition '||a.partition_name
from dba_ind_partitions a
where a.index_name in ('IDX_PI_T_BSCS_CONTRACT_HISTOR2', 'IDX_PI_T_BSCS_CONTRACT_HISTOR3', 'IDX_PI_T_BSCS_RATEPLAN_HIST_C1')
and a.status = 'UNUSABLE'
And I need to iterate over the result and then execute immediate each statement.
Basically, you'd convert SELECT you wrote into a cursor FOR loop and execute it.
Here's how; I can't test it, but - if SELECT returns what you want, that's the way to do it.
SQL> show user
USER is "SYS"
SQL> BEGIN
2 FOR cur_r
3 IN (SELECT 'alter index '
4 || a.index_owner
5 || '.'
6 || a.index_name
7 || ' rebuild partition '
8 || a.partition_name
9 as l_alter
10 FROM dba_ind_partitions a
11 WHERE a.index_name IN ('IDX_PI_T_BSCS_CONTRACT_HISTOR2',
12 'IDX_PI_T_BSCS_CONTRACT_HISTOR3',
13 'IDX_PI_T_BSCS_RATEPLAN_HIST_C1')
14 AND a.status = 'UNUSABLE')
15 LOOP
16 EXECUTE IMMEDIATE cur_r.l_alter;
17 END LOOP;
18 END;
19 /
PL/SQL procedure successfully completed.
SQL>

Getting ORA-00904 invalid identifier with pl/sql loop

When attempting to use a list of tables as segment names for a query against dba_segments I get an ORA-00904: invalid identifier error.
I've tried moving around various quotes in case it's a syntax error, but I'm not sure what the issue might be.
declare
v_sql_c1 varchar2 (1000);
V_dblink varchar2(100) := 'DB1';
begin
for c1 in (select * from TABLE_LIST)
loop
execute immediate' select /*+parallel*/ bytes from dba_extents '|| '#' ||V_dblink ||' a '
||' where segment_name ='||
c1.table_name
into v_sql_c1;
dbms_output.put_line(v_sql_c1);
end loop;
end;
/
I would like ideally for this to report a value for 'bytes' in every row in the table_name column of table_list, which is the same as the segment_name column of dba_segments.
Can anyone help?
This is what you currently have:
SQL> CREATE TABLE table_list (table_name VARCHAR2 (20));
Table created.
SQL> INSERT INTO table_list VALUES ('EMP');
1 row created.
SQL> set serveroutput on;
SQL> DECLARE
2 v_sql_c1 VARCHAR2 (1000);
3 V_dblink VARCHAR2 (100) := 'DB1';
4 v_sql VARCHAR2 (1000);
5 BEGIN
6 FOR c1 IN (SELECT * FROM TABLE_LIST)
7 LOOP
8 v_sql :=
9 ' select /*+parallel*/ bytes from dba_extents '
10 || '#'
11 || V_dblink
12 || ' a '
13 || ' where segment_name ='
14 || c1.table_name;
15
16 DBMS_OUTPUT.put_line (v_sql);
17
18 -- EXECUTE IMMEDIATE v_sql INTO v_sql_c1;
19
20 DBMS_OUTPUT.put_line (v_sql_c1);
21 END LOOP;
22 END;
23 /
select /*+parallel*/ bytes from dba_extents #DB1 a where segment_name =EMP
PL/SQL procedure successfully completed.
SQL>
See? An invalid SELECT statement.
But, if you
remove space in front of a database link name
apply single quotes to segment_name
you'll get something that might work:
SQL> DECLARE
2 v_sql_c1 VARCHAR2 (1000);
3 V_dblink VARCHAR2 (100) := 'DB1';
4 v_sql VARCHAR2 (1000);
5 BEGIN
6 FOR c1 IN (SELECT * FROM TABLE_LIST)
7 LOOP
8 v_sql :=
9 ' select /*+parallel*/ bytes from dba_extents'
10 || '#'
11 || V_dblink
12 || ' a '
13 || ' where segment_name ='
14 || CHR (39)
15 || c1.table_name
16 || CHR (39);
17
18 DBMS_OUTPUT.put_line (v_sql);
19
20 -- EXECUTE IMMEDIATE v_sql INTO v_sql_c1;
21
22 DBMS_OUTPUT.put_line (v_sql_c1);
23 END LOOP;
24 END;
25 /
select /*+parallel*/ bytes from dba_extents#DB1 a where segment_name ='EMP'
PL/SQL procedure successfully completed.
SQL>
Basically, you should always display statement you'll be running as a dynamic SQL, make sure it is correct, and then actually EXECUTE IMMEDIATE it.

Display result from loop tables (oracle, pl/sql)

I'm try to loop some tables and run select as below:
set serveroutput on
declare
type tables_names is table of varchar2(30);
type selectTable is table of varchar2(30);
tName tables_names;
sTableName selectTable;
begin;
tName := tables_names('PERIOD','SETTING','RAP','LOG');
sTableName := selectTable('m_table1','m_table2','m_table3','m_table4','m_table5');
for i in 1..tName.count loop
for j in 1..sTableName.count loop
select col10, count(*) from user.sTableName(j)
where table_name = tName(i) group by col10;
end loop;
end loop;
end;
I got error:PL/SQL: ORA-00933.
Can you please tell me how can I correctly run PL/SQL procedure to have displayed result from my select?
UPDATE: looking result
Normally, to get this I need to run below select's:
select column_name,
count(*) as countColumn
from user.m_table1 where table_name = 'PERIOD' group by column_name;
select column_name,
count(*) as countColumn
from user.m_table2 where table_name = 'PERIOD' group by column_name;
Oracle complains (ORA-00933) that command isn't properly ended. That's probably because of a semi-colon behind the BEGIN; also, you lack the INTO clause.
I'm not sure what PERIOD, SETTING, ... are opposed to m_table1, m_table2, ... Which ones of those are table names? What are those other values, then?
Anyway: here's an example which shows how to do something like that - counting rows from tables. Try to adjust it to your situation, or - possibly - add some more info so that we'd know what you are doing.
SQL> set serveroutput on
SQL> declare
2 tname sys.odcivarchar2list := sys.odcivarchar2list();
3 l_cnt number;
4 l_str varchar2(200);
5 begin
6 tname := sys.odcivarchar2list('EMP', 'DEPT');
7
8 for i in 1 .. tname.count loop
9 l_str := 'select count(*) from ' || tname(i);
10 execute immediate l_str into l_cnt;
11 dbms_output.put_line(tname(i) ||': '|| l_cnt);
12 end loop;
13 end;
14 /
EMP: 14
DEPT: 4
PL/SQL procedure successfully completed.
SQL>
[EDIT: added GROUP BY option]
Here you go; as EMP and DEPT share the DEPTNO column, I chose it for a GROUP BY column.
SQL> declare
2 tname sys.odcivarchar2list := sys.odcivarchar2list();
3 type t_job is record (deptno varchar2(20), cnt number);
4 type t_tjob is table of t_job;
5 l_tjob t_tjob := t_tjob();
6 l_str varchar2(200);
7 begin
8 tname := sys.odcivarchar2list('EMP', 'DEPT');
9
10 for i in 1 .. tname.count loop
11 l_str := 'select deptno, count(*) from ' || tname(i) ||' group by deptno';
12 execute immediate l_str bulk collect into l_tjob;
13
14 for j in l_tjob.first .. l_tjob.last loop
15 dbms_output.put_Line('Table ' || tname(i) || ': Deptno ' || l_tjob(j).deptno||
16 ': number of rows = '|| l_tjob(j).cnt);
17 end loop;
18
19 end loop;
20 end;
21 /
Table EMP: Deptno 30: number of rows = 6
Table EMP: Deptno 20: number of rows = 5
Table EMP: Deptno 10: number of rows = 3
Table DEPT: Deptno 10: number of rows = 1
Table DEPT: Deptno 20: number of rows = 1
Table DEPT: Deptno 30: number of rows = 1
Table DEPT: Deptno 40: number of rows = 1
PL/SQL procedure successfully completed.
SQL>
You are probably looking for something like this. Note that you can't run a simple select statement inside a PL/SQL without INTO clause. use a refcursor and DBMS_SQL.RETURN_RESULT
DECLARE
TYPE tables_names IS TABLE OF VARCHAR2 (30);
TYPE selectTable IS TABLE OF VARCHAR2 (30);
tName tables_names;
sTableName selectTable;
rc SYS_REFCURSOR;
BEGIN
tName :=
tables_names ('PERIOD',
'SETTING',
'RAP',
'LOG');
sTableName :=
selectTable ('m_table1',
'm_table2',
'm_table3',
'm_table4',
'm_table5');
FOR i IN 1 .. tName.COUNT
LOOP
FOR j IN 1 .. sTableName.COUNT
LOOP
OPEN rc FOR
'select col10, count(*) from '||USER||'.'
|| sTableName (j)
|| ' where table_name = '''
|| tName (i)
|| ''' group by col10';
DBMS_SQL.RETURN_RESULT (rc);
END LOOP;
END LOOP;
END;
/

Dynamic sql/query: apostrophe in SELECT

So I have the problem, that there is a variable, which could contains different column names
and then in the SELECT I want to compare the column with a specific word.
But then it seems like the apostrophe make problems:
query := 'SELECT value FROM table WHERE ' || variable || ' like ''word''';
EXECUTE IMMEDIATE query INTO rec;
EXCEPTION WHEN OTHERS THEN htp.p(dbms_utility.format_error_stack);
SQL> set serveroutput on;
SQL> DECLARE
2 VAR VARCHAR2(20);
3 REC NUMBER;
4 query VARCHAR2(1000);
5 BEGIN
6 var := 'TABLE_NAME';
7 QUERY := 'SELECT count(*) FROM USER_TABLES WHERE ' || VAR || ' like ''%EMP%''';
8 dbms_output.put_line(query);
9 EXECUTE IMMEDIATE QUERY INTO REC;
10 dbms_output.put_line(rec);
11 END;
12 /
SELECT count(*) FROM USER_TABLES WHERE TABLE_NAME like '%EMP%'
1
PL/SQL procedure successfully completed.
With REC as collection type :
SQL> DECLARE
2 var VARCHAR2(20);
3 TYPE rec_typ
4 IS TABLE OF user_tables%ROWTYPE;
5 rec REC_TYP;
6 query VARCHAR2(1000);
7 BEGIN
8 var := 'TABLE_NAME';
9
10 query := 'SELECT * FROM USER_TABLES WHERE '
11 || var
12 || ' like ''%EMP%''';
13
14 dbms_output.Put_line(query);
15
16 EXECUTE IMMEDIATE query bulk collect INTO rec;
17
18 FOR i IN 1..rec.count LOOP
19 dbms_output.Put_line(Rec(i).table_name);
20 END LOOP;
21 END;
22 /
SELECT * FROM USER_TABLES WHERE TABLE_NAME like '%EMP%'
EMP
PL/SQL procedure successfully completed.
If you're searching for values in column that are equal to word then change like to =.
If you're searching for values in column that contains word then change like ''word'' to like ''%word%'' (don't forget about the third apostrophe that's closing dynamic query).