Procedure to insert data if not exist pl sql - 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 );

Related

How to check if exists a row before insert - PL/SQL

Im new with SQL, and I want to knw how can I add a SQL in this structure to check if the row already exists before insert, Im tried this way, but I think its not the best.
DECLARE
v_project_id NUMBER;
v_group_name VARCHAR2(100);
v_user_id NUMBER;
v_group_id NUMBER;
BEGIN
FOR project IN (
SELECT project_id
FROM JSON_TABLE(:p_request,
'$' COLUMNS(NESTED PATH '$.projects[*]'
COLUMNS(
project_id VARCHAR2(100) PATH '$.project.id'
)
)
)
)
LOOP
v_project_id := project.project_id;
v_group_name := json_value (:p_request,'$.group_name');
v_user_id := json_value (:p_request,'$.user_id');
v_group_id := s_project_group.NEXTVAL;
dbms_output.put_line(v_group_id||' - '||v_group_name||' - '||v_user_id||' - '||v_project_id);
--------- HERE, I WANT TO CHECK IF THE NEW ROW ALREADY EXISTS BEFORE INSERT, IM TRIED THIS WAY, BUT I THINK ITS NOT THE BEST------------
(select case
when NOT exists (select 1 FROM APP_PROJECT_GROUP WHERE GROUP_NAME = :v_group_name AND USER_ID = :v_user_id)
INSERT INTO APP_PROJECT_GROUP (GROUP_ID, GROUP_NAME, PROJECT_ID, USER_ID) VALUES (v_group_id, v_group_name, v_project_id, v_user_i)
);
----------------------------------------------------------------------------------------------------------------------------------------
COMMIT;
END LOOP;
END;
As I commented on your previous question, use a MERGE statement then you can perform all the INSERTs in a single SQL statement (without having to use use cursor loops and repeatedly context-switching from PL/SQL to SQL):
MERGE INTO app_project_group dst
USING (
SELECT group_id,
group_name,
TO_NUMBER(project_id) AS project_id,
user_id
FROM JSON_TABLE(
:p_request,
'$'
COLUMNS(
group_name VARCHAR2(150) PATH '$.group_name',
group_id NUMBER(15) PATH '$.group_id',
user_id NUMBER(15) PATH '$.user_id',
NESTED PATH '$.projects[*]'
COLUMNS (
project_id VARCHAR2(15) PATH '$.project.id'
)
)
)
) src
ON (src.group_name = dst.group_name AND src.user_id = dst.user_id)
WHEN NOT MATCHED THEN
INSERT (group_id, group_name, project_id, user_id)
VALUES (s_project_group.NEXTVAL, src.group_name, src.project_id, src.user_id);
db<>fiddle here

How to use GROUP function (MAX) in FUNCTION?

CREATE TABLE cursor_table(
emp_id NUMBER(10) PRIMARY KEY,
emp_name VARCHAR2(30),
emp_salary NUMBER(5)
);
INSERT INTO cursor_table VALUES(101,'addeesh',25000);
INSERT INTO cursor_table VALUES(102,'arunkumar',28000);
INSERT INTO cursor_table VALUES(103,'ashokkumar',35000);
INSERT INTO cursor_table VALUES(104,'durairaj',22000);
CREATE OR REPLACE FUNCTION high_payed_emp
RETURN NUMBER
IS
high_payed_emp_id NUMBER(10):=0;
DECLARE max_emp_salary = MAX(emp_salary);
BEGIN
SELECT emp_id INTO high_payed_emp_id
FROM cursor_table
WHERE emp_salary = max_emp_salary;
RETURN high_payed_emp_id;
END;
I need to create a function that will find the highest paid employee.
Error message: Encountered the symbol "DECLARE" when expecting one of the following: begin function pragma procedure subtype type current cursor delete exists prior
You need a SQL statement to use your MAX function.
CREATE OR REPLACE FUNCTION high_payed_emp
RETURN NUMBER
IS
high_payed_emp_id NUMBER(10):=0;
max_emp_salary NUMBER :=0;
BEGIN
SELECT MAX(emp_salary)
INTO max_emp_salary
FROM cursor_table;
SELECT emp_id INTO high_payed_emp_id
FROM cursor_table
WHERE emp_salary = max_emp_salary;
RETURN high_payed_emp_id;
END;
Please check the code below, you had both syntactical and logical errors.
CREATE OR REPLACE FUNCTION high_payed_emp
RETURN NUMBER
IS
high_payed_emp_id NUMBER(10):=0;
BEGIN
select emp_id
into high_payed_emp_id
from (
select emp_id
from cursor_Table
order by emp_salary desc
)
where rownum = 1;
RETURN high_payed_emp_id;
END;
Just use the subquery to compare max salary, your code should look like this:
CREATE OR REPLACE FUNCTION high_payed_emp
RETURN NUMBER
IS
high_payed_emp_id NUMBER(10);
BEGIN
SELECT emp_id INTO high_payed_emp_id
FROM cursor_table
WHERE emp_salary = (Select max(emp_salary) from cursor_table);
RETURN high_payed_emp_id;
END;
Execution:
Select high_payed_emp from dual;
OUTPUT:
HIGH_PAYED_EMP
--------------
103

Get all records if table type parameter is null

In Oracle, I want to get all values of column if the table valued parameter is null otherwise only the matched records.
create or replace PROCEDURE pr_Employees (
lastnames IN LastName,
rowCursor OUT SYS_REFCURSOR) IS
BEGIN
Select * from emp where lastname in (
SELECT COLUMN_VALUE FROM TABLE(lastnames)
)
Try this:
create or replace procedure pr_employees
( lastnames in lastname
, rowcursor out sys_refcursor )
is
begin
open rowcursor for
select * from emp
where lastname in
( select column_value from table(lastnames) )
or lastnames is empty;
end pr_employees;

Check if there is a location_id and manager_id with the same name (they are from different tables)

So on that code I checked the uniqueness of department_id and department_name. Now I would like to do the same with manager_id and locations_id. Please keep in mind that manager_id is from table EMPLOYEES and locations_id is from table LOCATIONS.
I wonder if I can just continue that statement:
upper(s.department_id) = upper(d.department_id)
OR upper(s.department_name) = upper(d.department_name)
But I think that will not be enough, because it will check only in the DEPARTMENTS table. Not from LOCATIONS and EMPLOYEES like I want. Please advise.
CREATE OR REPLACE PROCEDURE add_dep(p_id NUMBER,
p_name VARCHAR2,
p_mgr NUMBER,
p_loc NUMBER) IS
BEGIN
MERGE INTO departments d
USING (
SELECT
p_id department_id,
p_name department_name,
p_mgr manager_id,
p_loc location_id
FROM dual) s
ON ( upper(s.department_id) = upper(d.department_id)
OR upper(s.department_name) = upper(d.department_name))
WHEN NOT MATCHED THEN
INSERT VALUES (s.department_id, s.department_name, s.manager_id, s.location_id);
IF SQL%ROWCOUNT = 0 THEN
INSERT INTO error_depa VALUES (p_id, p_name, p_mgr, p_loc);
END IF;
END;
EDIT - additional information
I got an error that there are not enough values for LOCATIONS and EMPLOYEES table. But I also tried to make this with exception and no_data_found. Can you modify this code and help me with that please? I would like to check the same for locations_id and manager_id.
create or replace procedure add_de(
p_id NUMBER,
p_name VARCHAR2,
p_mgr NUMBER,
p_loc NUMBER
)
is
v_dummy number;
begin
select 1
into v_dummy
from departments
where department_name = p_name OR DEPARTMENT_ID = p_id ;
insert
into error_depa
values(
p_id,
p_name,
p_mgr,
p_loc
);
exception
when no_data_found
then
insert
into departments
values(
p_id,
upper(p_name),
p_mgr,
p_loc
);
end;
I think that you want something like this:
CREATE OR REPLACE PROCEDURE add_dep(p_id NUMBER,
p_name VARCHAR2,
p_mgr NUMBER,
p_loc NUMBER) IS
BEGIN
MERGE INTO departments d
USING (
SELECT
p_id department_id,
p_name department_name,
p_mgr manager_id,
p_loc location_id
FROM dual) s
ON ( upper(s.department_id) = upper(d.department_id)
OR upper(s.department_name) = upper(d.department_name))
WHEN NOT MATCHED THEN
***INSERT(department_id, department_name,manager_id,location_id)*** VALUES (s.department_id, s.department_name, s.manager_id, s.location_id);
COMMIT;
MERGE INTO employees trg
USING (
SELECT
*
FROM employees
where
manager_id = p_mgr) src
ON ( upper(trg.manager_id) = upper(src.manager_id))
WHEN NOT MATCHED THEN
***INSERT(department_id, ...)*** VALUES (src.department_id, ....);
COMMIT;
MERGE INTO locations trg
USING (
SELECT
*
FROM locations
where
locations_id = p_loc) src
ON ( upper(trg.locations_id) = upper(src.locations_id))
WHEN NOT MATCHED THEN
***INSERT(locations_id, ...)*** VALUES (src.locations_id, ....);
COMMIT;
IF SQL%ROWCOUNT = 0 THEN
INSERT INTO error_depa VALUES (p_id, p_name, p_mgr, p_loc);
END IF;
END;
/

PL/SQL MERGE Using Collection

I am having trouble merging a table with a collection.
Let's say I have a table emp.
Here is my PL/SQL code snippet.
TYPE empcol is table of emp%ROWTYPE INDEX BY BINARY_INTEGER;
tmpemp empcol;
-- Code here to load data from a CSV file into tmpemp collection
-- tmpemp(1).emp_id := parsedstring
-- etc.
MERGE INTO emp A using tmpemp B ON A.emp_id = B.emp_id
WHEN MATCHED THEN UPDATE SET A.fname = B.fname, A.lname = B.lname
WHEN NOT MATCHED THEN INSERT (emp_id, fname, lname) VALUES (b.emp_id, b.fname, b.lname);
Compiler doesn't like it. Its throwing ORA-0942 - Table or View doesn't exist.
What am I doing wrong? or How can I do this better.
Thanks a lot for any help you can provide.
PL/SQL types like emp%ROWTYPE or TABLE OF ... INDEX BY ... cannot be used in SQL queries.
The type must be declared as SQL type (not as PL/SQL type) to be used in SQL query.
Try this approach (example):
create table emp(
firstname varchar2(100),
salary number
);
insert into emp values( 'John', 100 );
commit;
create type my_emp_obj is object(
firstname varchar2(100),
salary number
);
/
create type my_emp_obj_table is table of my_emp_obj;
/
declare
my_emp_tab my_emp_obj_table;
begin
null;
my_emp_tab := my_emp_obj_table( my_emp_obj( 'John', 200 ), my_emp_obj( 'Tom', 300 ));
MERGE INTO emp
USING ( SELECT * FROM TABLE( my_emp_tab )) src
ON ( emp.firstname = src.firstname )
WHEN MATCHED THEN UPDATE SET salary = src.salary
WHEN NOT MATCHED THEN INSERT VALUES( src.firstname, src.salary );
end;
/
select * from emp;
FIRSTNAME SALARY
----------------------- ----------
John 200
Tom 300