Oracle trigger on 2 tables : no data found - oracle

Please help,I'm trying to allow/ban insertion into a table called 'vol' that has a foreign key (id_av) from another table 'avion'
allow insertion : if avion.etat = 'disponible'
ban it if it is different from 'disponible'
for that I have created this trigger :
create or replace trigger t
before insert on vol
declare etat VARCHAR(10);
BEGIN
select avion.etat into etat
from vol,etat
where avion.id_av = vol.id_av;
IF(etat <> 'disponible')
THEN
RAISE_APPLICATION_ERROR( -20001, 'insertion imposible');
END IF;
END t;
/
the result : the trigger is created but when I tryed to insert in vol it shows me these errors
I've tryed also with JOIN..ON but didn't really worked out

Perhaps something like this?
create or replace trigger t
before insert on vol
for each row --> edited
declare
etat VARCHAR(10);
BEGIN
-- MAX will prevent NO-DATA-FOUND
-- Also, you don't need join - use :NEW.ID_AV which is equal to currently inserted value
select max(avion.etat)
into etat
from avion
where avion.id_av = :new.id_av;
-- NVL because - if SELECT returns, nothing, you can't compare NULL with 'disponible'
IF nvl(etat, 'x') <> 'disponible'
THEN
RAISE_APPLICATION_ERROR( -20001, 'insertion imposible');
END IF;
END t;
/

Related

Getting error PLS-003036 wrong number or types of argument in call to =

I had written the below trigger
create or replace trigger my_trigger
Before insert or update on table1
referencing new as new old as old
for each row
declare id number;
cursor id_cnt is
select count(*) from table2 where my_id=:new.my_id;
begin
if :new.my_id is null
then RAISE_APPLICATION_ERROR(-001,"MY_ID should nit be null");
elsif id_cnt=0 then
RAISE_APPLICATION_ERROR(-002,"not a valid id ");
else
select new_id from table2 where my_id=:new.my_id;
if lenght(new_id) <5
then
RAISE_APPLICATION_ERROR(-003,"length is very small ");
END IF;
END IF;
END my_trigger;
At if :new.my_id is null i am getting the below error
error PLS-003036 wrong number or types of argument in call to =
There are 2 conditions needs to be checked first condition i need to check my_id is null or not and second condition need to check the length of new_id before that i am checking if that my_id is already existed in table 2 before inserting into table1
You:
want to SELECT ... INTO rather than using a CURSOR
misspelt LENGTH
need to use ' for string literals and not "; and
need to use -20000 to -20999 for user-defined error numbers.
Like this:
create or replace trigger my_trigger
Before insert or update on table1
referencing new as new old as old
for each row
declare
id number;
id_cnt PLS_INTEGER;
v_new_id table2.new_id%TYPE;
begin
IF :new.my_id is null THEN
RAISE_APPLICATION_ERROR(-20001,'MY_ID should nit be null');
END IF;
select count(*)
INTO id_cnt
from table2
where my_id=:new.my_id;
if id_cnt=0 then
RAISE_APPLICATION_ERROR(-20002,'not a valid id');
else
select new_id
INTO v_new_id
from table2
where my_id=:new.my_id;
if length(v_new_id) < 5 then
RAISE_APPLICATION_ERROR(-20003,'length is very small');
END IF;
END IF;
END my_trigger;
/
db<>fiddle here

Update with "with data as" clause and regex do not commit. Why?

Sorry for the delay! I've taken a workload greater then i can handle these couple weeks.
Ok, lets clarify things up!
There is a proprietary software running on top of it and
I do not have de ability to change this software! Actually it allows me to do a few things.
In this specific case I can not create a relation 1>N, I can not create a new table! What I do can is create fields.
So, how do i customize things? Through the database! Using triggers, functions and procedures. I know its a bad "work around" but it's what i got and works flawlessly 99% of times. Any exception my code throws the application shows on the screen!
The problem is actually the update not working.
You ask:
Why you're looping when you're forcing there to only be one iteration (unless P_QTDLINHAS can be < 1 I suppose?)
R: Couse the user can select multiple lines in the application but I dont want them to do it. So a throw an error on the screen.
Why you have nested begin/end blocks.
R: Couse I may have to throw exceptions, I got used to write begin Statements Exception some message end.
Sample data:
CREATE TABLE SAMPLE_DATA
(
PKNUMBER NUMBER NOT NULL
, DESCRIPTION VARCHAR2(20)
, GROUPTOCHANGE VARCHAR2(100)
, STATUS VARCHAR2(1 BYTE)
, CONSTRAINT SAMPLE_DATA_PK PRIMARY KEY
(
PKNUMBER
)
ENABLE
);
INSERT INTO sample_data VALUES (1000,'ORDER1',NULL,NULL);
INSERT INTO sample_data VALUES (2000,'ORDER2',NULL,NULL);
INSERT INTO sample_data VALUES (3000,'ORDER3',NULL,NULL);
INSERT INTO sample_data VALUES (4000,'ORDER4','1000,2000,30001',NULL);
In this case the field GROUPTOCHANGE will be filled by the user like this "2108,8090,8843". Each number represents a PKNUMBER in same table "SAMPLE_DATA".
yes i know! the user can type something wrong.. let's ignore this for now!
The field STATUS will eventually be updated to 'C','L','R' OR NULL. When this happens I Need this logic to be executed:
IF OLD.STATUS <> NEW.STATUS AND GROUPTOCHANGE IS NOT NULL THEN
UPDATE SAMPLE_DATA SP
SET SP.STATUS = :NEW.STATUS
WHERE SP.PKNUMBER IN (:NEW.GROUPTOCHAGE)
AND PS.GROUPTOCHANGE IS NULL;
END IF;
Dispite the bad design is it possible to do?
Thank for any help!!
Here what I've done so far:
create or replace PROCEDURE "AD_LIBERA_FRETES_FILHOS"(
P_CODUSU NUMBER,
P_IDSESSAO VARCHAR2,
P_QTDLINHAS NUMBER,
P_MENSAGEM OUT VARCHAR2)
AS
P_NUNOTA NUMBER(10);
P_CONTROLE VARCHAR(100);
P_PEDIDOS VARCHAR(100);
P_STATUS VARCHAR(100);
BEGIN
-- avoid more than 1 at a time
IF (P_QTDLINHAS > 1) THEN
RAISE_APPLICATION_ERROR(-20000, 'SELECIONE APENAS UM PEDIDO PARA EXECUTAR ESTA AÇÃO.');
END IF;
FOR I IN 1..P_QTDLINHAS LOOP
--extract param from session
P_NUNOTA := ACT_INT_FIELD(P_IDSESSAO, I, 'PKNUMBER');
P_STATUS := ACT_TXT_FIELD(P_IDSESSAO, I, 'STATUS');
--verify typed text should be "84090,89830,83393..."
BEGIN
SELECT REGEXP_REPLACE(CAB.GROUPTOCHANGE, '[0-9-, ]', ''),
CAB.GROUPTOCHANGE
INTO P_CONTROLE,
P_PEDIDOS
FROM SAMPLE_DATA CAB
WHERE CAB.PKNUMBER = P_NUNOTA;
END;
IF (P_CONTROLE IS NOT NULL) THEN
RAISE_APPLICATION_ERROR(-20000, '<B> SOMETHING WRONG !</B>');
ELSE
--perform de update (not working)
BEGIN
UPDATE SAMPLE_DATA C
SET C.STATUS = P_STATUS
WHERE
C.GROUPTOCHANGE IS NULL AND
C.PKNUMBER IN
(WITH DATA AS
(SELECT CAB.GROUPTOCHANGE STR
FROM SAMPLE_DATA CAB
WHERE CAB.PKNUMBER = P_NUNOTA )
SELECT TRIM(REGEXP_SUBSTR(STR, '[^,]+', 1, LEVEL)) STR
FROM DATA CONNECT BY INSTR(STR, ',', 1, LEVEL - 1) > 0);
END;
END IF;
END LOOP;
--mgs to show
P_MENSAGEM := 'DONE!! CHECK -> '||P_PEDIDOS;
END;

Oracle Compound Trigger Mutation Table

I'm trying to create a compound trigger to avoid the mutation problem.
I've a table and a python's procedure that perfoms a transaction insert. The table has n fields.
What I´m trying to do is when a value of one of those fields is negative, then do not perform the operation , and insert the value from the previous record of the field (prior to insert) of the table. Another concern is that one of the fields is and id, to distinguish between sites.
For no, this is the code I've, Considering only one field (KWHGEN):
CREATE OR REPLACE TRIGGER "CIRCU3".D_measures_TP_test
--FOR INSERT OR UPDATE ON T_MEASURES_TP_NEW
FOR INSERT ON T_MEASURES_TP_NEW
COMPOUND TRIGGER
VAL_KWHGEN NUMBER(21,2);
VAL_autoin NUMBER (19,0);
AFTER EACH ROW IS
BEGIN
SELECT autoin, KWHGEN INTO VAL_ID_MED, VAL_KWHGEN FROM
(SELECT *
FROM T_measures_TP_NEW WHERE ID_site = :NEW.ID_site
ORDER BY TIMESTAMP DESC)
WHERE ROWNUM = 1;
IF :NEW.KWHGEN <0
THEN UPDATE T_MEASURES_TP_NEW SET KWHGEN = VAL_KWHGEN WHERE autoin = VAL_autoin;
END IF;
END AFTER EACH ROW;
END D_MEASURES_TP_test;
But the mutation error is following me ;-)
You have created trigger on T_MEASURES_TP_NEW and then updating same table T_MEASURES_TP_NEW within trigger. This will again call your trigger.
If the first select in trigger again returns negative value in VAL_KWHGEN then mutating error will follow you.
You defined only an AFTER EACH block, nothing else. This is the same as creating a row-level trigger (i.e. using FOR EACH ROW)
It must be like this (not tested):
CREATE OR REPLACE TRIGGER "CIRCU3".D_measures_TP_test
FOR INSERT ON T_MEASURES_TP_NEW
COMPOUND TRIGGER
VAL_KWHGEN NUMBER(21,2);
VAL_autoin NUMBER (19,0);
TYPE RowIdTableType IS TABLE OF ROWID;
TYPE KWHGENTableType IS TABLE OF T_MEASURES_TP_NEW.KWHGEN%TYPE;
RowIdTable RowIdTableType;
KWHGENTable KWHGENTableType;
BEFORE STATEMENT IS
BEGIN
RowIdTable := RowIdTable();
KWHGENTable := KWHGENTableType();
END BEFORE STATEMENT;
BEFORE EACH ROW IS
BEGIN
RowIdTable.EXTEND;
RowIdTable(RowIdTable.LAST) := :NEW.ROWID;
KWHGENTable.EXTEND;
KWHGENTable(RowIdTable.LAST) := :NEW.KWHGEN;
END BEFORE EACH ROW;
AFTER STATEMENT IS
BEGIN
FOR i IN RowIdTable.FIRST..RowIdTable.LAST LOOP
SELECT
DISTINCT MIN(autoin) OVER (ORDER BY TIMESTAMP DESC),
DISTINCT MIN(KWHGEN) OVER (ORDER BY TIMESTAMP DESC)
INTO VAL_ID_MED, VAL_KWHGEN
FROM T_measures_TP_NEW
WHERE ROWID = RowIdTable(i);
IF KWHGENTable(i) < 0
THEN UPDATE T_MEASURES_TP_NEW
SET KWHGEN = VAL_KWHGEN
WHERE autoin = VAL_autoin;
END IF;
END LOOP;
END AFTER STATEMENT;
END;
/
OK, I do have a solution:
1.- Create a package where record the new insert data (BEFORE)
create or replace PACKAGE PCK_MEDIDAS_TP AS
TYPE DATOS_MEDIDAS_TP IS RECORD(
v_id_sede NUMBER (10,0),
v_id_med NUMBER (10,0),
v_kwhGEN NUMBER (21,2),
v_timestamp TIMESTAMP
);
type T_MEDTP is table of DATOS_MEDIDAS_TP index by binary_integer;
tabla_medidas_tp T_MEDTP;
END PCK_MEDIDAS_TP;
2.- Create a procedure each row (BEFORE) to read the new insert data and then record them into de package's table.
create or replace TRIGGER "CIRCU3".D_MEDIDAS_TP_test
BEFORE INSERT ON T_MEDIDAS_TP_NEW
FOR EACH ROW
DECLARE
Indice binary_integer;
BEGIN
--AUTOINCREMENTAL DEL CAMPO ID_MEDIDAS
SELECT T_MEDIDAS_TP_NEW_SEQ.NEXTVAL INTO :NEW.id_MEDIDAS_OLD FROM DUAL;
Indice:= PCK_MEDIDAS_TP.tabla_medidas_tp.COUNT+1;
PCK_MEDIDAS_TP.tabla_medidas_tp(Indice).v_id_sede := :NEW.ID_SEDE;
PCK_MEDIDAS_TP.tabla_medidas_tp(Indice).v_id_med := :NEW.ID_MEDIDAS;
PCK_MEDIDAS_TP.tabla_medidas_tp(Indice).v_kwhGEN := :NEW.KWHGEN;
PCK_MEDIDAS_TP.tabla_medidas_tp(Indice).v_timestamp := :NEW.TIMESTAMP;
IF :NEW.KWHGEN <0 THEN
DBMS_OUTPUT.put_line('first trigger:' ||:NEW.ID_MEDIDAS||','||:NEW.ID_SEDE||','||:NEW.TIMESTAMP);
-- INSERT INTO TEST_TRIGGER VALUES ('100', :NEW.KWHGEN, SYSDATE);
--ELSE DBMS_OUTPUT.PUT_LINE('¿?');
END IF;
END;
3.- Create a statement procedure (AFTER) where you can check your condition, in my case if kwhgen <0. If is true, I'll read the previous record in the original tbale and update the insert record with taht value.
create or replace TRIGGER D_MEDIDAS_TP_TEST_STATEMENT
AFTER INSERT ON T_MEDIDAS_TP_NEW
DECLARE
Indice binary_integer;
s_id_sede NUMBER (10,0);
s_id_med NUMBER (10,0);
s_kwhGEN NUMBER (21,2);
s_timestamp TIMESTAMP;
BEGIN
FOR Indice in 1..PCK_MEDIDAS_TP.tabla_medidas_tp.count LOOP
DBMS_OUTPUT.put_line('second trigger: kwhgen: '||PCK_MEDIDAS_TP.tabla_medidas_tp(Indice).v_kwhGEN||', id_sede: '||PCK_MEDIDAS_TP.tabla_medidas_tp(Indice).v_id_sede);
IF PCK_MEDIDAS_TP.tabla_medidas_tp(Indice).v_kwhGEN <0 THEN
DBMS_OUTPUT.put_line('second trigger: v_kwhGEN is negative');
SELECT prev_KWHGEN INTO s_kwhgen
from(
SELECT LEAD (KWHGEN,1) over (ORDER BY id_medidas desc) as prev_KWHGEN
FROM T_MEDIDAS_TP_NEW WHERE ID_SEDE = PCK_MEDIDAS_TP.tabla_medidas_tp(Indice).v_id_sede
ORDER BY id_medidas DESC) where rownum =1;
INSERT INTO TEST_TRIGGER VALUES ('100', '5555', SYSDATE);
DBMS_OUTPUT.put_line('second trigger. KWHGEN: '||s_kwhGEN);
DBMS_OUTPUT.put_line('UPDATE');
UPDATE T_MEDIDAS_TP_NEW SET KWHGEN = S_KWHGEN WHERE ID_MEDIDAS = PCK_MEDIDAS_TP.tabla_medidas_tp(Indice).v_id_med;
else DBMS_OUTPUT.put_line('¿?');
END IF;
END LOOP;
PCK_MEDIDAS_TP.tabla_medidas_tp.delete; -- vaciamos la tabla
END;

Oracle trigger checks previous record before insert/update

I would like to create a trigger that prevents a student from enrolling into a new module if he has any outstanding bills.
studID studNRIC paymentStatus
-------------------------------------
200 F7654672F Non Payment
it would reject the following statement:
INSERT INTO student(studID, studNRIC, paymentStatus)
VALUES (201, 'F7654672F', 'Good');
I've came out with the following trigger but I'm still able to insert a new student.
set define off;
CREATE OR REPLACE TRIGGER reject_new_account
AFTER INSERT OR UPDATE ON Student
FOR EACH ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
totover NUMBER(3);
BEGIN
SELECT COUNT (*)
INTO totover
FROM Student
WHERE :NEW.nric = student.nric
AND :NEW.paymentStatus = 'Non Payment';
IF totover > 0 THEN
RAISE_APPLICATION_ERROR ( -20002,
'Student' || :NEW.nric ||
' has outstanding bills' );
END IF;
END;
/
there's seems to be a problem with line 13 AND :NEW.paymentStatus = 'Non Payment';
so how do I go about doing this?
table structure
CREATE TABLE Student(
studID INTEGER NOT NULL,
firstName CHAR(25) NULL,
lastName CHAR(25) NULL,
NRIC CHAR(9) NOT NULL,
paymentStatus CHAR(25) Default 'Good',
CONSTRAINT stud_Pkey PRIMARY KEY (studID),
CONSTRAINT studPaymentStatus_type CHECK (PaymentStatus IN ('Late Payment', 'Non Payment', 'Good'))
);
There are a couple of issues with this trigger.
First if you want to prevent an INSERT you need to use a BEFORE trigger. An AFTER trigger will fire after the insert has successfully completed, by which point it is too late to stop the insert.
Secondly, I'm unsure about what you are trying to achieve with your SQL statement. Since the trigger is attached to the customer table you don't need to do a select on the customer table to access the record data. You can just do:
IF :NEW.badstatus = 'Non Payment'
THEN
RAISE_APPLICATION_ERROR ( -20002,
'Employee ' || :NEW.nric ||
' already has 5 readings' );
END IF;
You probably don't want to check if the new record has a bad status, you want to check if the existing customer records have a bad status. As James mentioned, a BEFORE trigger probably makes more sense, but that depends on what you are trying to do, whether the insert gets rollback or not by the caller, etc.
CREATE OR REPLACE TRIGGER reject_new_account
AFTER INSERT OR UPDATE ON Customer
FOR EACH ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
totover NUMBER(3);
BEGIN
SELECT COUNT (*)
INTO totover
FROM Customer c
WHERE c.nric = :NEW.nric
AND c.badstatus = 'Non Payment';
IF totover > 0 THEN
RAISE_APPLICATION_ERROR ( -20002,
'Employee ' || :NEW.nric ||
' already has 5 readings' );
END IF;
END;
/
I've removed the PRAGMA AUTONOMOUS_TRANSACTION; for you and replace it with an exception to handle the issue, can you try if it works.
set define off;
CREATE OR REPLACE TRIGGER reject_new_account
BEFORE INSERT OR UPDATE ON CUSTOMER
FOR EACH ROW
DECLARE
totover CHAR(100);
BEGIN
SELECT distinct badStatus
INTO totover
FROM customer
WHERE :NEW.nric = CUSTOMER.nric;
IF totover = 'Non Payment' THEN
RAISE_APPLICATION_ERROR ( -20003,
'Customer ' || :NEW.firstName||''||:NEW.lastName ||
' cannot create new account due to bad payment' );
END IF;
EXCEPTION
WHEN NO_DATA_FOUND
THEN DBMS_OUTPUT.PUT_LINE(TO_CHAR(SQLERRM(-20299)));
END;
/
As I assume you have discovered, you cannot select from the same table that a row-level trigger is defined against; it causes a table mutating exception. You have attempted to get round this by adding the autonomous transaction pragma. Unfortunately, although this works, it is just covering up your mistake in methodology.
In order to properly create this validation using a trigger a procedure should be created to obtain user-specified locks so the validation can be correctly serialized in a multi-user environment.
PROCEDURE request_lock
(p_lockname IN VARCHAR2
,p_lockmode IN INTEGER DEFAULT dbms_lock.x_mode
,p_timeout IN INTEGER DEFAULT 60
,p_release_on_commit IN BOOLEAN DEFAULT TRUE
,p_expiration_secs IN INTEGER DEFAULT 600)
IS
-- dbms_lock.allocate_unique issues implicit commit, so place in its own
-- transaction so it does not affect the caller
PRAGMA AUTONOMOUS_TRANSACTION;
l_lockhandle VARCHAR2(128);
l_return NUMBER;
BEGIN
dbms_lock.allocate_unique
(lockname => p_lockname
,lockhandle => p_lockhandle
,expiration_secs => p_expiration_secs);
l_return := dbms_lock.request
(lockhandle => l_lockhandle
,lockmode => p_lockmode
,timeout => p_timeout
,release_on_commit => p_release_on_commit);
IF (l_return = 1) THEN
raise_application_error(-20001, 'dbms_lock.request Timeout');
ELSIF (l_return = 2) THEN
raise_application_error(-20001, 'dbms_lock.request Deadlock');
ELSIF (l_return = 3) THEN
raise_application_error(-20001, 'dbms_lock.request Parameter Error');
ELSIF (l_return = 5) THEN
raise_application_error(-20001, 'dbms_lock.request Illegal Lock Handle');
ELSIF (l_return not in (0,4)) THEN
raise_application_error(-20001, 'dbms_lock.request Unknown Return Value ' || l_return);
END IF;
-- Must COMMIT an autonomous transaction
COMMIT;
END request_lock;
This procedure can then be used in a compound trigger (assuming at least Oracle 11, this will need to be split into individual triggers in earlier versions)
CREATE OR REPLACE TRIGGER reject_new_account
FOR INSERT OR UPDATE ON student
COMPOUND TRIGGER
-- Table to hold identifiers of inserted/updated students
g_studIDs sys.odcinumberlist;
BEFORE STATEMENT
IS
BEGIN
-- Reset the internal student table
g_studIDs := g_studIDs();
END BEFORE STATEMENT;
AFTER EACH ROW
IS
BEGIN
-- Store the inserted/updated students; the payment status may be updated
-- without checking the constraint
IF ( INSERTING
OR ( UPDATING
AND ( :new.studID <> :old.studID
OR :new.NRIC <> :old.NRIC)))
THEN
g_studIDs.EXTEND;
g_studIDs(g_studIDs.LAST) := :new.studID;
END IF;
END AFTER EACH ROW;
AFTER STATEMENT
IS
CURSOR csr_students
IS
SELECT sdt.studID
, sdt.NRIC
FROM TABLE(g_studIDs) sid
INNER JOIN student sdt
ON (sdt.studID = sid.column_value)
ORDER BY sdt.NRIC;
CURSOR csr_constraint_violations
(p_studID student.studID%TYPE
,p_NRIC student.studNRIC%TYPE)
IS
SELECT NULL
FROM student sdt
WHERE sdt.NRIC = p_NRIC
AND sdt.paymentStatus = 'Bad Payment'
AND sdt.studID <> p_studID;
r_constraint_violation csr_constraint_violations%ROWTYPE;
BEGIN
-- Check if for any inserted/updated student there exists another record for
-- the same NRIC with a Bad Payment status. Serialise the constraint for each
-- NRIC so concurrent transactions do not affect each other
FOR r_student IN csr_students LOOP
request_lock('REJECT_NEW_ACCOUNT_' || r_student.NRIC);
OPEN csr_constraint_violations(r_student.studID, r_student.NRIC);
FETCH csr_constraint_violations INTO r_constraint_violation;
IF csr_constraint_violations%FOUND THEN
CLOSE csr_constraint_violations;
raise_application_error(-20001, 'Student ' || r_student.NRIC || ' has Bad Payment status');
ELSE
CLOSE csr_constraint_violations;
END IF;
END LOOP;
END AFTER STATEMENT;
END;

Fetching a single column into array

I have a table which has column_id of type varchar2. This table may contain 1, 0 or multiple rows. My business logic depends on each case.
I am trying to fetch the column into an array but i am getting weird error (given my limited knowledge of pl/sql)
TYPE t_col_id IS TABLE OF TEST_TABLE.COLUMN_ID%TYPE INDEX BY BINARY_INTEGER;
AR_COL_ID T_COL_ID;
Then i am trying to fetch data into this array
SELECT COLUMN_ID INTO AR_SIM_ID FROM TEST_TABLE WHERE COLUMN_ID = 1;
and i am getting this error
Error(7,3): PL/SQL: SQL Statement ignored
Error(7,25): PLS-00597: expression 'AR_SIM_ID' in the INTO list is of wrong type
Error(7,35): PL/SQL: ORA-00904: : invalid identifier
Is there something that i am missing? My original code would use this array as
BEGIN
-- FETCH ARRAY QUERY
IF (AR_SIM_ID.LENGTH = 0) THEN
-- BUSINESS LOGIC 1
ELSE
-- BUSINESS LOGIC 2
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- BUSINESS LOGIC 3
END;
Instead of using INTO you must use BULK COLLECT INTO:
DECLARE
TYPE t_col_id IS TABLE OF TEST_TABLE.COLUMN_ID%TYPE INDEX BY BINARY_INTEGER;
AR_COL_ID T_COL_ID;
BEGIN
SELECT COLUMN_ID
BULK COLLECT INTO AR_SIM_ID
FROM TEST_TABLE
WHERE COLUMN_ID = 1;
IF AR_SIM_ID.LENGTH = 0 THEN
-- BUSINESS LOGIC 1
ELSE
-- BUSINESS LOGIC 2
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- BUSINESS LOGIC 3
END;
But couldn't you just use COUNT for this scenario?
DECLARE
numRows NUMBER;
BEGIN
SELECT COUNT(COLUMN_ID)
INTO numRows
FROM TEST_TABLE
WHERE COLUMN_ID = 1;
IF numRows = 0 THEN
-- BUSINESS LOGIC 1
ELSE
-- BUSINESS LOGIC 2, 3, etc...
END IF;
END;
a. If you want to get all the rows to plsq table - you need bulk collect
b. If you just want to know how many rows there are in the table you should do:
SELECT count(*) INTO v_count from TEST_TABLE where COLUMN_ID = 1;
c. I don't like using the EXCEPTION scope as part of the program's workflow-
I'd do:
IF v_count = 0 THEN
-- BUSINESS LOGIC 1
ELSIF v_count = 1 THEN
-- BUSINESS LOGIC 2
ELSE
-- BUSINESS LOGIC 3
END IF;

Resources