Writing a PLSQL Oracle function to query a table - oracle

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;

Related

Oracle function Select Into not working correctly

Hi all i have the code below :
CREATE OR REPLACE FUNCTION GET_STATE_USER (user_id IN NUMBER)
RETURN VARCHAR2
AS
staffName VARCHAR2 (50);
BEGIN
DBMS_OUTPUT.put_line(user_id);
SELECT bs.FIRST_NAME || ' ' || bs.LAST_NAME
INTO staffName
FROM EMPLOYEE e
WHERE bs.USER_ID = user_id;
RETURN (CASE
WHEN staffName IS NOT NULL THEN staffName
ELSE 'Invalid'
END);
END;
/
And I'm getting following error :
ORA-01422: exact fetch returns more than requested number of rows
ORA-06512: at "GET_STATE_USER", line 8
You must change the name of the input variable. The variable name must not be the same as the table column name.
try this :
CREATE OR REPLACE NONEDITIONABLE FUNCTION GET_STATE_USER (user__id IN NUMBER)
RETURN VARCHAR2
AS
staffName VARCHAR2 (50);
BEGIN
DBMS_OUTPUT.put_line(user__id);
SELECT bs.FIRST_NAME || ' ' || bs.LAST_NAME
INTO staffName
FROM EMPLOYEE bs
WHERE bs.USER_ID = user__id;
RETURN (CASE
WHEN staffName IS NOT NULL THEN staffName
ELSE 'Invalid'
END);
END;
Actually I wanted to check two tables for existence of a record and finally the code below did the trick :
CREATE OR REPLACE FUNCTION GET_STATE_USER (userId IN NUMBER)
RETURN VARCHAR2
AS
r_count NUMBER;
userTitle VARCHAR2 (100);
BEGIN
SELECT COUNT (1)
INTO r_count
FROM EMPLOYEE bs
WHERE bs.USER_ID = userId;
IF r_count > 0
THEN
SELECT bs.FIRST_NAME || ' ' || bs.LAST_NAME
INTO userTitle
FROM EMPLOYEE bs
WHERE bs.USER_ID = userId;
ELSE
SELECT ac.name
INTO userTitle
FROM allcustomer ac
WHERE ac.CUSTOMER_USER_ID = userId;
END IF;
RETURN (CASE
WHEN userTitle IS NOT NULL THEN userTitle
ELSE 'inavlid'
END);
END;
/

The declaration of the type of this expression is incomplete or malformed

I am working on the following stored procedure.
My_Procedure
create or replace PROCEDURE My_Procedure(
v_ID IN NUMBER DEFAULT 0 ,
v_DETAIL_ID IN NUMBER DEFAULT 0 ,
v_HEADER_ID IN NUMBER DEFAULT 0 ,
v_JOB_DETAIL_ID IN NUMBER DEFAULT 0 ,
v_TABLE_NAME IN VARCHAR2,
v_ESCALATION_TYPE IN VARCHAR2 DEFAULT NULL ,
v_COLUMN_LIST IN NVARCHAR2 DEFAULT NULL ,
cv_1 OUT SYS_REFCURSOR,
cv_2 OUT SYS_REFCURSOR ) AS BEGIN NUll; END;
Stored Procedure in which i am executing
DECLARE
v_ident VARCHAR(30000) := NULL;
v_columns
varchar(30000) := NULL;
v_job_header_id number := 0;
BEGIN
SELECT
(
SELECT
rtrim(XMLAGG(xmlelement(e, c.column_name, ',').extract('//text()')
ORDER BY
c.column_id
).getclobval(), ',')
FROM
table1 c
WHERE
c.owner = t.owner
AND c.table_name = t.table_name
)
INTO v_columns
FROM
all_tables t
WHERE
t.table_name = 'TABLE_NAME';
v_ident := 'INSERT INTO TABLE_NAME ('
|| v_columns
|| ') EXEC My_Procedure(0,0,0,''tableName'',null,v_COLUMNS)';
dbms_output.put_line(v_ident);
EXECUTE IMMEDIATE ( v_ident, 'v_JOB_HEADER_ID INT OUT,v_COLUMNS VARCHAR(30000) OUT', v_job_header_id, v_columns );
END;
In this stored procedure i am taking the table columns name and inserting the values according to the columns.
I am getting the error on the execute immediate line. as the declaration of the type of this expression is incomplete or malformed.

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

Need help with PL SQL Query [Execute Immediate]

I have this query
EXECUTE IMMEDIATE 'UPDATE ' || dest || ' SET COUNTRY_CODE = :v1 WHERE col_id = :v2 returning rowid into :out'
USING l_vc_CountryCode, l_vc_ColId
returning into l_vc_rowid;
the l_vc_rowid is defined as varchar2(10);
I am trying to google, but couldn't find the problem.
Use sql%rowcount to determine how many rows were affected by the update.
With a working PL/SQL function:
create table tq84_update_test (
country_code number,
col_id varchar2(10)
);
create or replace
function tq84_update_func (dest in varchar2,
col_id in varchar2,
country_code in number
)
return varchar2
as
begin
execute immediate
'update ' || dest || ' set country_code = :v1 ' ||
'where col_id = :v2'
using country_code, col_id;
return sql%rowcount || ' rows updated';
end tq84_update_func;
/
insert into tq84_update_test values (4,'Italy');
exec dbms_output.put_line(tq84_update_func('tq84_update_test', 'foo', 2));
exec dbms_output.put_line(tq84_update_func('tq84_update_test', 'Italy', 9));
select * from tq84_update_test;

Resources