I am trying to use autoincrement on my package without using trigger.. Can someone explain me how I have to use it in my package.. I did this way didnt work its complaining abt variable not being declared or type not assigned.. I saw other autoincrement questions but nobody has used auto increment without trigger on package
PROCEDURE insertExample (
user_id_in IN sample.seq_user_id.nextval,
name_in IN sample.name%TYPE,
age_in IN sample.age%TYPE )
IS
BEGIN
INSERT INTO sample
(seq_user_id.nextval, name, age)
VALUES
(user_id_in, name_in, age_in);
END insertExample;
Are you wanting to allow a custom user_id to be passed in or always use the sequence?
In the first case, you would need something like:
CREATE OR REPLACE PROCEDURE insertexample (
user_id_in in sample.user_id%type,
name_in in sample.name%type,
age_in in sample.age%type
)
IS
BEGIN
insert into sample
(user_id, name, age
)
values (nvl(user_id_in, seq_user_id.nextval), name_in, age_in);
END insertexample;
If you always want to use the sequence (which is probably the right choice), just take out that input parameter and the NVL:
CREATE OR REPLACE PROCEDURE insertexample (
name_in in sample.name%type,
age_in in sample.age%type
)
IS
BEGIN
insert into sample
(user_id, name, age
)
values (seq_user_id.nextval, name_in, age_in);
END insertexample;
You want something like
PROCEDURE insertExample (
name_in IN sample.name%TYPE,
age_in IN sample.age%TYPE )
IS
BEGIN
INSERT INTO sample
(user_id, name, age)
VALUES
(seq_user_id.nextval, name_in, age_in);
END insertExample;
Your not that far away from a working solution:
PROCEDURE insertExample (
name_in IN sample.name%TYPE,
age_in IN sample.age%TYPE )
IS
BEGIN
INSERT INTO sample (user_id, name, age)
VALUES (seq_user_id.nextval, name_in, age_in);
END insertExample;
I'm assuming that the table has at least three columns called user_id, name and age. Furthermore, I'm assuming that you have already created a sequence called seq_user_id (CREATE SEQUENCE seq_user_id START WITH 1 INCREMENT BY 1).
As the user Id is not automatically assigned, it is no longer part of the parameter list.
I would phrase your procedure as a FUNCTION instead, and return the new ID to the caller:
FUNCTION insert_user
(
name_in IN users.name%TYPE,
age_in IN users.age%TYPE
)
RETURN users.id%TYPE
IS
v_id users.id%TYPE;
BEGIN
INSERT INTO users (
user_id,
name,
age
) VALUES (
seq_user_id.nextval,
name_in,
age_in
)
RETURNING
user_id
INTO
v_id
;
RETURN v_id;
END insert_user;
This is generally a more useful pattern for inserts, especially where you may want to subsequently insert child records in other tables.
Related
I need some help with PL SQL. I have to insert some data into table. Another application is calling my procedure and caller is passing few details which I also need to insert into my table.
here is the syntax I am struggling with:
PROCEDURE invform_last2orders_item_insert( p_userId IN NUMBER
,p_accountId IN NUMBER
,p_site_Id IN NUMBER
,p_return_message OUT VARCHAR2) IS
Begin
insert into mytable
(p_userId , p_accountId , p_site_Id , sku, description, 'Cart', 1, unitId)
as
select sku, description, unitId
from mycatalogtable where site_id= p_site_Id ) ;
End;
Can you help me with syntax? I need to pass three parameters from called in parameter and some values returned from select query. How can I achieve this?
thank you for your help.
That would be something like this; see comments within code:
PROCEDURE invform_last2orders_item_insert
( p_userId IN NUMBER
,p_accountId IN NUMBER
,p_site_Id IN NUMBER
,p_return_message OUT VARCHAR2)
IS
Begin
insert into mytable
-- first name all columns you'll be inserting into; I don't know their
-- names so I just guessed
(userid,
accountid,
siteid,
sku,
description,
col1,
col2,
unitid
)
-- if you were to insert only values you got via parameters, you'd use the
-- VALUE keyword and insert those values separately.
-- As some of them belong to a table, use SELECT statement
(select p_userid,
p_accountid,
p_siteid,
c.sku,
c.description,
'Cart',
1,
c.unitid
from mycatalogtable c
where c.site_id = p_site_Id
);
-- I don't know what you are supposed to return; this is just an example
p_return_message := sql%rowcount || ' row(s) inserted';
End;
in your select statement you should have the same number of columns as you are inserting into the table, your code should be something like this example,
DECLARE
userid varchar2(20) := 'Jack';
Begin
INSERT INTO mytable (SELECT userid, SPORT from OLYM.OLYM_SPORTS);
commit;
end;
Please anyone can assist me. I have a table type object.
CREATE OR REPLACE TYPE SI_PHONE_ACC_T AS TABLE OF SI_PHONE_ACC;
CREATE OR REPLACE TYPE SI_PHONE_ACC AS OBJECT
(
PHONE_ACC VARCHAR2(15);
);
Below is generally passed to the procedure as an input from UI end.
Example:
SI_PHONE_ACCT_T( SI_PHONE_ACC('123-345-6543'),SI_PHONE_ACC('999-999-9999'), SI_PHONE_ACC( 'ax878974545787wp')); -- first 2 are phone no., 3rd is account no.
Phonetable:
Emp_id--- phone_number
1 -- 123-345-6543
2 -- 999-999-9999
3--- 897-897-8781
Account table:
Emp_id--- account_number
10 -- A0000
20 -- B0000
30--- ax878974545787wp
CREATE OR REPLACE PACKAGE BODY order_mgr
IS
PROCEDURE ins_trees ( p_emp_details_in IN SI_PHONE_ACC_T )
BEGIN
-- Now, I need to retrieve emp_ids from Phone and Account tables based on the phone or account numbers passed in the input parameter. please let me know how to do this.
---once I get those emp_id's, i need to insert into employee table which contains only one column emp_id.
--Please let me know how to do this.
FOR i IN 1 .. p_emp_details_in.count
LOOP
INSERT into employee (emp_id)
values(??);
END IF;
END LOOP;
END;
PROCEDURE ins_trees ( p_emp_details_in IN SI_PHONE_ACC_T )
BEGIN
INSERT into employee (emp_id)
select Emp_id
from table(p_emp_details_in) t
, Phone_table t2
where t.PHONE_ACC = t2.phone_number
union
select Emp_id
from table(p_emp_details_in) t
, Account_table t2
where t.PHONE_ACC = t2.account_number
;
END;
/
I have this package where I'm adding some values into the table,this works....
create or replace PACKAGE BODY DC_LOAN_PKG
IS
PROCEDURE loan_book
(
loan_id IN DC_LOAN_EVIDENCE.ID%type,
b_id IN DC_LOAN_EVIDENCE.BOOK_ID%type,
m_id IN DC_LOAN_EVIDENCE.MEMBER_ID%type,
d_ofl IN DC_LOAN_EVIDENCE.DATE_OF_LOAN%type)
IS
BEGIN
INSERT
INTO DC_LOAN_EVIDENCE
(
ID,
BOOK_ID,
MEMBER_ID,
DATE_OF_LOAN
)
VALUES
(
DC_SEQ.NEXTVAL,
b_id,
m_id,
SYSDATE
);
COMMIT;
END loan_book;
now, I want to write procedures return_book and extend_loan inside which I want to get DATE when the book is returned and a new date of loan after extending. What I'm doing wrong? Here is the example....
PROCEDURE return_book
(
b_id IN DC_LOAN_EVIDENCE.BOOK_ID%type,
m_id IN DC_LOAN_EVIDENCE.MEMBER_ID%type,
r_dat IN DC_LOAN_EVIDENCE.RETURN_DATE%type
)
IS
dStartDate DATE := TO_DATE('2014-12-23', 'YYYY-MM-DD');
dEndDate DATE := TO_DATE('2015-01-23', 'YYYY-MM-DD');
BEGIN
UPDATE DC_LOAN_EVIDENCE
SET RETURN_DATE =r_dat
WHERE MEMBER_ID =m_id
AND BOOK_ID =b_id
AND RETURN_DATE BETWEEN dStartDate AND dEndDate ;
COMMIT;
END return_book;
PROCEDURE extend_loan
(
b_id IN DC_LOAN_EVIDENCE.BOOK_ID%type,
m_id IN DC_LOAN_EVIDENCE.MEMBER_ID%type,
d_ofl IN DC_LOAN_EVIDENCE.DATE_OF_LOAN%type
)
IS
BEGIN
UPDATE DC_LOAN_EVIDENCE
SET DATE_OF_LOAN =d_ofl
WHERE MEMBER_ID =m_id
AND BOOK_ID =b_id;
COMMIT;
END extend_loan;
END DC_LOAN_PKG;
You need to keep the inner procedure in the declaration block of the main procedure.
Something like -
PROCEDURE return_book(
b_id IN DC_LOAN_EVIDENCE.BOOK_ID%type,
m_id IN DC_LOAN_EVIDENCE.MEMBER_ID%type,
r_dat IN DC_LOAN_EVIDENCE.RETURN_DATE%type )
IS
dStartDate DATE := TO_DATE('2014-12-23', 'YYYY-MM-DD');
dEndDate DATE := TO_DATE('2015-01-23', 'YYYY-MM-DD');
PROCEDURE extend_loan(
b_id IN DC_LOAN_EVIDENCE.BOOK_ID%type,
m_id IN DC_LOAN_EVIDENCE.MEMBER_ID%type,
d_ofl IN DC_LOAN_EVIDENCE.DATE_OF_LOAN%type )
IS
BEGIN
UPDATE DC_LOAN_EVIDENCE
SET DATE_OF_LOAN =d_ofl
WHERE MEMBER_ID =m_id
AND BOOK_ID =b_id;
COMMIT;
END extend_loan;
BEGIN
UPDATE DC_LOAN_EVIDENCE
SET RETURN_DATE =r_dat
WHERE MEMBER_ID =m_id
AND BOOK_ID =b_id
AND RETURN_DATE BETWEEN dStartDate AND dEndDate ;
COMMIT;
END return_book;
END DC_LOAN_PKG;
I would insist you wrap it in a package. With such stand alone procedure, you are introducing the dependency chain. Which could be avoided using a package. You could implement encapsulation and it has many advantages just like Tom Kyte said -
break the dependency chain (no cascading invalidations when you install a new package body -- if
you have procedures that call procedures -- compiling one will invalidate your database)
support encapsulation -- I will be allowed to write MODULAR, easy to understand code -- rather
then MONOLITHIC, non-understandable procedures
increase my namespace measurably. package names have to be unique in a schema, but I can have
many procedures across packages with the same name without colliding
support overloading
support session variables when you need them
promote overall good coding techniques, stuff that lets you write code that is modular,
understandable, logically grouped together....
EDIT If you have the procedures independent, then you need to have them separately.
CREATE OR replace PACKAGE BODY dc_loan_pkg
IS
-- Procedure 1
PROCEDURE Loan_book(loan_id IN dc_loan_evidence.id%TYPE,
b_id IN dc_loan_evidence.book_id%TYPE,
m_id IN dc_loan_evidence.member_id%TYPE,
d_ofl IN dc_loan_evidence.date_of_loan%TYPE)
IS
BEGIN
INSERT INTO dc_loan_evidence
(id,
book_id,
member_id,
date_of_loan)
VALUES ( dc_seq.NEXTVAL,
b_id,
m_id,
SYSDATE );
COMMIT;
END loan_book;
-- Procedure 2
PROCEDURE Return_book (b_id IN dc_loan_evidence.book_id%TYPE,
m_id IN dc_loan_evidence.member_id%TYPE,
r_dat IN dc_loan_evidence.return_date%TYPE)
IS
dstartdate DATE := To_date('2014-12-23', 'YYYY-MM-DD');
denddate DATE := To_date('2015-01-23', 'YYYY-MM-DD');
BEGIN
UPDATE dc_loan_evidence
SET return_date = r_dat
WHERE member_id = m_id
AND book_id = b_id
AND return_date BETWEEN dstartdate AND denddate;
COMMIT;
END return_book;
-- Procedure 3
PROCEDURE Extend_loan(b_id IN dc_loan_evidence.book_id%TYPE,
m_id IN dc_loan_evidence.member_id%TYPE,
d_ofl IN dc_loan_evidence.date_of_loan%TYPE)
IS
BEGIN
UPDATE dc_loan_evidence
SET date_of_loan = d_ofl
WHERE member_id = m_id
AND book_id = b_id;
COMMIT;
END extend_loan;
END dc_loan_pkg;
/
Your original procedure, loan_book, has two parameters you don't use - whatever you pass as id and book_loan_date is ignored, and is replaced by the sequence value and current date respectively. Using a sequence for the ID makes sense but letting users pass in their own value will be confusing, so you should maybe remove that argument. Whether you use sysdate or the passed value for the date of the loan depends on your business rules, but at the moment that is also confusing at best. A compromise might be to have that argument default to sysdate, so it can be overridden if needed.
Your return_book procedure is not going to match any records created by loan_book because it's checking the return_date, which will be null (unless you've already set it manually in the meantime); that check seems rather odd if you can only return a loan once. Essentially, AND RETURN_DATE BETWEEN dStartDate AND dEndDate will not match any rows where return_date is null.
Your extend_loan procedure does seem to be working though, assuming you're passing the right book and member values.
SQL Fiddle.
If you are not seeing the date_of_loan being extended then you must be passing the wrong values to the procedure.
However, passing the book and member to your new procedures implies that a book can only be loaned to a member once. If you want to allow the same book to be borrowed more than once you should be passing the ID value instead, as that is (presumably!) unique. Perhaps your return_date filter in return_book is actually supposed to be checking the date_of_loan is recent, in an attempt to fix that ambiguity. Passing ID instead would be better; explicitly looking for non-returned books, and/or the most recent loan for a specific book/member, would work to some extent too but would be less robust.
It's also generally considered bad practice to commit inside a procedure, it's better to let the caller decide whether to commit or rollback.
I created the following simple PL/SQL stored procedure example to ask a specific question. This procedure inserts an employee name and id number into a table called employees_???. The ??? is explained below.
PROCEDURE hire_employee (emp_id IN INTEGER, name IN VARCHAR2, country IN VARCHAR2)
AS
BEGIN
INSERT INTO employees_??? VALUES (emp_id, name, 1000);
END hire_employee;
What I need is to set the table name based on the IN variable country. For example,
If country = 'usa', I want the INSERT line to read:
INSERT INTO employees_usa VALUES (emp_id, name, 1000);
If country = 'germany', I want the INSERT line to read:
INSERT INTO employees_germany VALUES (emp_id, name, 1000);
If country = 'france', I want the INSERT line to read:
INSERT INTO employees_france VALUES (emp_id, name, 1000);
etc...
Is there a way to do this in PL/SQL by substituting something in place of employee_??? so only one line of code for INSERT is used? Or is using a case or if/then/else statement the best way?
To answer your question, you have to use execute immediate and create your statement dynamically.
create or replace procedure hire_employee (
emp_id IN INTEGER
, name IN VARCHAR2
, country IN VARCHAR2 ) is
-- maximum length of an object name in Oracle is 30
l_table_name varchar2(30) := 'employees_' || country;
begin
execute immediate 'insert into ' || l_table_name
|| ' values (:1, :2, 1000)'
using emp_id, name;
end hire_employee;
However, this is a massively over-complicated way of storing the data. If you want to select all data you have to union large numbers of tables.
It would be far better to normalise the database properly and add country to an employees table.
Something like the following:
create table employees (
emp_id number(16)
, country varchar2(3) -- ISO codes
, name varchar2(4000) -- maximum who knows what name people might have
, < other_columns >
, constraint pk_employees primary key ( emp_id )
);
Your procedure then becomes a very simple insert statement:
create or replace procedure hire_employee (
emp_id in integer
, name in varchar2
, country in varchar2 ) is
insert into employees
values ( emp_id, country, name, 1000 );
end hire_employee;
You can use dynamic SQL and the EXECUTE IMMEDIATE construct. In this, you construct the query as a string and then execute it. A good example is at http://docs.oracle.com/cd/B10500_01/appdev.920/a96590/adg09dyn.htm
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Autoincrement in Oracle
I have a table in oracle and I want to make userid auto increment..
Once I make it auto increment by using sequence and try to insert into table using procedure where do i put my sample.seq_userid
How do i insert userid? do i have to declare it in my procedure?
PROCEDURE insertExample
(
name_in IN sample.name%TYPE,
age_in IN sample.age%TYPE
)
IS
BEGIN
INSERT INTO sample
(name, age)
VALUES
(name_in, age_in);
END insertExample;
here is my update one
PROCEDURE updateExample
(
userid_in IN sample.userid%TYPE,
name_in IN sample.name%TYPE,
age_in IN sample.age%TYPE
)
IS
BEGIN
UPDATE sample
SET name = name_in,
age = age_in
WHERE userid = userid_in;
END updateExample;
you need a sequence:
create sequence seq_user_id start with 1 increment by 1;
and a trigger on a table
CREATE TRIGGER user_id_trg
BEFORE insert
ON sample
FOR EACH ROW
BEGIN
SELECT seq_user_id.NEXTVAL INTO :new.user_id FROM dual;
END;
/