Erorr in create Package Body - oracle

I Created Table called Class then its Package called PKG_Class also created
it is OK.
But the execution of (Backage Body) give me erorr "Package Body Created with Compilation Erorr"
create table Class
(
Class_id number(10) primary key,
Tybe number(5),
Class_name varchar2(10),
Max number(20)
);
CREATE OR REPLACE PACKAGE PKG_Class IS
PROCEDURE ClassAdd(
ID_C in Class.Class_id%Type ,
C_Type in Class.Tybe%Type ,
C_Name in Class.Class_name%Type ,
C_Max in Class.Max%Type );
PROCEDURE ClassUpdate(
ID_C in Class.Class_id%Type ,
C_Type in Class.Tybe%Type ,
C_Name in Class.Class_name%Type ,
C_Max in Class.Max%Type );
PROCEDURE ClassDelete(
ID_C in Class.Class_id%Type );
PROCEDURE ClassFind(
ID_C in Class.Class_id%Type ,
C_Type out Class.Tybe%Type ,
C_Name out Class.Class_name%Type ,
C_Max out Class.Max%Type );
FUNCTION FindClass(
ID_C in Class.Class_id%Type
) RETURN Class%ROWTYPE ;
FUNCTION Find(
ID_C in Class.Class_id%Type ,
C_Type out Class.Tybe%Type ,
C_Name out Class.Class_name%Type ,
C_Max out Class.Max%Type
) RETURN BOOLEAN;
END PKG_Class;
////////////////////////////////
CREATE OR REPLACE PACKAGE Body PKG_Class IS
PROCEDURE ClassAdd(
ID_C in Class.Class_id%Type ,
C_Type in Class.Tybe%Type ,
C_Name in Class.Class_name%Type ,
C_Max in Class.Max%Type )
IS
BEGIN
insert into Class
values(ID_C, C_Type, C_Name, C_Max );
Commit;
END ClassAdd;
PROCEDURE ClassUpdate(
ID_C in Class.Class_id%Type ,
C_Type in Class.Tybe%Type ,
C_Name in Class.Class_name%Type ,
C_Max in Class.Max%Type )
IS
BEGIN
Update Class set C_Type = Tybe ,
C_Name = Class_name ,
Width_Pool = Width_P ,
C_Max = Max
WHERE ID_C = Class_id ;
Commit;
END ClassUpdate;
PROCEDURE ClassDelete(
ID_C in Class.Class_id%Type )
IS
BEGIN
DELETE FROM Class WHERE
ID_C = Class_id;
Commit;
END ClassDelete;
PROCEDURE ClassFind(
ID_C in Class.Class_id%Type ,
C_Type out Class.Tybe%Type ,
C_Name out Class.Class_name%Type ,
C_Max out Class.Max%Type )
IS
BEGIN
SELECT Tybe, Class_name, Max
INTO C_Type, C_Name, C_Max
FROM Class
WHERE Class_id = ID_C;
END ClassFind;
FUNCTION Find(
ID_C in Class.Class_id%Type ,
C_Type out Class.Tybe%Type ,
C_Name out Class.Class_name%Type ,
C_Max out Class.Max%Type
) RETURN BOOLEAN
IS
BEGIN
SELECT Tybe, Class_name, Max
INTO C_Type, C_Name, C_Max
FROM Class
WHERE Class_id = ID_C;
RETURN TRUE;
Exception
WHEN NO_DATA_FOUND THEN
RETURN False;
END Find;
FUNCTION FindClass(
ID_C in Class.Class_id%Type
) RETURN Class%ROWTYPE
IS
RECORD_C Class%ROWTYPE;
BEGIN
SELECT *
Into RECORD_C
From Class
Where ID_C=Class_id;
RETURN RECORD_C;
END FindClass;
END PKG_Class;

In the body of procedure ClassUpdate, you update the values in the columns of table Class. In the update statement, you update the column C_Max to the value Max. That's the wrong way. The name of the column in the table is Max, and you want to update it to the value passed as an argument, C_Max. An assignment is one-directional: the right-hand side is assigned to the left-hand side. You can't turn around the "equality" since it's not really an equality, it only uses the same symbol.
If I am reading the rest of your code correctly, you are making similar mistakes for the other columns.
As an aside, max is the name of an aggregate function. While it is not reserved (which means you may use it as a column name), you still shouldn't use it as a column name; you will create confusion for yourself and for others who may need to maintain your code. And even if this is just for school (meaning, no one will ever maintain this code), it is best to form good habits.

Related

'Member of' in Oracle

I am trying to use member of in Oracle.
I am able to use this when table type is of number or any other data type. Below is the code for this:
declare
type t is table of number;
lt t;
begin
select channel_key
bulk collect into lt
from dim_channels;
if 22 member of lt then
dbms_output.put_line('ss');
end if;
end;
How do I use member of when the table is based on a record as in the code below.
declare
type rt is record
(
channel_key number(10),
channel_code varchar2(100)
);
type t is table of rt;
lt t;
lrt rt;
begin
select channel_key, channel_code
bulk collect into lt
from dim_channels;
end;
This won't work with plain local PL/SQL record types. To include more attributes you will need an object type with a MAP or ORDER function:
create or replace type demo_ot as object
( channel_key integer
, channel_code varchar2(30)
, map member function demo_map return varchar2 )
/
create or replace type body demo_ot as
map member function demo_map return varchar2
is
begin
return self.channel_key || '<#>' || self.channel_code;
end demo_map;
end;
/
declare
type demo_t is table of demo_ot; -- You would normally create this globally in SQL
my_set demo_t;
my_object demo_ot;
begin
select demo_ot(ckey, ccode)
bulk collect into my_set
from ( select 1 as ckey, 'One' as ccode from dual
union all
select 2 as ckey, 'Two' as ccode from dual );
my_object := demo_ot(2, 'Two');
if my_object member of my_set then
dbms_output.put_line('Member found');
else
dbms_output.put_line('Member not found');
end if;
end;
/
I created below code to test it
create or replace PROCEDURE P_MEMBER_OF_TEST(p_fname IN VARCHAR2,
p_lname in varchar2)
AS
type type_rec is record
(
first_name employees.first_name%type,
last_name employees.last_name%type
);
TYPE T_TAB_TYPE IS TABLE OF type_rec;
T_TAB T_TAB_TYPE;
t_rec type_rec;
i int;
BEGIN
t_rec.first_name := p_fname;
t_rec.last_name := p_lname;
SELECT FIRST_NAME,last_name bulk collect INTO T_TAB FROM
EMPLOYEES;
dbms_output.put_line(t_rec.first_name || ',' || t_rec.last_name);
IF t_rec MEMBER OF T_TAB THEN
DBMS_OUTPUT.PUT_LINE ('YES');
ELSE
DBMS_OUTPUT.PUT_LINE('NO');
END IF;
END;
It compiled with no issues however when i execute it i get error that my connection has been reset , when i comment the if-else-end if block . it gets executed. Can you also suggest what is the problem in code #William Robertson

If condition is giving Error in Oracle Package

I'm new to oracle database i created a PL/SQL package with some if else statements. It contains three procedures respectively for insert, update and delete. That is giving me error. Can anyone help me sorting out this..
create or replace
PACKAGE body USERS_tapi
IS
-- insert
PROCEDURE ins(
p_FIRSTNAME IN USERS.FIRSTNAME%type ,
p_ADDRESS IN USERS.ADDRESS%type ,
p_ROLEID IN USERS.ROLEID%type DEFAULT NULL ,
p_USERNAME IN USERS.USERNAME%type ,
p_PASSWORD IN USERS.PASSWORD%type ,
p_USERID IN USERS.USERID%type ,
p_LASTNAME IN USERS.LASTNAME%type,
O_val OUT NUMBER
)
IS
BEGIN
select count(*) as cnt from USERS where username=p_USERNAME and password=p_PASSWORD;
if cnt = 0 then
INSERT INTO USERS(
FIRSTNAME ,
ADDRESS ,
ROLEID ,
USERNAME ,
PASSWORD ,
USERID ,
LASTNAME
)
VALUES
(
p_FIRSTNAME ,
p_ADDRESS ,
p_ROLEID ,
p_USERNAME ,
p_PASSWORD ,
USERS_SEQ.nextval ,
p_LASTNAME
);
O_val:=0;
else if cnt > 0 then
O_val:=1;
else
O_val:=2;
END IF;
return O_val;
END;
-- update
PROCEDURE upd
(
p_FIRSTNAME IN USERS.FIRSTNAME%type ,
p_ADDRESS IN USERS.ADDRESS%type ,
p_ROLEID IN USERS.ROLEID%type DEFAULT NULL ,
p_USERNAME IN USERS.USERNAME%type ,
p_PASSWORD IN USERS.PASSWORD%type ,
p_USERID IN USERS.USERID%type ,
p_LASTNAME IN USERS.LASTNAME%type
)
IS
BEGIN
UPDATE USERS
SET FIRSTNAME = p_FIRSTNAME ,
ADDRESS = p_ADDRESS ,
ROLEID = p_ROLEID ,
USERNAME = p_USERNAME ,
PASSWORD = p_PASSWORD ,
LASTNAME = p_LASTNAME
WHERE USERID = p_USERID;
END;
-- del
PROCEDURE del(
p_USERID IN USERS.USERID%type )
IS
BEGIN
DELETE FROM USERS WHERE USERID = p_USERID;
END;
END USERS_tapi;
Use elsif instead of else if in the following line :
else if cnt > 0 then
The former is just one more case in your condition block.
The latter syntax is starting a nested condition block in the else part of the first condition block. That nested block would need its own END IF to work.
You could also use a CASE statement :
CASE
WHEN cnt = 0 then
-- insert statement
O_val:=0;
WHEN cnt > 0 then
O_val:=1;
else
O_val:=2;
END CASE;

Why does this get-or-create function never seem to insert?

I'm writing a function for batch-importing data into our org-chart. It seems to work fine for retrieving entries that already exists, but when an entry does not already exist, and it's supposed to insert, commit, and return the result of a re-attempt (so as to get the auto-generated ID), it always returns NULL.
I'm sure I'm doing something wrong here, but what? Help appreciated.
Note: there's a before-insert trigger that fills in DEPT_ID if it's not specified. Works fine if the insert statement is executed by hand.
CREATE TABLE DEPTS
(
"DEPT_ID" VARCHAR2(10 BYTE),
"HEADER_ID" VARCHAR2(10 BYTE),
"COMMENTS" VARCHAR2(100 BYTE),
"CATEGORY" VARCHAR2(2 BYTE)
);
CREATE OR REPLACE FUNCTION get_or_make_unit(
, in_cat VARCHAR2
, in_cmt VARCHAR2
, in_hdr VARCHAR2 DEFAULT NULL
) RETURN VARCHAR2 AS
unit_id VARCHAR2(10);
BEGIN
unit_id := NULL;
IF in_hdr IS NULL THEN
SELECT dept_id
INTO unit_id
FROM depts unit
WHERE unit.category = in_cat
AND unit.comments = in_cmt
AND unit.header_id IS NULL;
ELSE
SELECT dept_id
INTO unit_id
FROM depts unit
WHERE unit.category = in_cat
AND unit.comments = in_cmt
AND unit.header_id = in_hdr;
END IF;
IF unit_id IS NULL THEN
DBMS_OUTPUT.PUT_LINE('Inserting!');
INSERT INTO depts (
header_id
, comments
, category
) VALUES (
in_hdr
, in_cmt
, in_cat);
COMMIT;
unit_id := get_or_make_unit(in_cat, in_cmt, in_hdr);
RETURN unit_id;
ELSE
DBMS_OUTPUT.PUT_LINE('Not inserting!');
RETURN unit_id;
END IF;
END get_or_make_unit;
And the trigger:
CREATE OR REPLACE TRIGGER HRD.DEPTS_BIR
BEFORE INSERT
ON HRD.DEPTS
FOR EACH ROW
DECLARE
JML NUMBER;
BEGIN
SELECT SEQ_DEPT_ID.NEXTVAL INTO JML FROM DUAL;
:NEW.DEPT_ID:='D'||to_char(JML);
END DEPTS_BIR;
Examples
This works:
INSERT INTO depts (
header_id
, comments
, category
) VALUES (
'D532'
, 'ACCOUNTING'
, '2');
COMMIT;
SELECT get_or_make_unit('2', 'ACCOUNTING', 'D532') FROM DUAL;
=> 'D533'
This does not:
SELECT get_or_make_unit('2', 'NEW DEPT', 'D532') FROM DUAL;
=> NULL
Instead of:
INSERT INTO depts (
header_id
, comments
, category
) VALUES (
in_hdr
, in_cmt
, in_cat);
COMMIT;
unit_id := get_or_make_unit(in_cat, in_cmt, in_hdr);
use RETURNING INTO:
INSERT INTO depts (
header_id
, comments
, category
) VALUES (
in_hdr
, in_cmt
, in_cat) RETURNING dept_id INTO unit_id;
COMMIT;
I think recursive call in not the best approach, but if you are strict of using it, then please post definition of mentioned before-insert trigger.
UPDATE: You cannot call functions containing DML operations from SQL statement. Please see this answer for details. Example of correct call:
DECLARE
unit_id varchar2(32);
BEGIN
unit_id := get_or_make_unit('2', 'NEW DEPT', 'D532');
dbms_output.put_line(unit_id);
END;
UPDATE2: Also you need to catch NO_DATA_FOUND exception that is raised when you call your function with not existent combination. Below is example:
CREATE OR REPLACE FUNCTION get_or_make_unit(in_cat VARCHAR2,
in_cmt VARCHAR2,
in_hdr VARCHAR2 DEFAULT NULL)
RETURN VARCHAR2 AS
unit_id VARCHAR2(10);
BEGIN
unit_id := NULL;
IF in_hdr IS NULL THEN
SELECT dept_id
INTO unit_id
FROM depts unit
WHERE unit.category = in_cat
AND unit.comments = in_cmt
AND unit.header_id IS NULL;
ELSE
SELECT dept_id
INTO unit_id
FROM depts unit
WHERE unit.category = in_cat
AND unit.comments = in_cmt
AND unit.header_id = in_hdr;
END IF;
DBMS_OUTPUT.PUT_LINE('Not inserting!');
RETURN unit_id;
exception
when NO_DATA_FOUND then
DBMS_OUTPUT.PUT_LINE('Inserting!');
INSERT INTO depts
(header_id, comments, category)
VALUES
(in_hdr, in_cmt, in_cat)
returning dept_id into unit_id;
COMMIT;
RETURN unit_id;
END get_or_make_unit;
Your call to get_or_make_unit appears to be missing the first parameter.
INSERT INTO depts (
header_id
, comments
, category
) VALUES (
in_hdr
, in_cmt
, in_cat);
This code is not inserting anything into dept_id of depts table. Thats the reason you are getting null

Writing a PLSQL Oracle function to query a table

I have the following table
CREATE TABLE Book
(
book_id INTEGER NOT NULL ,
isbn VARCHAR2 (20) NOT NULL,
tittle VARCHAR2 (100) NOT NULL ,
shelf_letter CHAR (1) NOT NULL ,
call_number INTEGER ,
no_of_copies INTEGER NOT NULL ,
) ;
I need to write a function to retrieve book_id, title,call_number, shelf_letter, no_of_copies for a given isbn.
Input parameters: isbn
Output parameters: title, no_of_copies,call_number,shelf_letter.
Return book_id if the query is successful and -1 if not.
How can I properly write this function?
create OR replace FUNCTION get_book_id
(
p_isbn IN VARCHAR2
, po_title OUT VARCHAR2
, po_no_of_copies OUT NUMBER
, po_call_number OUT NUMBER
, po_shelf_letter OUT NUMBER
)
RETURN NUMBER
IS
v_book_id NUMBER;
BEGIN
BEGIN
SELECT
book_id
, title
, no_of_copies
, call_number
, shelf_letter
INTO
v_book_id
, po_title
, po_no_of_copies
, po_call_number
, po_shelf_letter
FROM book
WHERE isbn = 'p_isbn'
;
EXCEPTION
WHEN NO_DATA_FOUND THEN
v_book_id := -1;
END;
RETURN v_book_id;
END;
/
DECLARE
TYPE book_info_rec IS RECORD
(
book_id NUMBER(1)
, title VARCHAR2(30)
, call_number NUMBER(1)
, shelf_letter VARCHAR2(30)
, no_of_copies NUMBER(1)
);
l_book book_info_rec;
FUNCTION get_book_info(isbn_in IN VARCHAR2) RETURN book_info_rec
AS
l_book_info book_info_rec;
BEGIN
SELECT 1
, 'A Book'
, 2
, 'A'
, 3
INTO l_book_info
FROM DUAL
WHERE dummy = isbn_in;
RETURN l_book_info;
END;
BEGIN
l_book := get_book_info('X');
DBMS_OUTPUT.PUT_LINE
(
l_book.book_id
|| ' ' || l_book.title
|| ' ' || l_book.call_number
|| ' ' || l_book.shelf_letter
|| ' ' || l_book.no_of_copies
);
END;

Return two lists in PLSQL

I am trying to return a list of 3 VARRAYS/COLLECTIONS to my application. I am having trouble though, think I am either implementing the solution incorrectly
create or replace
PROCEDURE "GENERATE_PEOPLE"
(
-- In this example pi_string will be "This.is.a.test"
pi_string IN VARCHAR2 ,
po_firstnames OUT VARRAY ,
po_lastnames OUT VARRAY ,
po_descriptions OUT VARRAY ,
po_error_code OUT VARCHAR2 ,
po_error_message OUT VARCHAR2
)
IS
CURSOR people_cursor IS SELECT firstname, lastname, description FROM people;
BEGIN
FOR person_rec IN people_cursor
LOOP
-- This is where I am trying to return 3 collections of po_firstnames, po_lastnames, po_descriptions
-- The print statements below print out exactly what it is I am trying to return!
dbms_output.put_line('Firstname: ' || person_rec.firstname);
dbms_output.put_line('Lastname: ' || person_rec.lastname);
dbms_output.put_line('Description: ' || pi_string || person_rec.description);
-- This is where the values would be added to the list/array/collection
po_firstnames(num?) := person_rec.firstname;
po_lastnames(num?) := person_rec.lastname;
po_descriptions(num?) := pi_string || person_rec.description;
END LOOP;
RETURN;
END;
Any help is greatly appreciated
Oracle 10g
And I am calling it as such:
DECLARE
TYPE po_firstnames AS vc2_array;
TYPE po_lastnames AS vc2_array;
TYPE po_descriptions AS vc2_array;
po_error_code VARCHAR2(50);
po_error_message VARCHAR2(50);
BEGIN
GENERATE_PEOPLE
(
'This.is.a.test' ,
po_firstnames ,
po_lastnames ,
po_descriptions ,
po_error_code ,
po_error_message
);
END;
VARRAY cannot be used directly as the type of a parameter or variable. Instead you need to create a TYPE that is a VARRAY like this (e.g.):
create type vc2_array as varray(100) of varchar2(4000);
then:
create or replace
PROCEDURE "GENERATE_PEOPLE"
(
-- In this example pi_string will be "This.is.a.test"
pi_string IN VARCHAR2 ,
po_firstnames OUT vc2_array ,
po_lastnames OUT vc2_array ,
po_descriptions OUT vc2_array ,
po_error_code OUT VARCHAR2 ,
po_error_message OUT VARCHAR2
)
Generally I would use TABLE rather than VARRAY, bacause with TABLE you do not have to specify a maximum number of elements:
create type vc2_array as table of varchar2(4000);
The values can then be assigned like this in your loop:
num := num+1; -- num must be declared above and initialised to 0
po_firstnames(num) := person_rec.firstname;
po_lastnames(num) := person_rec.lastname;
po_descriptions(num) := pi_string || person_rec.description;
However it would be more efficient to do this:
create or replace
PROCEDURE "GENERATE_PEOPLE"
(
-- In this example pi_string will be "This.is.a.test"
pi_string IN VARCHAR2 ,
po_firstnames OUT vc2_array ,
po_lastnames OUT vc2_array ,
po_descriptions OUT vc2_array ,
po_error_code OUT VARCHAR2 ,
po_error_message OUT VARCHAR2
)
IS
BEGIN
SELECT firstname, lastname, pi_string||description
BULK COLLECT INTO po_firstnames, po_lastnames, po_descriptions
FROM people;
END;

Resources