Insert into by chunks - oracle

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;

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,

ORA-00904 - Invalid Identifier in Dynamic SQL

I'm trying to get to the bottom of ORA-00904 - Invalid Identifier error that I'm getting in my stored procedure.
Here is the code:
CREATE OR REPLACE PROCEDURE CDG4_HIER_GET_SUBTREE(pDimensionId IN VARCHAR2, pPeriodId IN NUMBER, pNodeId IN NUMBER, pMode IN CHAR, rCursor OUT SYS_REFCURSOR) IS
v_table_name VARCHAR2(30);
v_function_name VARCHAR2(30) := 'CDG4_HIER_MGR_HAS_CHILDREN'; -- some function defined in the same package
InvalidMode EXCEPTION;
---
v_sql VARCHAR2(2000);
BEGIN
-- Get dynamic table name
v_sql := 'SELECT UPPER(TABLE_ID)'
||'FROM CDG4_CFG_MAP_GER '
||'WHERE UPPER(DIMENSION_ID) = UPPER(''' || pDimensionId || ''') ';
EXECUTE IMMEDIATE v_sql INTO v_table_name;
IF pMode = 'F' THEN
-- Do something
ELSIF pMode = 'S' THEN
v_sql := 'SELECT A.PERIODO_K, '
||' A.FIGLIO_K, '
||' A.PADRE_K, '
||' A.F_LAYOUT, '
||' A.F_VISUALIZZA, '
||' DECODE(A.PADRE_K, NULL, NULL, B2.CODICE) AS CODICE_PADRE, '
||' A.F_ORDINE, '
||' DECODE(A.GEN1, ''2'', ''I'', A.GEN1) AS GEN1, '
||' A.GEN2, '
||' A.GEN3, '
||' ''[''|| B1.CODICE || ''] - '' || B1.DESCR AS DESCR, '
||' ''[''|| B1.CODICE || ''] - '' || B1.DESCR AS DESCRIZIONE, '
||' ' || v_function_name || '(''' || pDimensionId || ''', ' || pPeriodId || ', A.FIGLIO_K) AS HAS_CHILDREN '
||'FROM DM_GERARCHIE A, DIM_BO_CANALE_PRIMARIA B1, DIM_BO_CANALE_PRIMARIA B2 '
||'WHERE A.FIGLIO_K = B1.CODICE_K '
||'AND A.PADRE_K = B2.CODICE_K '
||'AND UPPER(A.DIMENSION_ID) = UPPER(''' || pDimensionId || ''') '
||'AND UPPER(B1.DIMENSION_ID) = UPPER(''' || pDimensionId || ''') '
||'AND UPPER(B2.DIMENSION_ID) = UPPER(''' || pDimensionId || ''') '
||'AND A.PERIODO_K = ' || pPeriodId
||' AND A.FIGLIO_K = ' || pNodeId;
ELSE
RAISE InvalidMode;
END IF;
OPEN rCursor FOR v_sql;
EXCEPTION
WHEN InvalidMode THEN
RAISE_APPLICATION_ERROR(-20005, 'Invalid selection mode.');
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(-20004, 'Generic error - '||SQLCODE||' -ERROR- '||SQLERRM);
END;
Oracle seems not to be able to identify the function v_function_name (it is in the same package) as it keeps returning ORA-00904 while opening the cursor rCursor.
Any hint to a plausible clause of this behaviour?
Thanks in advance.
Oracle seems not to be able to identify the function v_function_name (it is in the same package
Dynamic SQL is executed as SQL but outside of the scope of the package. That means we need to build a valid SQL statement. If you were running a similar static SELECT statement you would have written package_name.v_function_name because your SQL would execute outside the package. And that's what you need to do here.
Although now I've looked more closely at your code and I don't understand why you are using Dynamic SQL at all. You could execute your statements as static SQL with bind variables and parameters.
OPEN rCursor FOR
SELECT A.PERIODO_K,
A.FIGLIO_K,
A.PADRE_K,
A.F_LAYOUT,
A.F_VISUALIZZA,
DECODE(A.PADRE_K, NULL, NULL, B2.CODICE) AS CODICE_PADRE,
A.F_ORDINE,
DECODE(A.GEN1, ''2'', ''I'', A.GEN1) AS GEN1,
A.GEN2,
A.GEN3,
'[' || B1.CODICE || '] - ' || B1.DESCR AS DESCR,
'[' || B1.CODICE || '] - ' || B1.DESCR AS DESCRIZIONE,
package_name.v_function_name (pDimensionId ,pPeriodId , A.FIGLIO_K) AS HAS_CHILDREN
FROM DM_GERARCHIE A, DIM_BO_CANALE_PRIMARIA B1, DIM_BO_CANALE_PRIMARIA B2
WHERE A.FIGLIO_K = B1.CODICE_K
AND A.PADRE_K = B2.CODICE_K
AND UPPER(A.DIMENSION_ID) = UPPER( pDimensionId )
AND UPPER(B1.DIMENSION_ID) = UPPER( pDimensionId )
AND UPPER(B2.DIMENSION_ID) = UPPER( pDimensionId )
AND A.PERIODO_K = pPeriodId
AND A.FIGLIO_K = pNodeId;

Oracle : Delete rows with "execute immediate" and rowcount

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;

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