Oracle : Delete rows with "execute immediate" and rowcount - oracle

I want to delete rows with an "Execute immediate" because the table name is in a variable.
How can I count the number of lines deleted?
I tried this, but it does not work with the INTO v_LINE_REMOVE;
v_sql := '
DELETE /*+parallel(t,4)*/
FROM "' || v_owner || '"."' || v_table_name ||'" t
where t."'|| v_column_name ||'" in (
select /*+parallel(rem,4)*/
rem.' || v_type_data || '
from ' || v_table_listeremove || ' rem
WHERE rem.dt_vact = '''|| v_dt_vact ||'''
)
';EXECUTE IMMEDIATE v_sql;--INTO v_LINE_REMOVE;
Thanks a lot

You should be able to use SQL%ROWCOUNT after running your DML statement
EXECUTE IMMEDIATE v_sql;
v_line_remove := SQL%ROWCOUNT;

Related

Best strategy to reset Oracle sequence monthly

I'm looking for the best strategy to reset a sequence which is used to generate unique keys. These keys are build using a prefix value, also generated by a combination of month and year values, like the following example:
2020060100000001 - Year(4d)Month(2d)Sequence(8d)
The sequence value must be restarted at the first second of each new month.
Is there any Oracle event that allows calling a function or procedure based in this situation to do this job?
Can anyone help me with opinions and experiences?
Thanks a lot!
Rodrigo
If you really want to set the value of a sequence you can use something like the following:
PROCEDURE SET_SEQUENCE(pinSequence_owner IN VARCHAR2,
pinSequence_name IN VARCHAR2,
pinNew_next_value IN NUMBER,
pinDebug IN BOOLEAN := FALSE)
IS
strSQL VARCHAR2(4000);
nNext_number NUMBER;
nOriginal_increment NUMBER;
nNew_nextval NUMBER;
nNew_last_number NUMBER;
BEGIN
strSQL := 'SELECT s.LAST_NUMBER, INCREMENT_BY ' ||
'FROM DBA_SEQUENCES s ' ||
'WHERE s.SEQUENCE_OWNER = ''' || pinSequence_owner || ''' AND ' ||
's.SEQUENCE_NAME = ''' || pinSequence_name || '''';
EXECUTE IMMEDIATE strSQL INTO nNext_number, nOriginal_increment;
-- Note that DBA_SEQUENCES.LAST_NUMBER represents the *next* number which will be
-- returned by a call to NEXTVAL.
IF pinNew_next_value NOT IN (nNext_number-1, nNext_number)
THEN
strSQL := 'ALTER SEQUENCE ' || pinSequence_owner || '.' || pinSequence_name ||
' INCREMENT BY ' || TO_CHAR(pinNew_next_value - nNext_number) || ' NOCACHE';
EXECUTE IMMEDIATE strSQL;
strSQL := 'SELECT ' || pinSequence_owner || '.' || pinSequence_name || '.NEXTVAL FROM DUAL';
EXECUTE IMMEDIATE strSQL INTO nNew_nextval;
strSQL := 'ALTER SEQUENCE ' || pinSequence_owner || '.' || pinSequence_name ||
' INCREMENT BY ' || nOriginal_increment || ' NOCACHE';
EXECUTE IMMEDIATE strSQL;
strSQL := 'SELECT s.LAST_NUMBER FROM DBA_SEQUENCES s WHERE s.SEQUENCE_OWNER = ''' || pinSequence_owner ||
''' AND s.SEQUENCE_NAME = ''' || pinSequence_name || '''';
EXECUTE IMMEDIATE strSQL INTO nNew_last_number;
END IF;
END SET_SEQUENCE;
Resetting sequence every month is not a valid approach. Basically you are violating the utilization of sequence. From your question i could understand, you wanted to append year and month to your column. In this case you can simple concatenate while inserting,
year||month||sequence_name.nextval
Eg: 2020||02||sequence_name.nextval
If you still wanted to reset, you can create a trigger to reset the sequence every month,

Searching for a particular value within an Oracle database

I am trying to search for a particular value within an Oracle database and the script is not working. Can this be done with the following code?
DECLARE
match_count integer;
v_search_string varchar2(4000) := 'QS/Operation';
BEGIN
FOR t IN (SELECT owner,
table_name,
column_name
FROM all_tab_columns
WHERE data_type in ('VARCHAR2') )
LOOP
BEGIN
EXECUTE IMMEDIATE
'SELECT COUNT(*) FROM '||t.owner || '.' || t.table_name||
' WHERE '||t.column_name||' = :1'
INTO match_count
USING v_search_string;
IF match_count > 0 THEN
dbms_output.put_line( t.owner || '.' || t.table_name ||' '||t.column_name||' '||match_count );
END IF;
EXCEPTION
WHEN others THEN
dbms_output.put_line( 'Error encountered trying to read ' ||
t.column_name || ' from ' ||
t.owner || '.' || t.table_name );
END;
END LOOP;
END;
The script is generating errors. I need to know how to get past the errors and find the value in the tables.
First, catching when others without re-raising is usually a bug; here it looks like you're intending to report errors but carry on, which doesn't seem unreasonable. But you should include the error in the information you display, e.g.:
EXCEPTION
WHEN others THEN
dbms_output.put_line( 'Error encountered trying to read ' ||
t.column_name || ' from ' ||
t.owner || '.' || t.table_name );
dbms_output.put_line(SQLERRM);
END;
Presumably that is telling that tables don't exist, or you have invalid identifiers, or you have privilege errors. You're searching all tables owned by everyone, and not even excluding those owned by SYS as the original code did - hopefully that isn't because you create your own tables under SYS - and it's possible somewhere in there you have tables with quoted identifiers. Actually, it's very likely; SYS has _default_auditing_options_, and system-generated nested table names can be mixed case, for instance.
To be on the safe side adding quotes won't hurt anyway:
BEGIN
EXECUTE IMMEDIATE
'SELECT COUNT(*) FROM "' || t.owner || '"."' || t.table_name || '"' ||
' WHERE "' || t.column_name || '" = :1'
INTO match_count
USING v_search_string;

Insert into by chunks

I am trying to use insert into statement, but getting the error:
ORA-01628: max # extents (32765) reached for rollback segment _SYSSMU134_1882489978$
Increasing the UNDO tablespace is not an option, so I would like a way to insert those data by chunks (for example with 1 million rows at a time). Can someone help to rewrite this procedure in that way?
CREATE OR REPLACE PROCEDURE create_chunks (
p_source_table IN VARCHAR2,
p_table_name_chunk IN VARCHAR2,
p_chunks IN VARCHAR2
) AS
v_insert_sql CLOB;
BEGIN
v_insert_sql := 'INSERT INTO ' || p_table_name_chunk ||
' (rid, chunk_number) ' ||
'SELECT /*+ parallel(64) */ rowid rid,' ||
'mod( ora_hash(rowid), :p_chunks ) as chunk_number '
'FROM ' || p_source_table;
EXECUTE IMMEDIATE v_insert_sql USING p_chunks;
COMMIT;
END;
This v_insert_sql is failing with above mentioned error. I have working solution using the cursor fetching like that:
DECLARE
CURSOR v_cur IS SELECT /*+ parallel(64) */
rowid rid, mod( ora_hash(rowid), 20000 ) AS chunk_number
-- I need this table to be parametric name
FROM some_table;
TYPE t_sample IS TABLE OF v_cur%ROWTYPE;
v_sample t_sample;
v_row_limit CONSTANT NUMBER := 1000000;
BEGIN
OPEN v_cur;
LOOP
FETCH v_cur BULK COLLECT INTO v_sample LIMIT v_row_limit;
FORALL i IN v_sample.first .. v_sample.last
INSERT INTO chunk_table VALUES v_sample(i);
COMMIT;
EXIT WHEN v_cur%NOTFOUND;
END LOOP;
CLOSE v_cur;
END;
I can't move this cursor straight into the procedure as the table name is varying and I need it to be parametric as with cursor approach I have to repeat the same code for different tables. So the question is how to deal with this?
Basically, I was able to just past the whole chunks approach inside procedure as dynamic query, like that:
CREATE OR REPLACE PROCEDURE create_chunks (
p_source_table IN VARCHAR2,
p_table_name_chunk IN VARCHAR2,
p_chunks IN VARCHAR2
) AS
v_insert_sql CLOB;
BEGIN
v_insert_sql := '' ||
' DECLARE ' || CHR(10) ||
' CURSOR cur1 IS SELECT ' || CHR(10) ||
' /*+ parallel(64) full(tbn)*/ rowid rid,' || CHR(10) ||
' mod( ora_hash(rowid), :p_chunks ) AS chunk_number' || CHR(10) ||
' FROM ' || p_source_table || ' tbn;' || CHR(10) ||
' TYPE t_sample IS TABLE OF cur1%ROWTYPE;' || CHR(10) ||
' v_sample t_sample;' || CHR(10) ||
' v_row_limit CONSTANT NUMBER := 1000000;' || CHR(10) ||
' BEGIN' || CHR(10) ||
' OPEN cur1;' || CHR(10) ||
' LOOP' || CHR(10) ||
' FETCH cur1 BULK COLLECT INTO v_sample LIMIT v_row_limit;' || CHR(10) ||
' FORALL i IN v_sample.first .. v_sample.last' || CHR(10) ||
' INSERT INTO ' || p_table_name_chunk || ' VALUES v_sample(i);' || CHR(10) ||
' COMMIT;' || CHR(10) ||
' EXIT WHEN cur1%NOTFOUND;' || CHR(10) ||
' END LOOP;' || CHR(10) ||
' CLOSE cur1;' || CHR(10) ||
' END;';
EXECUTE IMMEDIATE v_insert_sql USING p_chunks;
COMMIT;
END;

Gettig error PLS-00364

I'm trying to create a stored procedure where I'm passing select statement to for loop and i'm using dynamic table which is passing at runtime and getting below error:
LINE 23 PLS-00364: loop index variable 'I' use is invalid
LINE 19 PL/SQL: ORA-00942: table or view does not exist
CREATE OR REPLACE PROCEDURE CREATE_TEST(TBL_NM IN VARCHAR2)
IS
SRC_ID NUMBER(38);
SQL_Q VARCHAR2(250);
DEL_F VARCHAR2(250);
BEGIN
FOR I in (SELECT DEL_IND FROM TBL_NM)
LOOP
SRC_ID := SRC_FILE_ID_SEQ.NEXTVAL;
IF I.DEL_IND = 0
THEN
execute immediate 'INSERT INTO TEST_HIST ' || ' (a,b,c,d,e,DEL_IND) ' ||
' SELECT a,b,c,d,e, '|| 0 || ' || ' FROM ' || TBL_NM;
ELSIF I.DEL_IND = 1
THEN
execute immediate 'INSERT INTO JESTX_IGNR ' || ' (a,b,c,d,e,DEL_IND,SRC_ID_NO) ' ||
' SELECT a,b,c,d,e, '|| 2 ||' , '|| SRC_ID || ' FROM ' || TBL_NM;
END IF ;
END LOOP;
COMMIT;
END;
I call the procedure using:
EXEC CREATE_TEST('abc');
What you want is a REF CURSOR as you cannot use cursor for loop with dynamic sql.
I dont know the exact datatype for your column DEL_IND. Please declare accordingly.
Here is some more information on Oracle REF CURSORS.
Try below
CREATE OR REPLACE PROCEDURE CREATE_TEST(TBL_NM IN VARCHAR2)
IS
TYPE c1ref is REF CURSOR;
SRC_ID NUMBER(38);
SQL_Q VARCHAR2(250);
DEL_F VARCHAR2(250);
DEL_IND NUMBER(5);
BEGIN
vsql_text := 'select DEL_IND from ' || TBL_NM;
open c1ref for vsql_text;
LOOP
SRC_ID := SRC_FILE_ID_SEQ.NEXTVAL;
fetch c1ref into DEL_IND;
exit when c1ref%NOTFOUND;
IF (DEL_IND = 0)
THEN
execute immediate 'INSERT INTO TEST_HIST ' || ' (a,b,c,d,e,DEL_IND) ' ||
' SELECT a,b,c,d,e, '|| 0 || ' || FROM ' || TBL_NM;
ELSIF (DEL_IND = 1)
THEN
execute immediate 'INSERT INTO JESTX_IGNR ' || ' (a,b,c,d,e,DEL_IND) ' ||
' SELECT a,b,c,d,e, '|| 2 ||' , '|| SRC_ID || ' FROM ' || TBL_NM;
END IF ;
END LOOP;
CLOSE c1ref;
COMMIT;
END;

Execute Immediate in oracle

I have below query which gives an error as encounter an symbol ( in the line where loop is used. I am trying to develop a function which takes dynamic paramater as table_name,column_name,table_id and used for other tables as well.
FUNCTION get_encryp_pass( table_name IN varchar2,column_name IN varchar2,table_id IN varchar2) RETURN VARCHAR2
IS
BEGIN
EXECUTE IMMEDIATE 'for c1 in (select * from' || table_name ||) loop
EXECUTE IMMEDIATE 'update ' || table_name || ' set ' || column_name = encrypt_val(c1.column_name) || ' where ' || table_id || ' = ' || c1.table_id and column_name is not null;
end loop;
END get_encrypt_pass;
this should work:
CREATE PROCEDURE get_encryp_pass(table_name IN varchar2,
column_name IN varchar2,
table_id IN varchar2) IS
BEGIN
EXECUTE IMMEDIATE 'begin for c1 in (select * from ' || table_name ||
') loop update ' || table_name || ' set ' ||
column_name || ' = encrypt_val(c1.' || column_name ||
') where ' || table_id || ' = c1.'||table_id||' and ' || column_name ||
' is not null; end loop; end;'
;
END;
But why not simply call update FTP_SFTP_SERVER set PASSWORD=encrypt_val(PASSWORD) where PASSWORD is not null ?
keep care of what is a variable and what is a string-literal and must be single-quoted therefore ... and string-variables mus be double-quoted:
EXECUTE IMMEDIATE 'update ' || table_name || ' set ' || column_name || ' = ''' || encrypt_val(c1.column_name) || ''' where ' || table_id || ' = ' || c1.table_id || ' and column_name is not null';
Best practice is to concatenate the statement in a varchar2-variable first and inspect this. If the content of the variable is syntactical correct and executable, the EXECUTE IMMEDIATE should work as well
declare
stmt varchar2(4000);
begin
stmt := 'update ' || table_name || ' set ' || column_name || ' = ''' || encrypt_val(c1.column_name) || ''' where ' || table_id || ' = ' || c1.table_id || ' and column_name is not null';
EXECUTE IMMEDIATE stmt;
end;
I think i have one alternative for your question. MERGE can be used in this case. Please try it i dont have workspaceso dint test it but it should work Let me know if it helps.
FUNCTION get_encryp_pass(
table_name IN VARCHAR2,
column_name IN VARCHAR2,
table_id IN VARCHAR2)
RETURN VARCHAR2
IS
BEGIN
EXECUTE IMMEDIATE 'MERGE INTO '||table_name||' t1 USING
(
SELECT * FROM '||table_name|| ')t2
ON
(
t1.table_id = t2.table_id
)
WHEN MATCHED THEN
UPDATE SET t1.'||column_name||' = encrypt_val(t2.'||column_name||')'
||' WHERE and t1.'||column_name||' IS NOT NULL';
END;

Resources