Insert into a table using a stored procedure - sql

I have this simple stored procedure, where it would add a column to my Orders table
create or replace PROCEDURE ADD_ORDER
(
CUSTOMER_ID IN NUMBER
, NEW_ORDER_ID OUT NUMBER
) AS
DECLARE
NEW_ORDER_ID := MAX(ORDERS.ORDER_NO) + 1;
BEGIN
INSERT INTO ORDERS(ORDER_NO, REP_NO, CUST_NO, ORDER_DT, STATUS)
VALUES( NEW_ORDER_ID, 36, CUSTOMER_ID, CURDATE(), 'C')
END ADD_ORDER;
It is saying the the declare part is not at the correct place (I think), and also it should not end there. Here is what it is saying at the error screen:
Error(6,1): PLS-00103: Encountered the symbol "DECLARE" when expecting one of the following:
begin function pragma procedure subtype type current cursor delete exists prior external language The symbol "begin was inserted before "DECLARE" to continue.
Error(11,1): PLS-00103: Encountered the symbol "END" when expecting one of the following: , ; return returning
Can anyone tell me what is going wrong here ?

As has been mentioned, it is a bad idea to select the maximum order number and then use that to insert a row. If two processes do this at the same time, they try to insert rows with the same order number.
Better use Oracle's built-in features SEQUENCE or IDENTITY.
Here is how you could create the table:
CREATE TABLE orders
(
order_no NUMBER(8) GENERATED ALWAYS AS IDENTITY,
rep_no NUMBER(3) DEFAULT 36 NOT NULL,
cust_no NUMBER(8) NOT NULL,
order_dt DATE DEFAULT SYSDATE NOT NULL,
status VARCHAR2(1) DEFAULT 'C' NOT NULL
);
And this is what your procedure would look like then:
CREATE OR REPLACE PROCEDURE add_order
(
in_cust_no IN NUMBER,
out_order_no OUT NUMBER
) AS
BEGIN
INSERT INTO ORDERS(cust_no) VALUES (in_cust_no)
RETURNING order_no INTO out_order_no;
END add_order;
Demo: https://dbfiddle.uk/?rdbms=oracle_18&fiddle=4b49723c15eb810c01077286e171bc95

There is a syntax error in your code.
NEW_ORDER_ID := MAX(ORDERS.ORDER_NO) + 1; --not be used liked it.
Use below code
create or replace PROCEDURE ADD_ORDER
(
CUSTOMER_ID IN NUMBER
, NEW_ORDER_ID OUT NUMBER
) AS
V_NEW_ORDER_ID NUMBER;
BEGIN
SELECT NVL(MAX(ORDER_NO),0)+1 INTO V_NEW_ORDER_ID FROM ORDERS;
INSERT INTO ORDERS(ORDER_NO, REP_NO, CUST_NO, ORDER_DT, STATUS)
VALUES( V_NEW_ORDER_ID, 36, CUSTOMER_ID, CURDATE(), 'C');
NEW_ORDER_ID:=V_NEW_ORDER_ID;
/*
* CURDATE() -> I am assuming it is user defined function. You can also use SYSDATE, CURRENT_DATE istead of CURDATE()
* OUT Parameter is a write-only parameter. You cannot read value from OUT Parameter
*/
END ADD_ORDER;

Few things need to be correct.
If you're expecting to write a PROCEDURE or a FUNCTION you don't have to use the DECLARE keyword. In writing a test script or something, you should use the DECLARE keyword to declare variables.
When writing a procedure,
All the parameters should be inside the brackets.
Variables should define between AS and BEGIN keywords and should give the datatype.
If you need to fetch the MAX number of ORDERS TAB you have to write a SQL query for that. Because the MAX function only can be used inside a SQL. Additionally, if you interest there is an in-built feature call SEQUENCE in ORACLE which can use for NEW_ORDER_ID. You can check with the link below.
adding a sequence for oracle plsql
I did some changes to your code. Hope it's working fine now. Please take a visit here.
CREATE or REPLACE PROCEDURE ADD_ORDER (
CUSTOMER_ID IN NUMBER
NEW_ORDER_ID OUT NUMBER
) AS
CURSOR get_max_order_no IS
SELECT MAX(order_no)
FROM ORDERS;
rec_ NUMBER := 0;
BEGIN
OPEN get_max_order_no;
FETCH get_max_order_no INTO rec_;
CLOSE get_max_order_no;
NEW_ORDER_ID := rec_ + 1;
INSERT INTO ORDERS
(ORDER_NO, REP_NO, CUST_NO, ORDER_DT, STATUS)
VALUES
(NEW_ORDER_ID, 36, CUSTOMER_ID, SYSDATE, 'C');
END ADD_ORDER;

Related

Trigger with lookup from another table

I'm learning PL/SQL specifically triggers, and I want to learn how to operate on multiple tables using triggers but I'm having trouble understanding the process. Here's an example:
CREATE TABLE Sess
(code INTEGER NOT NULL,
dateStart DATE NOT NULL,
dateEnd DATE NOT NULL,
CONSTRAINT KeySess PRIMARY KEY(code)
)
CREATE TABLE Ins
(codeIns CHAR(12) NOT NULL,
code INTEGER NOT NULL,
dateIns DATE NOT NULL,
dateAb DATE,
note INTEGER,
CONSTRAINT KeyIns PRIMARY KEY (codeIns, code)
)
1/ I'm trying to build a trigger that will ensure this:
before Insert or Update Ins
dateAb is between dateStart and dateEnd
2/ I want also to ALTER TABLE and lock code, and codeIns in Ins so no one can update them but i don't know the command.
For the first trigger, I tried something like:
CREATE OR REPLACE TRIGGER test
BEFORE INSERT OR UPDATE OF dateAb ON Ins
FOR EACH ROW
DECLARE
start date;
BEGIN
SELECT DISTINCT s.dateStart INTO start
WHERE Ins.code = s.code
IF (:NEW.dateAb < start) THEN
raise_application_error(-20101,'dateAb incorrect');
END IF;
END;
Note: I didn't declare a variable end to check if it's between, my purpose here was to test the correct syntax
Note2: the table are empty.
I had Warning: Trigger created with compilation errors. and 3 errors :
PL/SQL: SQL Statement ignored
4/40 PL/SQL: ORA-00923: key-word FROM absent
9/5 PLS-00103: Encountered the symbol "IF" when expecting one of the
following:
;
And finally like I said I don't know the correct syntax to lock column on table to prevent updates or if it's possible.
I'm learning so if one of you can teach me the correct way to process with my two request, I would apreciate that.
Thank you
Try this:
create or replace trigger test
before insert or update of dateab on ins
for each row
declare
l_sess_start date;
begin
select s.datestart into l_sess_start
from sess s
where s.code = :new.code;
if :new.dateab < l_sess_start then
raise_application_error
( -20101
, 'Start date ' || to_char(:new.dateab,'DD-MON-YYYY') ||
' cannot be before ' || to_char(l_sess_start,'DD-MON-YYYY'));
end if;
end;
Test:
insert into sess values (1, date '2018-04-22', date '2018-04-30');
insert into ins values ('Z', 1, date '2018-04-01', date '2018-04-01', null);
ERROR at line 1:
ORA-20101: Start date 01-APR-2018 cannot be before 22-APR-2018
ORA-06512: at "WILLIAM.TEST", line 10
ORA-04088: error during execution of trigger 'WILLIAM.TEST'
PLS-00103: Encountered the symbol "IF" when expecting one of the
following:
;
This error is because you are missing ; after select statement

Stored procedure enabling a user to lend multiple books to a borrower

I'm having a lot of trouble trying to work this out. Essentially, I'm trying to create a procedure in Oracle SQL Developer which enables two books to be lent out to a borrower. The tables and values are correctly setup. I feel like my created procedure may be pretty close (on the right track), but I get confused about the syntax, especially with the dates and when trying to call the procedure.
There are a few other related tables in the schema, but the gist is I'm trying to insert library/borrower records into a table called loan. The values being inserted are: 2x isbn, branchid, cardno, dateout (when book/s are borrowed) and datein (null by default, until book/s are returned). These are the column names in the loan table.
This is the procedure I've been working on:
create or replace procedure BorrowTwoBooks(
p_isbn in varchar2,
p_isbn2 in varchar2,
p_branchid in number,
p_cardno in number
)
is
pDate Date Default SysDate;
begin
insert into loan values(p_isbn, p_branchid, p_cardno, pDate, null);
insert into loan values(p_isbn2, p_branchid, p_cardno, pDate, null);
end BorrowTwoBooks;
and this is an example of how I'd try to call the procedure once it's created:
begin
BorrowTwoBooks(
'9-9996751-3-1','1','489',pDate);
'1-8744165-3-2','1','489',pDate);
end;
where I'm hoping the syntax would be:
begin
BorrowTwoBooks(
'[isbn1],'[branchid],[cardno],[dateout]);
'[isbn2],'[branchid],[cardno],[dateout]);
end;
Any help would be greatly appreciated. This is my first post, thanks everyone in advance for the help. :)
for your procedure the syntax would be:
begin
BorrowTwoBooks([isbn1],[isbn2],[branchid],[cardno]);
end;
try to call:
begin
BorrowTwoBooks('9-9996751-3-1','1-8744165-3-2','1','489');
end;
Yoc created procedure that takes 4 parameters. Two of them are ISBNs of books and two others are common for both books so call should look like:
begin
BorrowTwoBooks(
'9-9996751-3-1','1-8744165-3-2',1,489);
end;
Also date is not a parameter of function. You set it to sysdate when procedure called.
You already got the answer regarding the specific case, but it seems to me that you want a procedure that have date as a parameter that is sysdate by default, but can be populated with some other date if needed.
If this is the case, your procedure should look like this:
create or replace procedure BorrowTwoBooks(
p_isbn in varchar2,
p_isbn2 in varchar2,
p_branchid in number,
p_cardno in number,
p_date in date default sysdate)
is
begin
insert into loan(isbn, branchid, cardno, dateout, datein)
values(p_isbn, p_branchid, p_cardno, p_date, null);
insert into loan(isbn, branchid, cardno, dateout, datein)
values(p_isbn2, p_branchid, p_cardno, p_date, null);
end BorrowTwoBooks;
You can call it without providing date:
begin
BorrowTwoBooks('9-9996751-3-1', '1-8744165-3-2', '1', '489');
end;
Or you can call it with the specific date:
declare
l_date date;
begin
l_date := to_date('2016/10/05', 'yyyy/mm/dd');
BorrowTwoBooks('9-9996751-3-1', '1-8744165-3-2', '1', '489', l_date);
end;
There is another way to call the procedure - by providing parameter names. This syntax may be easier for you to understand:
begin
BorrowTwoBooks(p_isbn => '9-9996751-3-1',
p_isbn2 => '1-8744165-3-2',
p_branchid => '1',
p_cardno => '489');
end;

PLSQL condition statement in trigger involving 2 tables

I've two tables purchases and customers, I need to update visits_made (number) in customers if time of purchase ptime (date) in purchases table is different from the already existing last_visit (date) in customers table.
Here is the trigger that I'm trying and I'm doing something terribly and shamefully wrong.
create or replace trigger update_visits_made
after insert on purchases
for each row
declare new_date purchases.ptime%type;
begin
select ptime into new_date
from purchases
where purchases.ptime = :new.ptime;
if new_date = customers.last_visit then
new.last_visit=old.last_visit;
else
update customers
set visits_made=visits_made+1
where purchases.ptime=:new.ptime;
end if;
end;
/
show errors
Can anybody please tell me where I'm going wrong?
I get following errors
LINE/COL ERROR
10/15 PLS-00103: Encountered the symbol "=" when expecting one of the
following:
:= . ( # % ;
11/1 PLS-00103: Encountered the symbol "ELSE"
16/1 PLS-00103: Encountered the symbol "END"
This is a scalar assignment in PL/SQL:
new.last_visit = old.last_visit;
Not only should this be done with := but new and old should have colons before their names:
:new.last_visit := :old.last_visit;
Once you have fixed that problem, then the update will pose an issue:
update customers
set visits_made=visits_made+1
where purchases.ptime = :new.ptime;
It is unclear to me what this is supposed to be doing, so I can't suggest anything, other than pointing out that purchases is not defined.
I think somehow i get your requirement. Basically its a ticker which count the vists of user based on Login dates. I have written a snippet below which replicates the same scenario as mentioned Above. Let me know if this helps.
-- Table creation and insertion script
CREATE TABLE PURCHASES
(
P_ID NUMBER,
P_TIME DATE
);
INSERT INTO PURCHASES
SELECT LEVEL,SYSDATE+LEVEL FROM DUAL
CONNECT BY LEVEL < 10;
CREATE TABLE CUSTOMERS
(
P_ID NUMBER,
P_VISITS NUMBER
);
INSERT INTO CUSTOMERS
SELECT LEVEL,NULL FROM DUAL
CONNECT BY LEVEL < 10;
-- Table creation and insertion script
--Trigger Script
CREATE OR REPLACE TRIGGER update_purchase BEFORE
INSERT ON purchases FOR EACH row
DECLARE
new_date purchases.p_time%type;
BEGIN
BEGIN
SELECT A.P_TIME
INTO new_date
FROM
(SELECT p_time,
ROW_NUMBER() OVER(PARTITION BY P_ID ORDER BY P_TIME DESC) RNK
-- INTO new_date
FROM purchases
WHERE purchases.p_id = :new.p_id
)a
WHERE A.RNK =1;
EXCEPTION WHEN OTHERS THEN
RETURN;
END;
IF :NEW.P_TIME <> new_date THEN
UPDATE customers
SET P_VISITS =NVL(P_VISITS,0)+1
WHERE p_id=:new.p_id;
END IF;
END;
--Trigger Script
--Testing Script
INSERT INTO PURCHASES VALUES
(9,TO_DATE('12/11/2015','MM/DD/YYYY'));
--Testing Script

trigger compilation error - statement ignored, missing value keyword,

I have the following code which produces a few compiler errors. I've followed several examples online to use these declare and the FROM DUAL concepts, but can't ID the problem. Thanks!
Error1: SQL statement Ignored (referencing line 9 - "DECLARE")
Error2: missing VALUE keyword (referencing line 15 - "FROM DUAL;")
---delete or replace trigger------------------------------
CREATE OR REPLACE TRIGGER videoAuditDeleteTrigger
BEFORE DELETE OR UPDATE
ON video
FOR EACH ROW
DECLARE
v_username VARCHAR2(20);
BEGIN
SELECT USER INTO v_username
FROM DUAL;
INSERT INTO videoAudit
(videoID
,title
,releaseDate
,category
,username
,datetimestamp);
VALUES
(:OLD.videoID
,:OLD.title
,:OLD.releaseDate
,:OLD.category
,v_username
,SYSDATE);
END;
/
Try this, you used extra semicolon in insert statement
CREATE OR REPLACE TRIGGER videoAuditDeleteTrigger
BEFORE DELETE OR UPDATE ON video FOR EACH ROW
DECLARE v_username VARCHAR2(20);
BEGIN
SELECT USER INTO v_username FROM DUAL;
INSERT INTO videoAudit (videoID ,title ,releaseDate ,category ,username ,datetimestamp)
VALUES (:OLD.videoID ,:OLD.title ,:OLD.releaseDate ,:OLD.category ,v_username ,SYSDATE);
END;

Want to insert timestamp through procedure in oracle

I have this procedure
PROCEDURE insertSample
(
return_code_out OUT VARCHAR2,
return_msg_out OUT VARCHAR2,
sample_id_in IN table1.sample_id%TYPE,
name_in IN table1.name%TYPE,
address_in IN table1.address%TYPE
)
IS
BEGIN
return_code_out := '0000';
return_msg_out := 'OK';
INSERT INTO table1
sample_id, name, address)
VALUES
(sample_id_in, name_in, address_in);
EXCEPTION
WHEN OTHERS
THEN
return_code_out := SQLCODE;
return_msg_out := SQLERRM;
END insertSample;
I want to add 4th column in table1 like day_time and add current day timestamp in it.. ho can i do that in this procedure.. thank you
Assuming you you have (or add) a column to the table outside of the procedure, i.e.
ALTER TABLE table1
ADD( insert_timestamp TIMESTAMP );
you could modify your INSERT statement to be
INSERT INTO table1
sample_id, name, address, insert_timestamp)
VALUES
(sample_id_in, name_in, address_in, systimestamp);
In general, however, I would strongly suggest that you not return error codes and error messages from procedures. If you cannot handle the error in your procedure, you should let the exception propagate up to the caller. That is a much more sustainable method of writing code than trying to ensure that every caller to every procedure always correctly checks the return code.
Using Sysdate can provide all sorts of manipulation including the current date, or future and past dates.
http://edwardawebb.com/database-tips/sysdate-determine-start-previous-month-year-oracle-sql
SYSDATE will give you the current data and time.
and if you add the column with a default value you can leave your procedure as it is
ALTER TABLE table1 ADD when_created DATE DEFAULT SYSDATE;