PL/SQL (INSERT/UPDATE) unique constraint violated error in trigger due to sequencel.nextval - oracle

I want insert/update records to another table (MICL_SUPERVISORS) using Trigger (pl/sql oracle 10g).
When trigger fired it is giving an error as
ORA-00001: unique constraint violated.
I know it happens because I want to add SUPID from sequence
Select micl_sup_id_seq.nextval into nSUPID from dual
And this is happening inside a loop.
SUPID column is primary key in my table( MICL_SUPERVISOR). So I can't drop that constraint.
Once I tried auto incrementing but it take long time and it didn't work well and it is slow. I have thousands of records in this table. I did it as
SELECT MAX((SUP_ID)+1 from micl_sup_id_seq
Due to this error I did a small research and found out we cannot use seq.nextval inside a trigger. So my question is is there any easy, accurate way to achieve this?
Here is the code (it all happening inside if clause else part is working Fine. Pls note that I have use a cursor , inside open cursor all this happen)
CREATE OR REPLACE TRIGGER "c"."INSERT_MICL_SUP_DETAILS"
AFTER INSERT OR UPDATE OF "ID","SUP_EMP_NO","EMP_NO" ON "MIMAX"."EMP"
REFERENCING OLD AS "OLD" NEW AS "NEW" FOR EACH ROW
DECLARE
miclaim_supervisor_count NUMBER;
employee_company_code VARCHAR2(10);
employee_businessunit NUMBER;
projmgr NUMBER;
nsupid NUMBER;
CURSOR projmgrscursor IS
SELECT b.bu_member_id
FROM bu_member b, emp_sub_div s
WHERE s.emp_no = :NEW.emp_no
AND s.sub_division_code = '0345' AND s.div_code = '1010'
AND b.bu_id IN (SELECT bu_id FROM bu_member WHERE bu_member_id = :NEW.emp_no);
BEGIN
DELETE
FROM micl_supervisors
WHERE emp_no = :NEW.emp_no
AND is_ovverridden = 0;
SELECT count(*)
INTO miclaim_supervisor_count
FROM micl_supervisors
WHERE emp_no = :NEW.emp_no
AND is_ovverridden = 1;
SELECT company_code
INTO employee_company_code
FROM employee_master
WHERE emp_no = :NEW.emp_no;
projmgr := 0;
IF (employee_company_code ='SOFT')THEN
OPEN projmgrscursor;
LOOP
FETCH projmgrscursor INTO projmgr;
EXIT WHEN projmgrscursor%notfound;
SELECT micl_sup_id_seq.nextval INTO nsupid FROM dual;
INSERT INTO micl_supervisors (sup_id,assigned_date
, assigned_by_emp_no
, amount_limit
, is_ovverridden
, sup_emp_no
, rtd_emp
, emp_no)
VALUES ( nsupid
, SYSDATE
, :NEW.entryaddedby_emp_no
, 3000
, 0
, projmgr
, NULL
, :NEW.emp_no);
END LOOP;
CLOSE projmgrscursor;
ELSE
IF(miclaim_supervisor_count IS NULL OR miclaim_supervisor_count<1) THEN
INSERT INTO micl_supervisors VALUES (:NEW.ID
, SYSDATE
, :NEW.entryaddedby_emp_no
, 3000
, 0
, :NEW.sup_emp_no
, NULL
, :NEW.emp_no);
END IF;
END IF;
END;
/
If anything unclear ask me I'll explain furthermore about this scenario , I hope anyone will help to solve this problem

What other constraints are present on the table? It's more likely that you're running into a constraint error other the sequence, which you are fixated upon.
Due to this Error I did a small research and found out we cannot use seq.nextval inside a trigger.
I don't know where you read that, but that's absolutely false. I've used seq.nextval for many of my audit triggers/tables, and it works fine.
Query all_constraints (or user_constraints) with table name micl_supervisors - like so
SELECT *
FROM user_constraints
WHERE table_name = 'MICL_SUPERVISORS'
and update the question or check with what data you're trying to insert.

Related

Merge with select with multiple rows

I have a query which every time runs, selects the rows of user_triggers which are related to a table(p_table_name_in). I want to run this procedure every day and I want to just insert new rows, not all rows again. but when I install this oackage , I get this error:
ORA-00932 (130: 21): PL / SQL: ORA-00932: Inconsistent data types:
CLOB expected, LONG received (line 31)
and when I try to change TRIGGER_BODY AS BODY_TRIGGER to TO_LOB(TRIGGER_BODY) AS BODY_TRIGGER I get this error:
ORA-00932 (111: 29): PL / SQL: ORA-00932: Inconsistent data types: -
expected, LONG received (line 12)
procedure:
PROCEDURE save_trigger_definitions ( p_table_name_in in VARCHAR2 ) IS
BEGIN
MERGE INTO hot_utils_reload_triggers t1
USING
(
SELECT TRIGGER_NAME ,
TABLE_NAME ,
STATUS ,
DESCRIPTION,
TRIGGER_BODY AS BODY_TRIGGER,
WHEN_CLAUSE
FROM user_triggers
)t2
ON(t2.TABLE_NAME like upper(p_table_name_in))
WHEN MATCHED THEN UPDATE SET
t1.DESCRIPTION = t2.DESCRIPTION,
t1.WHEN_CLAUSE = t2.WHEN_CLAUSE
WHEN NOT MATCHED THEN
INSERT (TRIGGER_NAME,
TABLE_NAME,
STATUS,
DESCRIPTION,
BODY_TRIGGER,
WHEN_CLAUSE)
VALUES (t2.TRIGGER_NAME,
t2.TABLE_NAME,
t2.STATUS,
t2.DESCRIPTION,
t2.BODY_TRIGGER,
t2.WHEN_CLAUSE);
commit;
END save_trigger_definitions;
It's also interesting to me that Oracle does not allow to use TO_LOB within a SELECT or MERGE Statement, while does for INSERT. Thus you can seperately use INSERT and MERGE with only the part containing MATCHED part such as
CREATE OR REPLACE PROCEDURE save_trigger_definitions ( p_table_name_in in VARCHAR2 ) IS
BEGIN
INSERT INTO hot_utils_reload_triggers
(trigger_name,
table_name,
status,
description,
body_trigger,
when_clause)
SELECT trigger_name,
table_name,
status,
description,
TO_LOB(trigger_body),
when_clause
FROM user_triggers
WHERE table_name LIKE UPPER(p_table_name_in)
AND NOT EXISTS ( SELECT 1
FROM hot_utils_reload_triggers
WHERE trigger_name = u.trigger_name
AND table_name = u.table_name
AND status = u.status );
UPDATE hot_utils_reload_triggers h
SET h.description = description, h.when_clause = when_clause
WHERE table_name LIKE UPPER(p_table_name_in);
COMMIT;
END;
/
assuming that you don't want duplicated rows for some columns such as trigger_name,table_name,status, I have added a subquery for them after NOT EXISTS clause.
Ref1
Ref2
Using DBMS_REDEFINITION.START_REDEF_TABLE() might be another alternative for LONG to LOB conversion cases.

concurrency in oracle plsql

I have a PL/SQL package in Oracle that its important function is :
function checkDuplicate(in_id in varchar2) return boolean is
cnt number;
begin
select count(*)
into cnt
from tbl_Log t
where t.id = in_id
if (cnt > 0) then
// It means the request is duplicate on in_id
return false;
end if;
insert into tbl_log (id,date) values(in_id , sysdate);
return true;
end;
When two requests call this function concurrently, both of them passed this function and two the same in_id inserted in tbl_log.
Note: tbl_log doesn't have a PK for performance issues.
Are there any solutions?
" both of them passed this function and two the same in_id inserted in tbl_log"
Oracle operates at the READ COMMITTED isolation level, so the select can only find committed records. If one thread has inserted a record for a given value but hasn't committed the transaction another thread looking for the same value will come up empty.
"Note: tbl_log doesn't have a PK for performance issues. "
The lessons of history are clear: tables without integrity constraints inevitably fall into data corruption.
"I want to recognize the duplication with this function ... Are there any solutions?"
You mean apart from adding a primary key constraint? There is no more efficient way of trapping duplication than a primary key. Maybe you should look at the performance issues. Plenty of applications mange to handle millions of inserts and still enforce integrity constraints. You should also look at the Java layer: why have you got multiple threads submitting the same ID?
Note: tbl_log doesn't have a PK for performance issues.
There is no PK nor unique index on this column in order to "avoid performance issues", but there are hundreds or thousands queries like SELECT ... WHERE t.id = .. running against this table. These queries must use a full table scan due to lack of index on this column !!!!
This can cause much bigger performance issues in my opinion.
Since the values of this columns are UUIDs, then there is a very little chance of conflicted values. In this case I would prefer not to use any locks.
Just use an unique constraint (index) on this column to prevent from inserting two duplicate values.
ALTER TABLE tbl_log ADD CONSTRAINT tbl_log_id_must_be_unique UNIQUE( id );
and then use this implementation of your function:
create or replace function checkDuplicate(in_id in varchar2) return boolean is
begin
insert into tbl_log (id,"DATE") values(in_id , sysdate);
return true;
exception when dup_val_on_index then
return false;
end;
/
In the vast majority of cases the function simply inserts a new record to the table without any delay because values are UUIDs.
In seldom cases of duplicated values, when the value is already commited in the table, the insert will immediatelly fail, without any delay.
In very very rare cases (almost impossible) when two threads are trying to simultanously insert the same UUID, the second thread will be held on INSERT command and will wait some time until the first thread will commit or rollback.
As per your condition, since you are reluctant to use Primary key data integrity enforcement( which will lead to data corruption anyhow ), i would suggest that you can use MERGE statment and keep an audit log for the latest thread updating the table. This way you will be able to eliminate the entry of duplicate record as well as keep a track of when and from which thread (latest info) the id got updated. Hope the below snippet helps.
---Create dummy table for data with duplicates
DROP TABLE dummy_hist;
CREATE TABLE dummy_hist AS
SELECT LEVEL COL1,
'AVRAJIT'
||LEVEL COL2,
SYSTIMESTAMP ACTUAL_INSERTION_DT,
SYSTIMESTAMP UPD_DT,
1 thread_val
FROM DUAL
CONNECT BY LEVEL < 100;
--Update upd_dt
UPDATE dummy_hist SET upd_dt = NULL,thread_val = NULL;
SELECT * FROM dummy_hist;
--Create function
CREATE OR REPLACE
FUNCTION checkDuplicate(
in_id IN VARCHAR2,
p_thread_val IN NUMBER)
RETURN BOOLEAN
IS
cnt NUMBER;
BEGIN
MERGE INTO dummy_hist A USING
(SELECT in_id VAL FROM dual
)B ON (A.COL1 = B.VAL)
WHEN MATCHED THEN
UPDATE
SET a.upd_dt = systimestamp,
a.thread_val = p_thread_val
WHERE a.col1 = b.val WHEN NOT MATCHED THEN
INSERT
(
a.col1,
a.col2,
a.actual_insertion_dt,
a.UPD_DT,
a.thread_val
)
VALUES
(
b.val,
'AVRAJIT',
SYSTIMESTAMP,
NULL,
p_thread_val
);
COMMIT;
RETURN true;
END;
/
--Execute the fucntion
DECLARE
rc BOOLEAN;
BEGIN
FOR I IN
(SELECT LEVEL LVL FROM DUAL CONNECT BY LEVEL BETWEEN 8 AND 50
)
LOOP
rc:=checkduplicate(I.LVL,3);
END LOOP;
END;
/

ORA-02437: "primary key violated" - why can't I see duplicate ID in SQL Developer?

I would receive an error:
ORA-02437: cannot validate (%s.%s) - primary key violated
Cause: attempted to validate a primary key with duplicate values or null values
I found it was because I have a stored procedure that increments the ID, but it had failed to do so when it re-ran and had an error related to one of my datatypes. I found I now had a duplicate ID in my database table. All this made sense and I was able to easily rectify it with a DELETE FROM MyTable WHERE ID = x, where x was the offending duplicate ID. The problem I have is the only way I was able to even find the IDs that were duplicated is in the first place is because I did a SELECT * FROM MyTable WHERE ID = x -- where x was one greater than the last ID I could actually see. I found it just by an educated guess. So:
Why can't I see these duplicate IDs when I open the table in Oracle SQL Developer? It only shows the last row as the ID before the duplicates. I don't think it is because of my primary key constraint, since the first line in my stored procedure is to remove that (and put it back, at the end - probably when I got my error), and it was not present when I looked at my table.
Is there some way to make these last IDs that got inserted into the table visible, so I wouldn't have to guess or assume that the duplicate IDs are "hiding" as one greater than the last ID I have in my table, in the future? There is a commit; in my stored procedure, so they should have appeared -- unless, of course, the procedure got hung up before it could run that line of code (highly probable).
Stored procedure that runs:
create or replace
PROCEDURE PRC_MYTABLE_INTAKE(
, EMPLOYEE_ID IN NVARCHAR2
, TITLE_POSITION IN NVARCHAR2
, CREATED_DATE IN DATE
, LAST_MODIFIED IN DATE
) AS
myid integer := 0;
appid integer := 0;
BEGIN
-- disable PK constraint so it can be updated
EXECUTE IMMEDIATE 'ALTER TABLE MYTABLE DROP CONSTRAINT MYTABLE_PK';
COMMIT;
-- assign ID to myid
SELECT ID INTO myid FROM MYTABLE WHERE ROWID IN (SELECT MAX(ROWID) FROM MYTABLE);
-- increment
myid := myid + 1;
-- assign APPLICATION_ID to appid
SELECT APPLICATION_ID INTO appid FROM MYTABLE WHERE ROWID IN (SELECT MAX(ROWID) FROM MYTABLE);
-- increment
appid := appid + 1;
-- use these ids to insert with
INSERT INTO MYTABLE (ID, APPLICATION_ID,
, EMPLOYEE_ID
, TITLE_POSITION
, CREATED_DATE
, LAST_MODIFIED
) VALUES(myid, appid,
, EMPLOYEE_ID
, TITLE_POSITION
, CREATED_DATE
, LAST_MODIFIED
);
COMMIT;
-- re-enable the PK constraint
EXECUTE IMMEDIATE 'ALTER TABLE PASS ADD CONSTRAINT MYTABLE_PK PRIMARY KEY (ID)';
COMMIT;
END;
Here's one problem:
SELECT ID
INTO myid
FROM MYTABLE
WHERE ROWID IN (SELECT MAX(ROWID) FROM MYTABLE)
There is no correlation between ID and ROWID, so you're not getting the maximum current ID, you're just getting the one that happens to be on the row that is furthest from the start of a datafile with a high number.
The code you need is:
SELECT COALESCE(MAX(ID),0)
FROM MYTABLE;
Or better yet, just use a sequence.
No idea why you're dropping the PK either.
Furthermore, when you issue the query:
SELECT APPLICATION_ID INTO appid ...
... that could be for a different row than the one you already got the id for, because a change could have been committed to the table.
Of course another issue is that you can't run two instances of this procedure at the same time either.
For David Aldridge, since he wants to look at code instead of the real reason I posted my question, run this ---
CREATE TABLE YOURSCHEMA.TESTING
(
TEST_ID NVARCHAR2(100) NOT NULL
, TEST_TYPE NVARCHAR2(100) NOT NULL
, CONSTRAINT TEST_PK PRIMARY KEY
(
TEST_ID
)
ENABLE
);
create or replace
PROCEDURE PRC_TESTING_INSERT(
TEST_TYPE IN NVARCHAR2
) AS
testid integer := 0;
BEGIN
-- disable PK constraint so it can be updated
EXECUTE IMMEDIATE 'ALTER TABLE TESTING DROP CONSTRAINT TEST_PK';
COMMIT;
-- assign TEST_ID to testid
SELECT TEST_ID INTO testid FROM TESTING WHERE ROWID IN (SELECT MAX(ROWID) FROM TESTING);
-- increment
testid := testid + 1;
-- use this id to insert with
INSERT INTO TESTING (TEST_ID, TEST_TYPE) VALUES(testid, TEST_TYPE);
COMMIT;
-- re-enable the PK constraint
EXECUTE IMMEDIATE 'ALTER TABLE TESTING ADD CONSTRAINT TEST_PK PRIMARY KEY (TEST_ID)';
COMMIT;
END;
SET serveroutput on;
DECLARE
test_type varchar(100);
BEGIN
test_type := 'dude';
YOURSCHEMA.PRC_TESTING_INSERT(test_type);
-- to verify the variable got set and procedure ran, could do:
--dbms_output.enable;
--dbms_output.put_line(test_type);
END;
Now, because there is no data in the table, the stored procedure will fail with ORA-06512: no data found. If you then try and run it again, you will get ORA-02443: cannot drop constraint - nonexistent constraint, because the EXECUTE IMMEDIATE 'ALTER TABLE TESTING DROP CONSTRAINT TEST_PK'; successfully dropped it, and the procedure never ran the command at the end to re-add it. This is what made me think I needed the commits, but even without them, it still will not complete the whole procedure.
To prove that the procedure DOES run, if given proper data, run this after creating the table, but before creating/running the stored procedure:
INSERT INTO TESTING (TEST_ID, TEST_TYPE)
VALUES ('1', 'hi');
And if you run the proc from a new table (not one with its constraint dropped), it will run fine.
Since mathguy didn't post this as the answer, though I'll credit him for the information...
Answer to why I can't see the duplicates is because the COMMIT does not occur in the procedure when it failed due to a datatype mismatch (which we found was actually in the application's code that sent the variable's values into this procedure, not in the stored procedure, itself). (It's also why I'll mark down anyone that says you don't have to add so many COMMIT lines in this procedure.) The commands were run in the session of the user that starts it - in my case, another session of the same DB user I was logged in with, but started from my application, instead of my SQL Developer session. It also explains why I could do a COMMIT, myself, but it did not affect the application's session - I could not commit any actions ran from another session. Had I ran a COMMIT as an OracleCommand and did an .ExecuteNonQuery on my OracleConnection right after the failure within the catch of my application, I would have seen the rows in SQL Developer without having to do a special query.
So, in short, the only way to see the items was with a direct query using WHERE ID =, find the last ID and increment it, and put it in the query.

PLSQL Trigger to update field value in another table

I am quite new to triggers so obviously I am doing something wrong somewhere. I am working on a report table which will get the data from original tables. For the sake of simplicity, let's say that there is one table and then there is one reporting table.
Original table (orig_tab)
CREATE TABLE orig_tab (
PK NUMBER(8) not null,
NAME VARCHAR2(20) ,
);
INSERT INTO orig_tab (PK, NAME) VALUES (1, 'AAA');
INSERT INTO orig_tab (PK, NAME) VALUES (2, 'BBB');
INSERT INTO orig_tab (PK, NAME) VALUES (3, 'CCC');
Then there is reporting table (rep_tab)
CREATE TABLE rep_tab (
PK NUMBER(8) not null,
NAME VARCHAR2(20) ,
);
Now from user inteface, someone changes the value of record 2. Obviously, this should be treated as an insert (because this record doesn't exist) for reporting table. Then after sometime, the value is changed so it is an update case for reporting table.
Question: How may I make this kind of trigger? I assume that it is a merge statemement case.
This is what I have done:
create or replace trigger vr_reporting_trigger
after update on orig_tab
for each row
begin
MERGE INTO rep_tab d
USING (SELECT pk FROM orig_tab) s
ON (d.pk = s.pk)
WHEN MATCHED THEN
UPDATE SET d.pk = s.pk,
d.name = s.name
WHEN NOT MATCHED THEN
INSERT (d.pk, d.name) VALUES (s.pk, s.name);
end vr_reporting_trigger;
Any suggestions or recommendations that can help me to figure it out? Thanks.
There are some corner cases that aren't handled in previous answers.
What if a matching pk already exists in the reporting table, when a row is inserted. (We wouldn't normally expect this to happen, but consider what would happen if someone deleted a row from the orig_tab, and then inserted it again. (This is the kind of problem that's going to crop up in production, not in test, at the most inopportune time. Better to plan for it now.)
BEGIN
IF inserting THEN
-- insure we avoid duplicate key exception with a NOT EXISTS predicate
INSERT INTO rep_tab(pk,name)
SELECT :new.pk, :new.name FROM DUAL
WHERE NOT EXISTS (SELECT 1 FROM rep_tab WHERE pk = :new.pk);
-- if row already existed, there's a possibility that name does not match
UPDATE rep_tab t SET t.name = :new.name
WHERE t.pk = :new.pk;
-- could improve efficiency of update by checking if update is actually
-- needed using a nullsafe comparison ( t.name <=> :new.name );
ELSIF updating THEN
-- handle updates to pk value (note: the row to be updated may not exist
-- so we need to fallthru to the merge)
IF :new.pk <> :old.pk THEN
UPDATE rep_tab t
SET t.pk = :new.pk
, t.name = :new.name
WHERE t.pk = :old.pk ;
END IF;
MERGE INTO rep_tab d
USING DUAL ON (d.pk = :old.pk)
WHEN MATCHED THEN
UPDATE SET d.name = :new.name
WHEN NOT MATCHED THEN
INSERT (d.pk,d.name) VALUES (:new.pk,:new.name);
END IF;
END;
Merge statement sounds like a plan, except that the trigger won't fire when you're doing the first insert because you've mentioned it's an AFTER UPDATE trigger, not an AFTER INSERT trigger.
Also, the SELECT pk FROM orig_tab will result in Mutating table problem.
Better way would be to define an AFTER INSERT OR UPDATE trigger, combine it with INSERT/UPDATING keywords to handle inserts/updates & use :new/:old to handle new data & old data respectively.
CREATE OR replace TRIGGER vr_reporting_trigger
AFTER INSERT OR UPDATE ON orig_tab
FOR EACH ROW
BEGIN
IF inserting THEN
INSERT INTO rep_tab
(pk,
name)
VALUES (:NEW.pk,
:NEW.name);
ELSIF updating THEN
UPDATE rep_tab r
SET name = :NEW.name
WHERE r.pk = :old.pk;
END IF;
END vr_reporting_trigger;
This is an Extension of Sathya Answer as Jaanna asked about if the record is updating in orrig_tab and no corresponding record in rep_tab then the below logic will cater the request below .Please don't judge me with this answer as this solution belongs to Sathya
CREATE OR replace TRIGGER vr_reporting_trigger
AFTER INSERT OR UPDATE ON orig_tab
FOR EACH ROW
BEGIN
IF inserting THEN
INSERT INTO rep_tab
(pk,
name)
VALUES (:NEW.pk,
:NEW.name);
ELSIF updating THEN
MERGE INTO rep_tab d
USING DUAL
ON (d.pk =:OLD.pk)
WHEN MATCHED THEN
UPDATE SET d.name = :OLD.name
WHEN NOT MATCHED THEN
INSERT (d.pk,d.name) VALUES (:OLD.PK,:NEW.PK );
END IF;
END vr_reporting_trigger;

Update or insert based on if employee exist in table

Do want to create Stored procc which updates or inserts into table based on the condition if current line does not exist in table?
This is what I have come up with so far:
PROCEDURE SP_UPDATE_EMPLOYEE
(
SSN VARCHAR2,
NAME VARCHAR2
)
AS
BEGIN
IF EXISTS(SELECT * FROM tblEMPLOYEE a where a.ssn = SSN)
--what ? just carry on to else
ELSE
INSERT INTO pb_mifid (ssn, NAME)
VALUES (SSN, NAME);
END;
Is this the way to achieve this?
This is quite a common pattern. Depending on what version of Oracle you are running, you could use the merge statement (I am not sure what version it appeared in).
create table test_merge (id integer, c2 varchar2(255));
create unique index test_merge_idx1 on test_merge(id);
merge into test_merge t
using (select 1 id, 'foobar' c2 from dual) s
on (t.id = s.id)
when matched then update set c2 = s.c2
when not matched then insert (id, c2)
values (s.id, s.c2);
Merge is intended to merge data from a source table, but you can fake it for individual rows by selecting the data from dual.
If you cannot use merge, then optimize for the most common case. Will the proc usually not find a record and need to insert it, or will it usually need to update an existing record?
If inserting will be most common, code such as the following is probably best:
begin
insert into t (columns)
values ()
exception
when dup_val_on_index then
update t set cols = values
end;
If update is the most common, then turn the procedure around:
begin
update t set cols = values;
if sql%rowcount = 0 then
-- nothing was updated, so the record doesn't exist, insert it.
insert into t (columns)
values ();
end if;
end;
You should not issue a select to check for the row and make the decision based on the result - that means you will always need to run two SQL statements, when you can get away with one most of the time (or always if you use merge). The less SQL statements you use, the better your code will perform.
BEGIN
INSERT INTO pb_mifid (ssn, NAME)
select SSN, NAME from dual
where not exists(SELECT * FROM tblEMPLOYEE a where a.ssn = SSN);
END;
UPDATE:
Attention, you should name your parameter p_ssn(distinguish to the column SSN ), and the query become:
INSERT INTO pb_mifid (ssn, NAME)
select P_SSN, NAME from dual
where not exists(SELECT * FROM tblEMPLOYEE a where a.ssn = P_SSN);
because this allways exists:
SELECT * FROM tblEMPLOYEE a where a.ssn = SSN

Resources