plsql insert, update for existing id - sql

I have to write a stored proc for the below requirement.
Desired output : TAX table
CATEGORY_ID TAX_PERCENTAGE FROM_DATE CREATE_DATE TAX_ID TO_DATE
-------------------------------------------------------------------------------------
1 10 4/1/2012 12/19/2013 8:54:20 PM 61 31-MAR-13
1 12.5 4/1/2013 12/19/2013 8:54:44 PM 62 31-dec-9998
When a new CATEGORY_ID say 1 with tax_percentage 10 for business year(from_date) 4/1/2012 is inserted, then a default value should be placed in to_date column say 31-dec-9998. When i try to input the same cat_id 1 with diff set of values say tax_percentage 12.5, this time from_date 4/1/2013 then to_date value should be (from_date-1) of cat_id 1,tax_percent 10 and my to_date value of 10,cat_id=1 should be updated with default date 31-dec-9998.something like this
CREATE OR REPLACE procedure SCOTT.Sp_SaveNewTaxPercentage
(
iv_category_id number,
iv_tax_percentage varchar2,
iv_from_date varchar2,
iv_to_date varchar2 default '12/31/9998' ,
iv_created_date date,
ov_err_code out nocopy varchar2,
ov_err_msg out nocopy varchar2
)
is
lv_category_id varchar2(25);
LV_TO_DATE varchar2(25);
lv_cat_id varchar2(12);
begin
ov_err_code:=0;
for J in ( SELECT CATEGORY_ID,FROM_DATE FROM TAX_P WHERE CATEGORY_ID =IV_CATEGORY_ID)
loop
SELECT to_date(iv_from_date,'dd/mm/yyyy') -1 INTO LV_TO_DATE FROM DUAL;
lv_cat_id := J.CATEGORY_ID;
end loop;
update tax_p set TO_DATE=LV_TO_DATE where CATEGORY_ID =lv_cat_id;
commit;
IF lv_cat_id IS NULL THEN
LV_TO_DATE := iv_to_Date;
END IF;
insert into tax_p( TAX_ID , CATEGORY_ID ,TAX_PERCENTAGE, FROM_DATE ,TO_DATE,CREATE_DATE)
values(tax_seq.nextval,iv_category_id,iv_tax_percentage,iv_from_date,LV_TO_DATE,sysdate);
select 'Successfully Saved' into ov_err_msg from dual;
commit;
Exception
when others then
rollback;
ov_err_code:=1;
ov_err_msg:='Error while saving'||SQLERRM;
end Sp_SaveNewTaxPercentage;
/
show errors;

I think you don't need to loop records to update the to_date field, just a single UPDATE statement will work for you.
BEGIN
UPDATE tax_p
SET to_date = to_date(iv_from_date,'dd/mm/yyyy') -1
WHERE category_id = iv_category_id;
INSERT INTO tax_p(tax_id, category_id, tax_percentage, from_date, to_date, create_date)
VALUES(tax_seq.nextval, iv_category_id, iv_tax_percentage, iv_from_date, iv_to_date, SYSDATE);
COMMIT;
EXCEPTION WHEN ...
...
END;

Related

How can i make uniqueness in a sql table with using columns?

in my table there is no unique column , but i need to make uniqueness with using columns , adding a new column is worst option , i need to do this something like concat two or more columns how can i do that ?
i need to make uniqeness in my sql table with using two or more columns , adding a new columns is worst option , i need a solution which is concat two or more columns something like that how can i do that ?
Oracle
There is a way to check for "uniqueness" if you compare distinct values of different field combinations with total number of rows. In below example the score 0 (zero) means uniqueness:
WITH
tbl AS
(
Select 1 "COL_1", 'AXC' "COL_2", 'khgh jklj' "COL_3" From Dual Union All
Select 3 "COL_1", 'AXC' "COL_2", 'khgh jklj' "COL_3" From Dual Union All
Select 3 "COL_1", 'DEF' "COL_2", 'khgh jklj' "COL_3" From Dual Union All
Select 1 "COL_1", 'DEF' "COL_2", 'xxxx yyyy' "COL_3" From Dual
)
Select
Count(*) "TOTAL",
Count(*) - Count(DISTINCT COL_1) "COL_1_SCORE",
Count(*) - Count(DISTINCT COL_2) "COL_2_SCORE",
Count(*) - Count(DISTINCT COL_3) "COL_3_SCORE",
Count(*) - Count(DISTINCT COL_1 || COL_2) "COL_1-2_SCORE",
Count(*) - Count(DISTINCT COL_1 || COL_3) "COL_1-3_SCORE",
Count(*) - Count(DISTINCT COL_2 || COL_3) "COL_2-3_SCORE",
Count(*) - Count(DISTINCT COL_1 || COL_2 || COL_3) "COL_1-2-3_SCORE"
From
tbl
/* R e s u l t:
TOTAL COL_1_SCORE COL_2_SCORE COL_3_SCORE COL_1-2_SCORE COL_1-3_SCORE COL_2-3_SCORE COL_1-2-3_SCORE
---------- ----------- ----------- ----------- ------------- ------------- ------------- ---------------
4 2 2 2 0 1 1 0
*/
You can combine a handful of columns into a unique row id with the following SQL code.
I have done this when making data sets for machine learning in BigQuery.
select distinct
cast(to_hex(SHA1(concat(column1, column2, column3, column4))) as string) as observation_id
from table
Of course you need to check beforehand, that the combination of these columns produces unique rows.
One option is to create a unique index on set of columns you want to enforce uniqueness on.
For example:
create unique index ui1_test on test (id, name, address);
Doing so, users won't be able to insert new rows (or update existing ones) to a combination (of these columns) that already exists in the table as Oracle will raise an error.
I just implemented the following to make customer_id a unique column by combining a random number, part of a SYSTIMESTAMP and a sequence NUMBER. To make it difficult to decipher and shorter in length I ran in through a function. I also store the origin (seed) that was used to create the unique column.
As for the first_name and last_name I use so it makes sense in the example as there are names associated with customers.
CREATE OR REPLACE PACKAGE mf_names IS
FUNCTION random_first_name(
gender IN VARCHAR2 DEFAULT NULL,
percentage_mf IN NUMBER DEFAULT 50
) RETURN VARCHAR2;
FUNCTION random_last_name RETURN VARCHAR2;
END;
/
CREATE OR REPLACE PACKAGE BODY mf_names IS
first_names_male SYS.ODCIVARCHAR2LIST := SYS.ODCIVARCHAR2LIST(
'Tom', 'Andrew', 'Paul', 'Peter', 'Keith', 'Mark', 'Solomon', 'Joseph', 'John', 'Melvin', 'Harry', 'Barry', 'Larry', 'Gary', 'Jeffrey', 'karl'
);
first_names_female SYS.ODCIVARCHAR2LIST := SYS.ODCIVARCHAR2LIST(
'Alice', 'Anna', 'Barbara', 'Carol', 'Debra', 'Madison', 'Faith', 'Cheryl', 'Beth', 'Kathy', 'Jill', 'Grayce', 'Lynn', 'Roz', 'Deena', 'Laura', 'Sophia'
);
last_names SYS.ODCIVARCHAR2LIST := SYS.ODCIVARCHAR2LIST(
'Cooper', 'Dimeo', 'Spatafore', 'Jones', 'Coralnick', 'Torchiano', 'Fazio', 'Behrens', 'Lebowitz','Stern', 'Malden', 'Kramer','Stein', 'Tessio', 'Weinreb', 'Dillon', 'Rucker', 'Silverberg', 'Aarron', 'Kern', 'Saladino', 'Rice', 'Sanford', 'Orr', 'Roth'
);
FUNCTION random_first_name(
gender IN VARCHAR2 DEFAULT NULL,
percentage_mf IN NUMBER DEFAULT 50
) RETURN VARCHAR2
IS
BEGIN
IF UPPER(gender) LIKE 'M%' THEN
RETURN first_names_male(FLOOR(DBMS_RANDOM.VALUE(1, first_names_male.COUNT + 1)));
ELSIF UPPER(gender) LIKE 'F%' THEN
RETURN first_names_female(FLOOR(DBMS_RANDOM.VALUE(1, first_names_female.COUNT + 1)));
ELSIF DBMS_RANDOM.VALUE(0, 100) < percentage_mf THEN
RETURN random_first_name('M');
ELSE
RETURN random_first_name('F');
END IF;
END;
FUNCTION random_last_name RETURN VARCHAR2
IS
BEGIN
RETURN last_names(FLOOR(DBMS_RANDOM.VALUE(1, last_names.COUNT + 1)));
END;
END;
/
CREATE TABLE CUSTOMERS (
customer_id VARCHAR2 (20),
seed NUMBER,
first_name VARCHAR2 (20),
last_name VARCHAR2 (20));
create sequence customer_seq start with 1000000 minvalue 1000000 maxvalue 9999999 cycle;
create or replace function base34(p_num number) return varchar2 is
l_dig varchar2(34) := 'AB0CD1EF2GH3JK4LM5NP6QR7ST8UV9WXYZ';
l_num number := p_num;
l_str varchar2(38);
begin
loop
l_str := substr(l_dig,mod(l_num,34)+1,1) || l_str ;
l_num := trunc(l_num/34);
exit when l_num = 0;
end loop;
return l_str;
end;
/
create or replace function dec34(p_str varchar2) return number is
l_dig varchar2(34) := 'AB0CD1EF2GH3JK4LM5NP6QR7ST8UV9WXYZ';
l_num number := 0;
begin
for i in 1 .. length(p_str) loop
l_num := l_num * 34 + instr(l_dig,upper(substr(p_str,i,1)))-1;
end loop;
return l_num;
end;
/
create or replace trigger customer_trg
before update on customers for each row
begin
if ( updating('customer_id') )
then
raise_application_error(-20000,'Cant Update customer_id');
end if;
if ( updating('seed') )
then
raise_application_error(-20001,'Cant Update seed');
end if;
end;
/
DECLARE
seed NUMBER;
begin
for i in 1 .. 10 loop
seed := (to_number(trunc(dbms_random.value(1000,9999))|| to_char(systimestamp,'FFSS')||customer_seq.nextval));
INSERT into customers(
customer_id,
seed,
first_name,
last_name
) VALUES (
base34(seed),
seed,
mf_names.random_first_name(),
mf_names.random_last_name()
);
end loop;
end;
/
SELECT * from customers
CUSTOMER_ID SEED FIRST_NAME LAST_NAME
BJGEYVDR5HSBEL8 3754328637000441000120 Cheryl Stern
B8FPDSMR0WQSTNZ 4886346472000441000121 Roz Dillon
00KGDZARBUZTCB6 5711346658000441000122 Solomon Kern
KBMP0WU4VQ76T5 1058346784000441000123 Grayce Lebowitz
0EXWKL06SC2E3R6 6080346899000441000124 karl Jones
BM9XSDNX7CWNNZC 4128347038000441000125 Alice Sanford
01SKXFTE18JTT5M 5981347154000441000126 Cheryl Saladino
B53A6Y19CF7B54F 4164347334000441000127 John Stein
B3XMHNKT9LDLSDA 3726347470000441000128 Joseph Lebowitz
B7FT0265MYGJUTP 4643347602000441000129 Debra Rice
select to_char(seed) from customers where rownum = 1
union all
select base34(seed) from customers where rownum = 1
union all
select to_char(dec34(base34(seed))) from customers where rownum = 1;
TO_CHAR(SEED)
3754328637000441000120
BJGEYVDR5HSBEL8
3754328637000441000120
select base34(seed),
dump(base34(seed)) from customers where rownum = 1
BASE34(SEED) DUMP(BASE34(SEED))
BJGEYVDR5HSBEL8 Typ=1 Len=15: 66,74,71,69,89,86,68,82,53,72,83,66,69,76,56

Oracle converting an INSERT to MERGE ot not EXISTS

I have the following code, which is working fine. If I run the procedure more than once with the same values I get a PRIMARY KEY violation, which I expect. Could the INSERT be converted into a MERGE or NOT EXISTS to avoid this issue?
The examples I saw online appear to be using literal values or an ON statement with the MERGE.
As I am a novice SQL developer any help or sample code, which reflects my requirement would be greatly appreciated.
Thanks in advance to all who answer.
ALTER SESSION SET NLS_DATE_FORMAT = 'MMDDYYYY HH24:MI:SS';
CREATE OR REPLACE TYPE nt_date IS TABLE OF DATE;
/
CREATE OR REPLACE FUNCTION generate_dates_pipelined(
p_from IN DATE,
p_to IN DATE
)
RETURN nt_date PIPELINED DETERMINISTIC
IS
v_start DATE := TRUNC(LEAST(p_from, p_to));
v_end DATE := TRUNC(GREATEST(p_from, p_to));
BEGIN
LOOP
PIPE ROW (v_start);
EXIT WHEN v_start >= v_end;
v_start := v_start + INTERVAL '1' DAY;
END LOOP;
RETURN;
END generate_dates_pipelined;
/
create table schedule_assignment(
schedule_id number(4),
schedule_date DATE,
employee_id NUMBER(6) DEFAULT 0,
constraint sa_chk check (schedule_date=trunc(schedule_date, 'dd')),
constraint sa_pk primary key (schedule_id, schedule_date)
);
CREATE OR REPLACE PROCEDURE
create_schedule_assignment (
p_schedule_id IN NUMBER,
p_start_date IN DATE,
p_end_date IN DATE
)
IS
BEGIN
INSERT INTO schedule_assignment(
schedule_id,
schedule_date
)
SELECT
p_schedule_id,
COLUMN_VALUE
FROM TABLE(generate_dates_pipelined(p_start_date, p_end_date));
END;
EXEC create_schedule_assignment (1, DATE '2021-08-21', DATE '2021-08-30');
Rewrite procedure to
SQL> CREATE OR REPLACE PROCEDURE
2 create_schedule_assignment (
3 p_schedule_id IN NUMBER,
4 p_start_date IN DATE,
5 p_end_date IN DATE
6 )
7 IS
8 BEGIN
9 merge into schedule_assignment s
10 using (select p_schedule_id as schedule_id,
11 column_value as schedule_date
12 from table(generate_dates_pipelined(p_start_date, p_end_date))
13 ) x
14 on ( x.schedule_id = s.schedule_id
15 and x.schedule_date = s.schedule_date
16 )
17 when not matched then insert (schedule_id, schedule_date)
18 values (x.schedule_id, x.schedule_date);
19 END;
20 /
Procedure created.
SQL>
Testing: initially, table is empty:
SQL> select schedule_id, min(schedule_date) mindat, max(schedule_date) maxdate, count(*)
2 from schedule_assignment group by schedule_id;
no rows selected
Run the procedure for the 1st time:
SQL> EXEC create_schedule_assignment (1, DATE '2021-08-21', DATE '2021-08-30');
PL/SQL procedure successfully completed.
Table contents:
SQL> select schedule_id, min(schedule_date) mindat, max(schedule_date) maxdate, count(*)
2 from schedule_assignment group by schedule_id;
SCHEDULE_ID MINDAT MAXDATE COUNT(*)
----------- ---------- ---------- ----------
1 21/08/2021 30/08/2021 10
Run the procedure with same parameters again:
SQL> EXEC create_schedule_assignment (1, DATE '2021-08-21', DATE '2021-08-30');
PL/SQL procedure successfully completed.
SQL> EXEC create_schedule_assignment (1, DATE '2021-08-21', DATE '2021-08-30');
PL/SQL procedure successfully completed.
SQL> EXEC create_schedule_assignment (1, DATE '2021-08-21', DATE '2021-08-30');
PL/SQL procedure successfully completed.
Result: nothing changed, no rows in table (but no error either):
SQL> select schedule_id, min(schedule_date) mindat, max(schedule_date) maxdate, count(*)
2 from schedule_assignment group by schedule_id;
SCHEDULE_ID MINDAT MAXDATE COUNT(*)
----------- ---------- ---------- ----------
1 21/08/2021 30/08/2021 10
SQL>
Run the procedure with the same SCHEDULE_ID, but different dates:
SQL> EXEC create_schedule_assignment (1, DATE '2021-08-29', DATE '2021-09-02');
PL/SQL procedure successfully completed.
SQL> select schedule_id, min(schedule_date) mindat, max(schedule_date) maxdate, count(*)
2 from schedule_assignment group by schedule_id;
SCHEDULE_ID MINDAT MAXDATE COUNT(*)
----------- ---------- ---------- ----------
1 21/08/2021 02/09/2021 13
SQL>
Right; number of rows is now increased to 13 (was 10 previously, because 31.08., 01.09. and 02.09. were added).
New SCHEDULE_ID:
SQL> EXEC create_schedule_assignment (2, DATE '2021-09-05', DATE '2021-09-07');
PL/SQL procedure successfully completed.
SQL> select schedule_id, min(schedule_date) mindat, max(schedule_date) maxdate, count(*)
2 from schedule_assignment group by schedule_id;
SCHEDULE_ID MINDAT MAXDATE COUNT(*)
----------- ---------- ---------- ----------
1 21/08/2021 02/09/2021 13
2 05/09/2021 07/09/2021 3
SQL>
Looks OK to me.

Oracle difficulty creating a procedure that has subquery

I am attempting to build a procedure that will INSERT rows into the table emp_attendance.
I call a procedure that generates a list of dates based on a range. I then join that table with each employee_id.
Being a novice SQL developer, I am having difficulty trying to understand why the procedure create_emp_attendance is not being created.
Below is my test CASE. Once I get the rows working for the SELECT I will add the INSERT code as I am trying to take the one little piece at a time.
Thanks in advance for your help and expertise.
ALTER SESSION SET NLS_DATE_FORMAT = 'MMDDYYYY HH24:MI:SS';
CREATE OR REPLACE TYPE nt_date IS TABLE OF DATE;
CREATE OR REPLACE FUNCTION generate_dates_pipelined(
p_from IN DATE,
p_to IN DATE
)
RETURN nt_date PIPELINED DETERMINISTIC
IS
v_start DATE := TRUNC(LEAST(p_from, p_to));
v_end DATE := TRUNC(GREATEST(p_from, p_to));
BEGIN
LOOP
PIPE ROW (v_start);
EXIT WHEN v_start >= v_end;
v_start := v_start + INTERVAL '1' DAY;
END LOOP;
RETURN;
END generate_dates_pipelined;
CREATE SEQUENCE batch_seq
START WITH 1
MAXVALUE 999999999999999999999999999
MINVALUE 1
NOCYCLE
CACHE 20
NOORDER;
Create table employees(
employee_id NUMBER(6),
first_name VARCHAR2(20),
last_name VARCHAR2(20),
card_num VARCHAR2(10),
work_days VARCHAR2(7)
);
INSERT INTO employees (
employee_id,
first_name,
last_name,
card_num,
work_days
)
WITH names AS (
SELECT 1, 'John', 'Doe', 'D564311','YYYYYNN' FROM dual UNION ALL
SELECT 2, 'Justin', 'Case', 'C224311','YYYYYNN' FROM dual UNION ALL
SELECT 3, 'Mike', 'Jones', 'J288811','YYYYYNN' FROM dual UNION ALL
SELECT 4, 'Jane', 'Smith', 'S564661','YYYYYNN' FROM dual
) SELECT * FROM names;
CREATE TABLE emp_attendance(
seq_num integer GENERATED BY DEFAULT AS IDENTITY (START WITH 1) NOT NULL,
employee_id NUMBER(6),
start_date DATE,
end_date DATE,
week_number NUMBER(2),
create_date DATE DEFAULT SYSDATE
);
CREATE OR REPLACE PROCEDURE create_emp_attendance (
p_start_date IN DATE,
p_end_date IN DATE
)
IS l_batch_seq number;
BEGIN
SELECT get_batch_seq INTO l_batch_seq FROM dual;
SELECT
employee_id,
start_date,
start_date+NUMTODSINTERVAL(FLOOR(DBMS_RANDOM.VALUE(3600,43200)), 'SECOND') AS end_date,
to_char(start_date,'WW') AS week_number
FROM (
-- Need subquery to generate end_date based on start_date.
SELECT
e.employee_id, d.COLUMN_VALUE+ NUMTODSINTERVAL(FLOOR(DBMS_RANDOM.VALUE(0,86399)), 'SECOND') AS start_date
FROM employees e
INNER JOIN TABLE( generate_dates_pipelined(p_start_date, p_end_date)
) d
) ed
END;
EXEC create_emp_attendanc(DATE '2021-08-07', DATE '2021-08-14');
You didn't refer to the sequence, so I removed that. Your SELECT needs to have a target via INTO clause or be used within the context of an INSERT (or other) statement. The JOIN was missing an ON clause. But you appeared to want a CROSS JOIN.
If you really want to INSERT in one statement, here's the form:
CREATE OR REPLACE PROCEDURE create_emp_attendance (
p_start_date IN DATE,
p_end_date IN DATE
)
IS
BEGIN
INSERT INTO emp_attendance (employee_id, start_date, end_date, week_number)
SELECT employee_id
, start_date
, start_date+NUMTODSINTERVAL(FLOOR(DBMS_RANDOM.VALUE(3600,43200)), 'SECOND') AS end_date
, to_char(start_date,'WW') AS week_number
FROM ( -- Need subquery to generate end_date based on start_date.
SELECT e.employee_id, d.COLUMN_VALUE + NUMTODSINTERVAL(FLOOR(DBMS_RANDOM.VALUE(0,86399)), 'SECOND') AS start_date
FROM employees e
CROSS JOIN TABLE( generate_dates_pipelined(p_start_date, p_end_date) ) d
) ed
;
END;
/
EXEC create_emp_attendance(DATE '2021-08-07', DATE '2021-08-14');
/
-- Procedure CREATE_EMP_ATTENDANCE compiled
-- PL/SQL procedure successfully completed.
Read comments within code.
SQL> CREATE OR REPLACE PROCEDURE create_emp_attendance
2 (
3 p_start_date IN DATE,
4 p_end_date IN DATE
5 )
6 IS
7 l_batch_seq number;
8 BEGIN
9 -- there's no GET_BATCH_SEQ function (at least, you didn't post it)
10 -- SELECT get_batch_seq INTO l_batch_seq FROM dual;
11 l_batch_seq := batch_seq.nextval;
12
13 -- In order to avoid TOO_MANY_ROWS, switching to a cursor FOR loop
14 for cur_r in
15 (SELECT
16 employee_id,
17 start_date,
18 start_date+NUMTODSINTERVAL(FLOOR(DBMS_RANDOM.VALUE(3600,43200)), 'SECOND') AS end_date,
19 to_char(start_date,'WW') AS week_number
20 FROM (-- Need subquery to generate end_date based on start_date.
21 SELECT
22 e.employee_id,
23 d.COLUMN_VALUE + NUMTODSINTERVAL(FLOOR(DBMS_RANDOM.VALUE(0,86399)), 'SECOND') AS start_date
24 FROM employees e
25 -- not INNER, but CROSS join (or, if it were INNER, on which column(s)?)
26 CROSS JOIN TABLE(generate_dates_pipelined(p_start_date, p_end_date)) d
27 ) ed
28 ) loop
29 -- you'll probably have INSERT statement here, according to what you said
30 null;
31 end loop;
32 END;
33 /
Procedure created.
Testing:
SQL> EXEC create_emp_attendance(DATE '2021-08-07', DATE '2021-08-14');
PL/SQL procedure successfully completed.
SQL>

Procedure to insert data if not exist pl sql

I have a table with these columns (id,first_name,birth). I want to create a procedure that insert a new customer only if the id inserted doesn't exist in the table. If it already exist, then don't insert it. This is my code so far, but I got an error 'line 3 sql statement ignored'. Any idea? I need to use procedure and pl sql in oracle. Thanks!
CREATE OR REPLACE PROCEDURE add_emp(v_id IN int,
v_name IN varchar2,
v_bday IN date) IS
BEGIN
INSERT INTO Employees
(Id, First_name, Birth)
SELECT *
FROM (SELECT v_id, v_name, v_bday) AS tmp
WHERE NOT EXISTS (SELECT Id FROM Employees WHERE Id = v_id);
END;
/
DECLARE
m_id int := 3;
m_name varchar2 := 'John';
m_bday date := '16-Dec-1990';
BEGIN
add_cust(m_id, m_name, m_bday);
END;
/
Your procedure has some syntax issue which is fixed in following code:
CREATE OR REPLACE PROCEDURE ADD_EMP (
V_ID IN INT,
V_NAME IN VARCHAR2,
V_BDAY IN DATE
) IS
BEGIN
INSERT INTO EMPLOYEES (
ID,
FIRST_NAME,
BIRTH
)
SELECT V_ID,
V_NAME,
V_BDAY
FROM DUAL -- FROM clause was missing
WHERE NOT EXISTS (
SELECT ID
FROM EMPLOYEES
WHERE ID = V_ID
);
END;
/
Also, Your calling PL/SQL block has some issues which are corrected in the following code:
DECLARE
M_ID INT := 3;
M_NAME VARCHAR2(10) := 'John'; -- varchar2 must be declared with size
M_BDAY DATE := DATE '1990-12-16'; -- added date literal to convert string to date
BEGIN
ADD_CUST(M_ID, M_NAME, M_BDAY);
END;
/
In Oracle SELECT does not work without a FROM clause (other DBMS products are different). So you need to provide a table; you can use DUAL, which is a dummy table provided by Oracle which is guaranteed to return one row.
INSERT INTO Employees(Id,First_name,Birth)
SELECT v_id, v_name, v_bday
from dual
WHERE NOT EXISTS (
SELECT Id FROM Employees WHERE Id = v_id
);
Your INSERT statement would work with a slight change
CREATE OR REPLACE PROCEDURE add_emp(v_id Employees.Id%type,
v_name Employees.First_name%type,
v_bday Employees.Birth%type) IS
BEGIN
INSERT INTO Employees
(Id, First_name, Birth)
SELECT v_id, v_name, v_bday
FROM dual
WHERE NOT EXISTS (SELECT * FROM Employees WHERE Id = v_id);
END;
/
You can replace INSERT statement with MERGE as an alternative DML such as
MERGE INTO Employees e1
USING
(SELECT v_id AS id, v_name AS First_name, v_bday AS birth
FROM dual) e2
ON ( e1.id = e2.id )
WHEN MATCHED THEN UPDATE SET e1.First_name = e2.First_name,
e1.Birth = e2.Birth -- If you need to change for the matching case
WHEN NOT MATCHED THEN INSERT( e1.id, e1.First_name, e1.birth )
VALUES( e2.id, e2.First_name, e2.birth );

oracle sql : "get or insert" stored procedure

I would like to do a stored procedure that receive an input param and then
if targeted table does not contains that value, a new row is created and then the id of the created row is returned
if targeted table already contain input param, the id of the row is returned
For moment I only manage to insert new row only if input param is new:
--exemple of table with a primary id a column with value
create table unique_number_table (
id NUMBER(12) not null,
UNIQUE_NUMBER VARCHAR2(80) not null,
constraint PK_ID primary key (ID)
);
create sequence SEQ_NUMBER
INCREMENT BY 1
START WITH 2
MAXVALUE 999999999
MINVALUE 0;
create or replace procedure insert_or_get_unique_number ( input_number in varchar ) is
begin
insert into unique_number_table (id, UNIQUE_NUMBER)
select SEQ_NUMBER.NEXTVAL ,input_number
from dual
where not exists(select * from unique_number_table
where UNIQUE_NUMBER =input_number);
end insert_or_get_unique_number;
Do you know how to do this?
Seems to me like you want a stored function and not a procedure.
create or replace function insert_or_get_unique_number (input_number varchar2)
return UNIQUE_NUMBER_TABLE.ID%type
is
L_NUM UNIQUE_NUMBER_TABLE.ID%type;
begin
select ID
into L_NUM
from UNIQUE_NUMBER_TABLE
where UNIQUE_NUMBER = input_number;
return L_NUM;
exception
when NO_DATA_FOUND then
insert into unique_number_table (id, UNIQUE_NUMBER)
values (SEQ_NUMBER.NEXTVAL, input_number)
returning ID into L_NUM;
return L_NUM;
end insert_or_get_unique_number;
This is a possible solution to your problem.
CREATE OR REPLACE PROCEDURE insert_or_get_unique_number (
input_number IN VARCHAR,
c_out out sys_refcursor
) IS
Lv_input_exists INT;
lv_myRowid VARCHAR2(200);
err_code varchar2(600);
err_msg varchar2(500);
BEGIN
--step 1 check if the input param exists. -
select count(*)
INTO Lv_input_exists
FROM unique_number_table
WHERE unique_number = input_number;
--step 2 if it exists than get the rowid of that row and return that value
IF Lv_input_exists > 0
THEN
OPEN c_out for
SELECT ROWID
FROM unique_number_table Uni
WHERE uni.id = input_number ;
RETURN;
ELSE
-- STEP 3 the input number does not exists therefore we need to insert and return the rowid--
INSERT INTO unique_number_table (
id,
unique_number
)
VALUES(
seq_number.NEXTVAL,
input_number)
returning ROWID into lv_myRowid;
----STEP 4 Open the cursor and return get the rowid.
OPEN c_out for
SELECT lv_myRowid
FROM DUAL ;
SYS.dbms_output.put_line( 'Done' );
END IF;
EXCEPTION WHEN OTHERS THEN
err_code := SQLCODE;
err_msg := SUBSTR(SQLERRM, 1, 200);
SYS.dbms_output.put_line( err_code || ' '||': '||err_msg );
END insert_or_get_unique_number;
you can test the procedure like so.
set serveroutput on ;
DECLARE
INPUT_NUMBER VARCHAR2(200);
C_OUT sys_refcursor;
BEGIN
INPUT_NUMBER := '3';
INSERT_OR_GET_UNIQUE_NUMBER(
INPUT_NUMBER => INPUT_NUMBER,
C_OUT => C_OUT
);
DBMS_SQL.return_result(C_OUT);
END;