Create sequence with sql result in it - sql

I have a sequence like this
begin
if :new."ID" is null then
select to_number(sys_guid(),'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX') into :new.id from dual;
end if;
is there a way to set the ID to be the next upcoming ID in the table?
For example:
if my current last row ID is 5
I want the new.id to be 6, so when the INSERT executes then it would have ID of 6

IDENTITY
column is now available on Oracle 12c:
create table t1 (
id NUMBER GENERATED ALWAYS as IDENTITY(START with 1 INCREMENT by 1),
info VARCHAR2(10)
);

Related

Oracle - Adding NOT NULL constraint to a currently nullable column

I have a handful of numeric fields that are currently nullable. I want to add a NOT NULL constraint to these fields and have the default value now set to 0.
Data gets pushed in to this table on a schedule. There is nothing being pushed into these fields, and they're nullable, so these certain fields are all being set to null. This is causing some trouble when we're trying to do math with these fields.
If I first run updates on these fields to set all of the current nulls to zeros, should I have any issues adding a NOT NULL with default 0 constraint on these currently nullable fields? Is there anything I should look out for?
Thanks
edit:
SET SERVEROUTPUT ON
DECLARE
P_PROJECT_NUM VARCHAR2(200);
P_FISCAL_YEAR VARCHAR2(200);
P_RESP_CENTER VARCHAR2(200);
FIELD1 INTEGER;
FIELD2 INTEGER;
FIELD3 INTEGER;
BEGIN
P_PROJECT_NUM := '123456';
P_FISCAL_YEAR := '2019_2020';
P_RESP_CENTER := '123A';
FIELD1 := 0;
FIELD2 := 0;
FIELD3 := 0;
FMS_EXTRACT.LOAD_TEMPLATE(
P_PROJECT_NUM => P_PROJECT_NUM,
P_FISCAL_YEAR => P_FISCAL_YEAR,
P_RESP_CENTER => P_RESP_CENTER,
FIELD1 => FIELD1,
FIELD2 => FIELD2,
FIELD3 => FIELD3
);
COMMIT;
You can do it like this
Add the default clause to the columns
Update the null values to 0
Add the constraint NOT NULL
My example:
SQL> create table t ( c1 number , c2 number );
Table created.
SQL> insert into t values ( 1 , null ) ;
1 row created.
SQL> insert into t values ( null , 1 ) ;
1 row created.
SQL> select * from t ;
C1 C2
---------- ----------
1
1
SQL> alter table t modify c1 number default 0 ;
Table altered.
SQL> alter table t modify c2 number default 0 ;
Table altered.
SQL> update t set c1 = 0 where c1 is null ;
1 row updated.
SQL> update t set c2 = 0 where c2 is null ;
1 row updated.
SQL> commit ;
Commit complete.
SQL> alter table t modify c1 number not null ;
Table altered.
SQL> alter table t modify c2 number not null ;
Table altered.
SQL> insert into t ( c1 ) values ( 2 ) ;
1 row created.
SQL> select * from t ;
C1 C2
---------- ----------
1 0
0 1
2 0
This is too long for a comment. I don't think you want a NOT NULL constraint. That would result in your inserts failing if any of the NOT NULL columns are NULL.
The simplest solution is to not change the data at all. After all, NULL appears to be a valid value, if that is how the data is coming in. Just use COALESCE() in your logic:
select coalesce(x, 0) + coalesce(y, 0)
You can encompass this logic in a view or in generated columns:
alter table t add x_notnull generated always as (coalesce(x, 0));
If you want to prevent rows coming in with NULL values, then you can filter them out in your data important process. You don't provide details, so I can't make any suggestions.
You can add a default value. For instance:
alter table modify y default 0;
Then subsequent inserts where the value is missing will be replaced by the default value. However, explicit NULL values will not be affected.
Finally, you can add a NOT NULL constraint -- after removing all current NULL values.
Here is a db<>fiddle showing some examples of the above.

how can i access a value that's being updated by a trigger?

here's the value of ACCOUNT_NUMBER that has been generated by a sequence and inserted in ACCOUNTS table by ACCOUNT_NUMBER_TRIG trigger that i need to insert it into the TRANSACTION TABLE by the trigger ACCOUNTS_TRANSCATION_TRIG_1
CREATE OR REPLACE TRIGGER ACCOUNT_NUMBER_TRIG
BEFORE INSERT
ON ACCOUNTS
FOR EACH ROW
WHEN (NEW.ACCOUNT_NUMBER is not null)
DECLARE
V_ACC_NO ACCOUNTS.ACCOUNT_NUMBER%TYPE;
BEGIN
SELECT ACCOUNT_NO_SEQ.nextvaL INTO V_ACC_NO FROM DUAL;
:NEW.ACCOUNT_NUMBER := V_ACC_NO;
END ACCOUNT_NUMBER_TRIG;
------------------------------------------------------------------------------
CREATE OR REPLACE TRIGGER ACCOUNTS_TRANSCATION_TRIG_1 AFTER
INSERT ON ACCOUNTS FOR EACH ROW DECLARE CURSOR ACCOUNTS_CUR IS
SELECT ACCOUNT_NUMBER FROM ACCOUNTS;
DECLARE
TEMP_1 NUMBER(5,0);
BEGIN
SELECT ACCOUNTS.ACCOUNT_NUMBER FROM INSERTED INTO TEMP_1
OPEN ACCOUNTS_CUR;
INSERT
INTO TRANSACTIONS VALUES
(
SYSDATE,
- :NEW.ACCOUNT_NUMBER,
'NEW ACCOUNT',
0
);
CLOSE ACCOUNTS_CUR;
END ACCOUNTS_TRANSCATION_TRIG_1;
CREATE TABLE accounts(
ACCOUNT_NUMBER number,
ACCOUNT_NAME varchar2(20)
);
CREATE SEQUENCE ACCOUNT_NO_SEQ;
CREATE OR REPLACE TRIGGER ACCOUNT_NUMBER_TRIG
BEFORE INSERT
ON ACCOUNTS
FOR EACH ROW
WHEN (NEW.ACCOUNT_NUMBER is not null)
BEGIN
:NEW.ACCOUNT_NUMBER :=ACCOUNT_NO_SEQ.nextvaL;
END ACCOUNT_NUMBER_TRIG;
/
CREATE TABLE transactions(
TR_DATE date,
TR_ACCOUNT_NUMBER number,
TR_TYPE varchar2(20),
TR_somenumber int
);
CREATE OR REPLACE TRIGGER ACCOUNTS_TRANSCATION_TRIG_1 AFTER
INSERT ON ACCOUNTS FOR EACH ROW
BEGIN
INSERT INTO TRANSACTIONS( TR_DATE, TR_ACCOUNT_NUMBER, TR_TYPE, TR_somenumber )
VALUES
(
SYSDATE,
:NEW.ACCOUNT_NUMBER,
'NEW ACCOUNT',
0
);
END ACCOUNTS_TRANSCATION_TRIG_1;
/
INSERT INTO accounts( ACCOUNT_NUMBER, ACCOUNT_NAME ) VALUES (1111,'My Name' );
select * from accounts;
ACCOUNT_NUMBER ACCOUNT_NAME
-------------- --------------------
2 My Name
select * from transactions;
TR_DATE TR_ACCOUNT_NUMBER TR_TYPE TR_SOMENUMBER
---------- ----------------- -------------------- -------------
2017/07/11 2 NEW ACCOUNT 0
You can use CURVAL to get the most recent value returned by NEXTVAL:
CREATE OR REPLACE TRIGGER ACCOUNTS_TRANSCATION_TRIG_1 AFTER
INSERT ON ACCOUNTS FOR EACH ROW DECLARE CURSOR ACCOUNTS_CUR IS
BEGIN
INSERT
INTO TRANSACTIONS VALUES
(
SYSDATE,
- ACCOUNT_NO_SEQ.curval,
'NEW ACCOUNT',
0
);
CLOSE ACCOUNTS_CUR;
END ACCOUNTS_TRANSCATION_TRIG_1;
However in this case there is no need, as it has been used to set the ACOUNT_NUMBER:
INSERT
INTO TRANSACTIONS VALUES
(
SYSDATE,
- :NEW.ACCOUNT_NUMBER,
'NEW ACCOUNT',
0
);
BTW unless you are on an old version of Oracle this should work for first trigger:
CREATE OR REPLACE TRIGGER ACCOUNT_NUMBER_TRIG
BEFORE INSERT
ON ACCOUNTS
FOR EACH ROW
WHEN (NEW.ACCOUNT_NUMBER is not null)
BEGIN
:NEW.ACCOUNT_NUMBER := ACCOUNT_NO_SEQ.nextvaL;
END ACCOUNT_NUMBER_TRIG;
(I suspect the WHEN clause is wrong - should be when is null?)

Update column value after some validation on it

I have a scenario where i need to update the value of a column or say append !
Below is test table and data
create table test (rsrc_nm varchar2(50), parm varchar2(400));
insert into test values ('HLRCamelProfileBasic','resource_code:7|resource_type:HLR_Camel_Profile|Priority:1|');
insert into test values ('HSSUSERProfileBasic','resource_code:3|resource_type:HSS_User_Profile|Priority:1|');
insert into test values ('HSSUSERProfileBasic','resource_code:3|resource_type:HSS_User_Profile|Priority:2|');
insert into test values ('HSSUSERProfileBasic','resource_code:3|resource_type:HSS_User_Profile|Priority:1|');
insert into test values ('HLRBaseProfileBasic','resource_code:1|resource_type:HLR_Base_Profile|Priority:2|');
insert into test values ('HLRBaseProfileBasic','resource_code:1|resource_type:HLR_Base_Profile|Priority:3|');
Here we have 2 columns of a staging table in which rsrc_nm and parm are given what i have to do is i need to update or append the rsrc_nm column for parm coulmn where the last value i.e. priority is changing from 1 to 2 for same rsrc_nm.
For example
rsrc_ nm -- HSSUSERProfileBasic parm -- resource_code:3|resource_type:HSS_User_Profile|Priority:1|
rsrc_ nm -- HSSUSERProfileBasic parm -- resource_code:3|resource_type:HSS_User_Profile|Priority:2|
Here we have same parm priority but the values are different and i need to insert these values into another table for which i need to separate all PIPE values so while i insert it in the table it gives me error for unique constraint because that table table have 3 columns
RSRC_NM PARM VAL
------------------------------- -------------- ----------------
HSSUSERProfileBasic Priority 1
HLRCamelProfileBasic Priority 1
HLRBaseProfileBasic Priority 2
And on this table we have primary key on first two columns which enforce unique constraint also so i can not insert " HLRCamelProfileBasic " rsrc_nm for parm " Priority " because it fails in uniqueness.
So i find a solution to overcome this if i can append or update the rsrc_nm HLRCamelProfileBasic to " HLRCamelProfileBasic_1 " for " Priority:1 " and HLRCamelProfileBasic_2 for " Priority:2 " and so on for all RSRC_NM in Staging table.
My solution --
declare
cursor c1 is select * from RSRC_SVC_MAPPING where parm_entry like '%Priorit%';
parm_entry_length number;
l_value varchar2(100);
parm_name varchar2(100);
parm_val varchar2(100);
l_c1 c1%rowtype;
l_cnt number := 0;
l_rsrc_nm varchar2(100);
begin
open c1;
loop
fetch c1 into l_c1;
parm_entry_length := length(l_c1.Parm_Entry) - length(replace(l_c1.Parm_Entry,'|',''));
for i in 1 .. parm_entry_length loop
select regexp_substr(l_c1.Parm_Entry,'[^|]+',1,i) into l_value from dual;
select regexp_substr (l_value, '[^:]+', 1, 1) into parm_name from dual;
select regexp_substr (l_value, '[^:]+', 1, 2) into parm_val from dual;
-- dbms_output.put_line(l_value||' '||parm_name||' '||parm_val||' '||l_c1.resourcename);
for r in ( select count(*) cnt, resourcename
-- into l_cnt , l_rsrc_nm
from (select count(*), resourcename, parm_entry from RSRC_SVC_MAPPING where parm_entry like '%Priorit%' group by resourcename, parm_entry) group by resourcename)
loop
if r.cnt > 1
then
dbms_output.put_line(l_value||' '||parm_name||' '||parm_val||' '||l_c1.resourcename);
update RSRC_SVC_MAPPING
set resourcename = r.resourcename||'_'||l_cnt
where resourcename = r.resourcename;
l_cnt := l_cnt +1;
end if;
end loop;
end loop;
exit when c1%notfound;
end loop;
exception
when others then
dbms_output.put_line('ERROR OCCURED '||SQLCODE||' '||sqlerrm);
dbms_output.put_line(dbms_utility.format_error_backtrace());
end;
Data which needs to be like after UPDATE is like
HSSUSERProfileBasic_1 resource_code:3|resource_type:HSS_User_Profile|Priority:1|
HSSUSERProfileBasic_2 resource_code:3|resource_type:HSS_User_Profile|Priority:2|
HLRBaseProfileBasic_2 resource_code:1|resource_type:HLR_Base_Profile|Priority:2|
HLRBaseProfileBasic_3 resource_code:1|resource_type:HLR_Base_Profile|Priority:3|
Here we have changed the value rsrc_nm column and append it with the value of priority i.e. 1 and 2
target table is like
create table target (rsrc_nm varchar2(100), parm varchar2(50), val varchar2(50) constraint tgt_pk primary key (rsrrc_nm, parm));
MY DB is -- Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production

Sorting query results

How can I properly sort the results to show the results in the following order
1st - Parent Name
2nd - Child Name
3rd - Teacher Name
4th - Other Name
Where the parent name and child name is on the same table Family
id | parent name | child name
-----------------------------
1 Denz Hanz
2 Denz Pog
3 Joann Mac
while the other names are on different tables teacher table
id | teacher name
-----------------
1 Miguel
2 Sean
and Other_guest table
id | guest name
-----------------
1 Mike
2 Mal
where in cases that the parent did not arrive, the child name will be showed. Result of the query should show something like this
Participant Name
----------------
1. Denz
2. Denz
3. Mac
4. Miguel
5. Sean
6. Mal
7. Mike
I tried using order by field(),order by field asc, field2 dec ... etc but it seems not the result we wanted.
Below query works in most of the databases, this query will first sort by type and then name:
select * from (
select parent_name, 0 sort_by from table1
union all
select chile_name, 1 from table1
union all
select teacher_name, 2 from table2
union all
select guest_name, 3 from table3) t
order by sort_by, parent_name;
Here is my solution for Oracle DB:
Note that the UNION does not allow ORDER BY clause in subsequent select statements, therefore a procedure is necessary that generates the list of guests in the GUESTS table.
The table ARRIVED contains the IDs of the arrived parents, all other tables are self explanatory.
create table Family (id number, parentname varchar2(100), childname varchar2(100));
create table Teacher (id number, teachername varchar2(100));
create table Other_guest (id number, guestname varchar2(100));
create table Arrived (id number);
create table Guests (guestname varchar2(100));
create sequence Family_seq start with 1 order;
create sequence Teacher_seq start with 1 order;
create sequence Other_seq start with 1 order;
create or replace trigger Family_id before insert on Family for each row
begin
select Family_seq.nextval into :new.id from dual;
end;
create or replace trigger Teacher_id before insert on Teacher for each row
begin
select Teacher_seq.nextval into :new.id from dual;
end;
create or replace trigger Other_id before insert on Other_guest for each row
begin
select Other_seq.nextval into :new.id from dual;
end;
insert into Family (parentname, childname) values ('Denz', 'Hanz');
insert into Family (parentname, childname) values ('Denz', 'Pog');
insert into Family (parentname, childname) values ('Joann', 'Mac');
insert into Teacher (teachername) values ('Miguel');
insert into Teacher (teachername) values ('Sean');
insert into Other_guest (guestname) values ('Mike');
insert into Other_guest (guestname) values ('Mal');
insert into Arrived (id) values (1);
insert into Arrived (id) values (2);
create or replace procedure update_guest_list as
pragma autonomous_transaction;
cursor c_parents is select parentname from family where id in (select id from arrived) order by family.parentname;
cursor c_children is select childname from family where id not in (select id from arrived) order by family.childname;
cursor c_teachers is select teachername from teacher order by teacher.teachername asc;
cursor c_others is select guestname from other_guest order by other_guest.guestname asc;
begin
delete from guests;
for parents_rec in c_parents
loop
insert into guests (guestname) values (parents_rec.parentname);
end loop;
for children_rec in c_children
loop
insert into guests (guestname) values (children_rec.childname);
end loop;
for teachers_rec in c_teachers
loop
insert into guests (guestname) values (teachers_rec.teachername);
end loop;
for others_rec in c_others
loop
insert into guests (guestname) values (others_rec.guestname);
end loop;
commit;
end;
/
execute update_guest_list;

PL/SQL trigger to insert next value

I created a trigger which works like when I update/insert a row in one table, an insert of a row will a done in another table which contains a primary key.
Now when I insert a row in the first table I want the trigger to check the last value of primary key of another table and if that is null or '-' then I've to insert 1 into that primary key column so as to insert the remaining values.
I've written the code as follows:
create or replace trigger "T1"
AFTER
insert or update on "buses"
for each row
begin
-- Here I want to check the V_id on vehicles table, if that is null or '-' then insert V_id as 1 along with the below insert statement.
if :NEW."b_key" is not null then
INSERT INTO vehicles (b_KEY,B_NAME,ADDRESS_1,CITY,STATE,ZIP,PHONE,WEBSITE) VALUES (:new.b_KEY,:new.b_NAME,:new.ADDRESS_1,:new.CITY,:new.STATE,:new.ZIP,:new.PHONE,:new.WEBSITE);
end if;
end;
How to find the last b_id in the vehicles table, so that if that value is null or '-' insert b_id as 1, followed by the above insert statement in the same row.
By adding another trigger we can do that as follows:
create or replace TRIGGER "B_VEHICLES"
before insert on "buses"
for each row
declare b_number number;
begin
select max(B_ID) into b_number from Vehicles;
if :OLD."B_ID" is null and b_number is null then
select 1 into :new."B_ID" from dual;
else select b_number + 1 into :new."B_ID" from dual;
end if;
end;​