Automatically setting Oracle's sequence start value - oracle

I have many existing tables, each with a column called 'id'. This column has an integer value starting at 1. So for example, the table MY_TABLE contains 3 records with ids 1, 2 and 3 (super basic).
I want to create a sequence for each table I have and set its start value with the maximun id of the table. In my example, I would need something like this:
CREATE SEQUENCE MY_TABLE_SEQ START WITH 3 INCREMENT BY 1;
I tried something like this, but it didn't work:
CREATE SEQUENCE MY_TABLE_SEQ START WITH (SELECT NVL(MAX(id),1) FROM MY_TABLE) INCREMENT BY 1;
Any idea what I might be able to do?
Thanks

DECLARE
MAXVAL MY_TABLE.ID%TYPE;
BEGIN
SELECT NVL(MAX(id),1) INTO MAXVAL FROM MY_TABLE;
EXECUTE IMMEDIATE 'CREATE SEQUENCE MY_TABLE_SEQ START WITH ' || MAXVAL || ' INCREMENT BY 1';
END
/
You could also ALTER the sequences once they are created.
Some readings about the subject: http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:951269671592

You could generate the CREATE SEQUENCE commands:
CREATE TABLE test_tab1 (
id number
);
CREATE TABLE test_tab2 (
id number
);
INSERT INTO test_tab1 VALUES (20);
INSERT INTO test_tab2 VALUES (40);
COMMIT;
DECLARE
v_max_id NUMBER;
BEGIN
FOR v_table IN (SELECT table_name
FROM user_tables
WHERE table_name IN ('TEST_TAB1', 'TEST_TAB2'))
LOOP
EXECUTE IMMEDIATE 'SELECT NVL(MAX(id), 1) FROM ' || v_table.table_name
INTO v_max_id;
dbms_output.put_line(
'CREATE SEQUENCE ' || upper(v_table.table_name) || '_SEQ START WITH ' || v_max_id || ' INCREMENT BY 1;');
END LOOP;
END;
Output:
CREATE SEQUENCE TEST_TAB1_SEQ START WITH 20 INCREMENT BY 1;
CREATE SEQUENCE TEST_TAB2_SEQ START WITH 40 INCREMENT BY 1;

Related

create pl/sql trigger insert squence on all tables

i want to create a trigger insert sequence i created to apply on all primary key on tables ,
CREATE SEQUENCE HR.PRIMARY_KEY
START WITH 300
INCREMENT BY 10
MAXVALUE 99990
MINVALUE 1
NOCYCLE
NOCACHE
NOORDER;
create or replace trigger increment_pk_trigger
before insert
ON schema
--FOR EACH ROW
DECLARE
CURSOR get_pk_CURSOR IS
select a.constraint_name, b.column_name, a.table_name
from user_constraints a, user_cons_columns b
where a.constraint_type = 'P' and a.constraint_name = b.constraint_name;
BEGIN
FOR V_RECORD IN get_pk_CURSOR LOOP
EXECUTE IMMEDIATE 'insert into '||V_RECORD.TABLE_NAME||' :new.V_RECORD.column_name := primary_key.nextva ';
END LOOP;
END;
my problem i cannot get what will table_name will be i tried to make it on database|schema
but not work
This is not a requirement that makes sense so you realistically can't.
It would be very weird to have a single sequence as the source for the primary key on every table-- there would end up being quite a bit of contention on that single sequence. It would be much more normal to create one sequence per table or (assuming a recent version of Oracle) to simply declare the primary key as an identity column.
If you really wanted to, you could write some code that dynamically generated triggers for every table in the schema. Something like this (assuming that every table has a single column primary key and that you really want to use the same sequence to generate the primary key on every table despite the performance impact)
begin
for pk in (select a.constraint_name, b.column_name, a.table_name
from user_constraints a, user_cons_columns b
where a.constraint_type = 'P'
and a.constraint_name = b.constraint_name)
loop
execute immediate 'create or replace trigger trg_pk_' || pk.table_name ||
' before insert on ' || pk.table_name ||
' for each row ' ||
'begin ' ||
' :new.' || pk.column_name || ' := primary_key.nextval; ' ||
'end; ';
end loop
end;

Dynamically create tables based on the total counts of another table

I want to be able to create tables based on the total rows in another table.
Let's say I've a table A, and the count is 500K, and so I wanna be able to create 5 tables each of 100K dynamically inside a procedure in oracle.
table A will have counts changing each time, but I would still want to be able to create tables with 100K max, for instance, tomorrow the table A has 550K, I want to be able to create 6 tables with 5 tables having 100k and last one with 50k.
SET SERVEROUTPUT ON
DECLARE
ttl_tables NUMBER;
var_loop NUMBER := 0;
BEGIN
SELECT CAST(COUNT(*)/(500000) AS INT) INTO ttl_tables FROM
px_extract_checks;
DBMS_OUTPUT.PUT_LINE(ttl_tables);
LOOP
var_loop := var_loop + 1;
EXECUTE IMMEDIATE (' || CREATE TABLE ' || 'table' || var_loop || ' ' ||
(Card_Number) || ');
EXIT WHEN var_loop = ttl_tables;
END LOOP;
END;
Something like above.
I have created a small block to help you out using dynamic queries.
You need to use actual column names and little bit changes in the following code as per your requirement:
See inline comments for more information on steps.
BEGIN
-- BEFORE CREATING THE TABLES, NEED TO DROP THE TABLES
FOR T IN (
SELECT
TABLE_NAME
FROM
USER_TABLES
WHERE
TABLE_NAME LIKE 'MY_TABLES_DYNAMIC%'
) LOOP
EXECUTE IMMEDIATE 'DROP TABLE ' || T.TABLE_NAME;
END LOOP;
-- CREATING THE TABLES USING TABLE_A
FOR I IN (
SELECT
CEIL(COUNT(1) / 100000) AS CNT -- DIVIDED THE COUNT OF ROWS BY 100K
FROM
TABLE_A
) LOOP
-- USING CTAS
EXECUTE IMMEDIATE 'CREATE TABLE MY_TABLES_DYNAMIC_'
|| I.CNT
|| ' AS '
|| ' SELECT COL1, COL2, ... FROM'
|| ' (SELECT A.COL1, A.COL2, ...., ROW_NUMBER() OVER (ORDER BY A.PRIMARY_KEY_OF_TABLE_A) RN '
|| ' FROM TABLE_A A)'
|| ' WHERE RN BETWEEN '
|| ( ( I.CNT - 1 ) * 100000 ) + 1
|| ' AND '
|| ( I.CNT * 100000 );
END LOOP;
END;
Cheers!!
Hope this below code will help you for your requirement, you can test it by changing the column names,table names and counts accordingly:
declare
tname_count number:=0;
t_name varchar2(500);
begin
select count(1) into t_count from A;
for k in 1.. t_count
loop
if mod(k,100000)=0 then
tname_count:=tname_count+1;
t_name:='TAB_'||tname_count;
execute immediate 'create table '|| t_name||' (id varchar2(50))';
end if;
end loop;
if floor(t_count/100000)<>0 then
tname_count:=tname_count+1;
t_name:='TAB_'||tname_count;
execute immediate 'create table '|| t_name||' (id varchar2(50))';
end if;
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!!

Alter Table Add Column in PL SQL loop

I am trying to insert columns in a table using a for loop that iterates over a cursor. The code is:
declare
cursor Months_ET is
SELECT distinct to_char(FEE_CD_ACT_SUM_ACCTG_DA, 'MON-YY') as "Month_U"
FROM EXPORT_TABLE
WHERE EXPORT_TABLE.FEE_CD_ACT_SUM_ACCTG_DA >= to_date('10/01/2013','mm/dd/yyyy')
AND EXPORT_TABLE.FEE_CD_ACT_SUM_ACCTG_DA < to_date('10/01/2014', 'mm/dd/yyyy');
n integer := 1;
begin
for mon in Months_ET loop
dbms_output.put_line(mon."Month_U");
execute immediate 'Alter table "Fee_CT" add('|| mon."Month_U" ||' varchar(20))';
n := n +1;
end loop;
end;
The cursor in the beginning jsut gets a list of unique month names which the dbms_output.put_line prints out as:
SEP-14
AUG-14
JUL-14
So I know the variable is not empty.
So using those results I want to add three columns for each month- yr. However I get an invalid datatype error. I have also tried altering to the for loop to concatenate the table name outside of the quotes like this:
for mon in Months_ET loop
--Month_List(n) := mon."Month_U";
dbms_output.put_line(mon."Month_U");
execute immediate 'Alter table' ||"Fee_CT" || 'add('|| mon."Month_U" ||' varchar(20))';
n := n +1;
But I get a message that "Table,View Or Sequence reference 'Fee_CT' not allowed in this context." Not sure what I am doing wrong. The actual data is much larger and covers a wider time frame so using multiple alter table statements isn't realistic. plus the underlying data will be changing, so I need to be able to change the column names with the underlying data.
Your table name and column names use non-standard characters - lower case letters, dashes. This is a really bad idea, because it means having to wrap every reference in double-quotes. Every person who has to use your schema will curse you whenever they have to fix a PLS-00357, ORA-00903 or ORA-00904 exception because they forgot to double-quote an identifier. Look, it's even caught you out :)
But if you really want to persist, the statement you need is:
execute immediate 'Alter table "Fee_CT" add("'|| mon."Month_U" ||"' varchar(20))';
The table name is part of the boilerplate text not a variable. You need to wrap the non-standard column name in double-quotes. Make sure the boilerplate has spaces around the key-words.
Above all, remember that a syntax error in dynamic SQL throws a runtime error, not a compilation error. Use logging or DBMS_OUTPUT to review the assembled statements.
Not sure why you want to create columns dynamically.But it is possible though:
Errors :
1.Column names can only have '_'(underscore),no other special character. ie.,AUG-15 --> AUG_15
When using Alias names for further processing use SUBQUERY (Month_U )
Quotes should be properly used.
space between keywords/variable in execute statement.
create table Table_A
(id integer,
date1 date
);
-- Table created.
begin
insert into table_A values (1,trunc(sysdate) );
insert into table_A values (2,trunc(sysdate+100) );
end;
select to_char(date1, 'MON-YY') as "Month_U" from table_A;
--AUG-15
--DEC-15
Declare
cursor months_ET is select month_u from
( select to_char(date1, 'MON_YY') AS Month_U from table_A) DUAL;
sql_stmnt varchar2(400) ;
table_name varchar2(20) := 'Table_A';
column_name varchar2(20) := 'New_col1';
data_type varchar2(20) := 'date' ; -- you can change to varchar2
Begin
FOR MON in months_ET
LOOP
sql_stmnt := ' alter table ' || table_name || ' add( ' || MON.MONTH_U
|| ' ' || data_type || ' ) ' ;
dbms_output.put_line(sql_stmnt );
Execute immediate sql_stmnt ;
END LOOP;
End;
OUTPUT:
alter table Table_A add( AUG_15 date )
alter table Table_A add( DEC_15 date )
Table altered.
Always use a DBMS_OUTPUT.PUT_LINE to test your execute immediate statement.
Give space between keywords/variables.
Use single quotes
Now Check this Example:
create table Table_A(id integer);
-- Table created.
Declare
sql_stmnt varchar2(400) ;
table_name varchar2(20) := 'Table_A';
column_name varchar2(20) := 'New_col1';
data_type varchar2(20) := 'varchar2(20)' ;
Begin
sql_stmnt := ' alter table ' || table_name || ' add( ' || Column_name || ' ' || data_type || ' ) ' ;
execute immediate sql_stmnt ;
dbms_output.put_line(sql_stmnt );
End;
-- alter table Table_A add( New_col1 varchar2(20) )
-- Table altered.
Desc Table_A;
Column Data Type Length Precision Scale
ID NUMBER 22 - 0 - - -
NEW_COL1 VARCHAR2 20 - - - - -

How to change the Oracle Sequence using loop?

Hope someone can help. When I tried to insert something into a table it give me error saying the primary key is already existed. So I need to reset my sequence so that it is always max(id)+1.
The table is called 'People' with 2 columns (ID, Name). The sequence is called SEQ.
I am thinking of doing a loop. To run select SEQ.nextval from dual for n times. this n= max(id)-SEQ.currval
Wwill this work? and how Can I put it into the syntax?
Thanks a lot.
declare
l_MaxVal pls_integer;
l_Currval pls_integer default - 1;
begin
select max(id)
into l_MaxVal
from people;
while l_Currval < l_Maxval
loop
select my_seq.nextval
into l_Currval
from dual;
end loop;
end;
If this is a one off, you can use the alter sequence
alter sequence sequenceName increment by val ;
whereas val is +1 to the maximum
then call get nextVal, then set the increment back to 1.
I threw the below together to show you how it can be done without looping.
create sequence changeValue start with 18 increment by 1 nocache ;
select changeValue.nextval from dual ;
/
NEXTVAL
----------------------
18
set serveroutput on
declare
maxVal number := 24 ;
curVal number ;
diffVal number ;
incrementVal number ;
procedure alterSequence(seqName in varchar2, incVal in number) as
s varchar2(500);
begin
s := 'alter sequence ' || seqName || ' increment by ' || incVal ;
dbms_output.put_line(s);
execute immediate s;
end alterSequence;
begin
--(done in 11gr2 so if in earlier version select into)
curVal := changeValue.currval ;
dbms_output.put_line('curValue=>' || curVal );
diffVal := maxVal - curVal ;
dbms_output.put_line('diffVal=>' || diffVal );
alterSequence ( 'changeValue' , diffVal + 1 );
incrementVal := changeValue.nextval ;
dbms_output.put_line('incrementVal=>' || incrementVal );
alterSequence ( 'changeValue' , 1 );
curVal := changeValue.currval ;
dbms_output.put_line('curValue=>' || curVal );
end ;
/
curValue=>18
diffVal=>6
alter sequence changeValue increment by 7
incrementVal=>25
alter sequence changeValue increment by 1
curValue=>25
or better yet, as #Dave suggests, just drop and recreate the sequence with the acceptable Start With value.
With this one you can synchronize the sequence whatever it is forward or behind the max of the ID.
Just need to change the parameters in the final of the code.
declare
procedure SYNC_SEQUENCE
( P_IN_SEQ in varchar2
, P_IN_TABLE in varchar2
, P_IN_ID in varchar2
)
is
LV_MAXVAL number := 0;
LV_CURRVAL number := -1;
LV_AUX NUMBER;
begin
execute immediate
'select max('||P_IN_ID||')
from '||P_IN_TABLE into LV_MAXVAL;
execute immediate
'select '||P_IN_SEQ||'.nextval
from dual ' into LV_CURRVAL;
if LV_MAXVAL < LV_CURRVAL then
LV_AUX := (LV_CURRVAL - LV_MAXVAL);
execute immediate
'ALTER SEQUENCE '||P_IN_SEQ||' INCREMENT BY -'||LV_AUX;
execute immediate
'SELECT '||P_IN_SEQ||'.NEXTVAL FROM dual' INTO LV_AUX;
execute immediate
'ALTER SEQUENCE '||P_IN_SEQ||' INCREMENT BY 1';
end if;
while LV_CURRVAL < LV_MAXVAL
loop
execute immediate
'select '||P_IN_SEQ||'.nextval
from dual ' into LV_CURRVAL;
end loop;
end SYNC_SEQUENCE;
begin
SYNC_SEQUENCE('MY_SEQUENCIE_NAME','MY_TABLE_NAME','MY_FIELD_ID_NAME');
end;
/

Resources