I want to create a withdrawal procedure in oracle pl/sql.
I have this table
ACCOUNT_NO BRANCH_NAME AMOUNT_BALANCE
---------- -------------------------------------------------- --------------
102 MA 32900
101 NA 32000
103 IA 50000
104 SA 45000
105 MSA 20000
I try to use this code
CREATE OR REPLACE PROCEDURE withdrawal_proc IS
con number(6);
con1 number(6);
bal1 number(20);
bal2 number(20);
begin
con := &con;
bal1 := &bal;
select Account_No, Amount_Balance into con1, bal2 from Accounts where Account_No=con;
if (bal2 < bal1)
dbms_output.put_line('the amount enterd is more than the amount balance');
else
update Accounts set Amount_Balance=(bal1-bal2) where con =con1;
end if;
dbms_output.put_line('Money has been Withdraw succesfully');
END;
/
but there is a Warning: Procedure created with compilation errors.
You can't use SQL*Plus-style variables such as &con and &bal1 in stored procedures. In this case the con and bal values should probably be passed to the procedure as parameters:
CREATE OR REPLACE PROCEDURE withdrawal_proc(account_no_in IN NUMBER(6),
bal_in IN NUMBER(20))
IS
current_balance number(20);
BEGIN
select Amount_Balance
into current_balance
from Accounts
where Account_No = account_no_in;
if current_balance < bal_in then
dbms_output.put_line('The amount entered is more than the amount balance');
else
update Accounts
set Amount_Balance = bal_in - current_balance
where Account_No = account_no_in;
end if;
dbms_output.put_line('Money has been withdrawn successfully');
END;
You also used improper syntax for your IF statement - compare the original code with the version above.
In addition, note that I suspect that the account balance computation may be incorrect. You may need to debug this a bit.
Best of luck.
Related
I want to write a procedure to perform withdraw operation that only permits a withdrawal, if there is sufficient funds in the account then update the Account table and print the message, 'Transaction successful.' else print, 'Insufficient Amount.' . The procedure should take Account_id and withdrawal amount as input.
Account:
ACCNO NUMBER PK
CUSTOMER_NAME VARCHAR2(30)
BALANCE NUMBER(15,2)
12345 Williams 23455.6
23456 Robert 43221
34521 John 23449
Functional Requirement:
procedure withdraw(ano number , amt number)
Sample input:
withdraw(12345, 2000);
Sample output:
Transaction successful.
I tried to write this code which is as follows-
set serveroutput on;
create or replace procedure withdraw(ano number, amt number) is withdraw_operation account%rowtype;
begin
select * into withdraw_operation from account
if (amt > balance)
then dbms_output.put_line('Transaction successful');
else dbms_output.put_line('Insufficient Amount');
end if;
end;
But this is not showing any output nor error, please help. Thanks in advance!
this is not showing any output nor error
This is hard to believe because what you posted isn't correct.
Anyway: from what you posted, should be something like this:
Sample data:
SQL> SET SERVEROUTPUT ON;
SQL>
SQL> SELECT * FROM account;
ACCNO CUSTOMER BALANCE
---------- -------- ----------
12345 Williams 23455,6
23456 Robert 43221
34521 John 23449
Procedure:
SQL> CREATE OR REPLACE PROCEDURE withdraw (ano NUMBER, amt NUMBER)
2 IS
3 withdraw_operation account%ROWTYPE;
4 BEGIN
5 SELECT *
6 INTO withdraw_operation
7 FROM account
8 WHERE accno = ano;
9
10 IF amt > withdraw_operation.balance
11 THEN
12 DBMS_OUTPUT.put_line ('Transaction successful');
13 ELSE
14 DBMS_OUTPUT.put_line ('Insufficient Amount');
15 END IF;
16 END;
17 /
Procedure created.
Testing:
SQL> EXEC withdraw(12345, 2000);
Insufficient Amount
PL/SQL procedure successfully completed.
SQL>
I am trying to take two parameters. a customer id and a purchase amount. The purchase amount is not in the table i will be referencing. it is going to be compared to the credit limit assigned to said customer id and do a dbms output of ethier the credit is too low for the allowed amount or its fine.
I am having trouble implementing the procedure to take the purchase amount parameter and comparing it to the actual credit limit in the table
create or replace PROCEDURE check_available_credit(
c_cust_id IN demo_customer.custid%TYPE,
c_purchase_amount IN NUMBER
) AS c_credit_check VARCHAR(50);
climit demo_customer.creditlimit%type;
BEGIN
SELECT climit
INTO c_credit_check--PLACE INTO PROCEDURE
FROM demo_customer
WHERE custid = c_cust_id;
if(c_purchase_amount > climit)
THEN dbms_output.put_line('Amount is too high');
elsif(c_purchase_amount < climit)
THEN dbms_output.put_line('Amount is perfect');
COMMIT;
END IF;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
END check_available_credit;
I will be using 100 for the custid and 4000 for the purchase amount. that will pull the record of a customer whos credit limit is 5000 so it should report "amount is perfect"
You have used climit in the select statement incorrectly. I'm not sure by the way why you're using the variable c_credit_check.
The below procedure seems to work with the above mentioned change. For the exception block I've added a dbms_output to display the error stack. Rollback is not needed unless you've got other DMLs in your procedure body. Commit should ideally be in the calling section and can be removed if there are no DMLs
Test table / data
create table demo_customer( custid int,creditlimit number );
insert into demo_customer(custid,creditlimit) values (100,5000);
Procedure
CREATE OR REPLACE PROCEDURE check_available_credit(
c_cust_id IN demo_customer.custid%TYPE,c_purchase_amount IN NUMBER
) AS
climit demo_customer.creditlimit%TYPE;
BEGIN
SELECT creditlimit
INTO climit
FROM demo_customer
WHERE custid = c_cust_id;
IF(c_purchase_amount > climit )
THEN dbms_output.put_line('Amount is too high');
ELSIF ( c_purchase_amount < climit ) THEN
dbms_output.put_line('Amount is perfect');
--COMMIT; --not needed if there are no dmls, move this to calling
--block if there are any.
END IF;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK; --not needed unless there's a dml
dbms_output.put_line(dbms_utility.format_error_stack);
END check_available_credit;
Execution
SET SERVEROUTPUT ON
BEGIN
check_available_credit(100,4000);
END;
/
Amount is perfect
PL/SQL procedure successfully completed.
I have created Procedure which calculates gross salary of all employees from salary table.When executing stored procedure using execute statement Error:"invalid SQL statement" occurs and when i am executing procedure using PLSQL block then Error":PLS-00905 Object HR.PROC_GROSSSALARY is invalid" occurs
-- Creating Stored procedure --
create or replace procedure proc_grosssalary
AS
begin
select s.*,(Basic+HRA+DA+CA+Medical) Gross_Salary from salary s;
end;
-- Calling SP using EXECUTE --
execute proc_grosssalary;
-- Calling SP using PLSQL Block --
begin
proc_grosssalary;
end;
display all data in salary table with calculated Gross_Salary in the form of table structure
your PL/SQL syntax is not correct
create or replace procedure proc_grosssalary (out gross_salary number)
AS
begin
select (Basic+HRA+DA+CA+Medical)
into gross_salary
from salary s;
end;
You should also consider the option of creating a View.
Create or replace view v_grosssalary
as select s.*,(Basic+HRA+DA+CA+Medical) Gross_Salary
from salary s;
and simply do select * from v_grosssalary to get the output.
With procedures, there's also a PRINT command(which works in SQL* Plus and SQL developer) using CURSOR bind variables
VARIABLE x REFCURSOR
create or replace procedure proc_grosssalary
AS
begin
OPEN cursor :x for
select s.*,(Basic+HRA+DA+CA+Medical) Gross_Salary from salary s;
end;
/
Print x -- This will display the required result.
But.. What do you want to do? If you want calculate salary for every employee and you have your information in the same row, with a simple SQL you could have it, something like this:
Select id_emp, name_emp, surname_emp, (Basic+HRAs+DA+CA+Medical) as salary
from salary;
And it would be more quick than have strange functions and procedures.. If you don't like it, create a view like these and you'll have a column with the salary calculated, more clean and less obscure
You need to use a cursor so that you can loop on records in the table.
Here is what I did -
#create a table for sample data
create table tsalary0918(Basic number,HRA number,DA number,CA number,Medical number)
#inserting sample data
insert into tsalary0918 values (100,100,100,100,100)
#checking if data is inserted
select * from tsalary0918;
#query output
BASIC HRA DA CA MEDICAL
100 100 100 100 100
#create a stored procedure
create or replace procedure testproc
AS
cursor cur is select s.*,(Basic +HRA + DA +CA +Medical ) Gross_salary from tsalary0918 s;
begin
for records in cur
loop
dbms_output.put_line('DA is ' || records.DA);
dbms_output.put_line('Gross Salary is ' || records.Gross_salary);
end loop;
end;
/
#execute the stored procedure
begin
testproc;
end;
/
#output of the above execution
dbms_output:
DA is 100
Gross Salary is 500
Check online - https://dbfiddle.uk/?rdbms=oracle_11.2&fiddle=1744281398fe62a6cb42250641947249
Task completed:
create or replace procedure p1
IS
cursor c1 is select s.*,(Basic+HRA+CA+DA+Medical) Gross_Salary from salary s;
emp_rec c1%rowtype;
begin
open c1;
dbms_output.put_line('Emp_No Basic HRA Gross_Salary');
dbms_output.put_line('---------------------------------------');
loop
fetch c1 into emp_rec;
exit when c1%notfound;
dbms_output.put_line(emp_rec.employee_number||' '||emp_rec.Basic||' '||emp_rec.HRA||' '||' '||emp_rec.Gross_Salary);
end loop;
close c1;
end;
-- Executing the procedure using PLSQL block --
begin
p1;
end;
Below is expected Final result:
----------------------------------------------
Employee_number Basic HRA Gross_salary
----------------------------------------------
1 25000 10000 36750
2 7000 2800 11650
3 10000 4000 15950
I have asked this question before but I did not get any help.
I want to get the count of rows in two different table given an attribute.
This is my code .
Instead of fetching the total count where the condition holds, I am getting the whole count of the table
create or replace PROCEDURE p1( suburb IN varchar2 )
as
person_count NUMBER;
property_count NUMBER;
BEGIN
SELECT count(*) INTO person_count
FROM person p WHERE p.suburb = suburb ;
SELECT count(*) INTO property_count
FROM property pp WHERE pp.suburb = suburb ;
dbms_output.put_line('Number of People :'|| person_count);
dbms_output.put_line('Number of property :'|| property_count);
END;
/
Is there any other way to do this so that i can retrieve the real total count of people in that SUBURB
Some datas from PERSON TABLE
PEID FIRSTNAME LASTNAME
---------- -------------------- --------------------
STREET SUBURB POST TELEPHONE
---------------------------------------- -------------------- ---- ------------
30 Robert Williams
1/326 Coogee Bay Rd. Coogee 2034 9665-0211
32 Lily Roy
66 Alison Rd. Randwick 2031 9398-0605
34 Jack Hilfgott
17 Flood St. Bondi 2026 9387-0573
SOME DATA from PROPERTY TABLE
PNO STREET SUBURB POST
---------- ---------------------------------------- -------------------- ----
FIRST_LIS TYPE PEID
--------- -------------------- ----------
48 66 Alison Rd. Randwick 2031
12-MAR-11 Commercial 8
49 1420 Arden St. Clovelly 2031
27-JUN-10 Commercial 82
50 340 Beach St. Clovelly 2031
05-MAY-11 Commercial 38
Sorry for the way the table is looking .
This is the value I get when I run the above script.
SQL> exec p1('Randwick')
Number of People :50
Number of property :33
I changed the PROCEDURE ,this is what I get .
SQL> create or replace PROCEDURE p1( location varchar2 )
IS
person_count NUMBER;
property_count NUMBER;
BEGIN
SELECT count(p.peid) INTO person_count
FROM person p WHERE p.suburb = location ;
SELECT count(pp.pno) INTO property_count
FROM property pp WHERE pp.suburb = location ;
dbms_output.put_line('Number of People :'|| person_count);
dbms_output.put_line('Number of property :'|| property_count);
END;
/
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Procedure created.
SQL> exec p1('KINGSFORD')
Number of People :0
Number of property :0
PL/SQL procedure successfully completed.
SQL>
SQL>
SQL> exec p1('Randwick')
Number of People :0
Number of property :0
PL/SQL procedure successfully completed.
SQL>
The solution suppose to be this
SQL> exec p1('randwick');
Number of People: 7
Number of Property: 2
You named the variable the same as the field. In the query, suburb is first sought in the scope of the query, and it matches the field suburb even though it doesn't use the pp table alias.
So you're actually comparing the field with itself, therefore getting all records (where suburb is NOT NULL, that is). The procedure parameter isn't used in the query at all.
The solution: change the name of the procedure parameter.
To prevent errors like this, I always use P_ as a prefix for procedure/function parameters and V_ as a prefix for local variables. This way, they never mingle with field names.
Although I agree that the cause of the problem is a namespace issue between SQL and PL/SQL, in that the SQL engine has "captured" the name of the PL/SQL variable, I don't believe that changing the name of the parameter is the best approach. If you do this then you doom every developer to start prefixing every parameter name with "p_" or some other useless appendage, and to make sure that they never create a column with a P_ prefix.
If you look through the PL/SQL Supplied Packages documentation you see very few, if any, cases where Oracle themselves do this, although they have in the past done irritatingly inconsistent things like refer to table_name as "tabname".
A more robust approach is to prefix the variable name with the pl/sql procedure name when referencing it in SQL statements:
SELECT count(*)
INTO person_count
FROM person p WHERE p.suburb = p1.suburb ;
In your case you clearly wouldn't name your procedure "P1" so in fact you'd have something like:
SELECT count(*)
INTO person_count
FROM person p WHERE p.suburb = count_suburb_objects.suburb ;
Your code is now immune to variable name capture -- as a bonus your text editor might highlight all the instances where you've used a variable name in a SQL statement when you double-click on the procedure name.
First, create indices for case-insensitive search:
CREATE INDEX idx_person_suburb_u ON person(upper(suburb))
/
CREATE INDEX idx_property_suburb_u ON property(upper(suburb))
/
Second, use prefixes for procedure parameters and local variables:
CREATE OR REPLACE PROCEDURE p1(p_location VARCHAR2)
IS
v_person_count NUMBER;
v_property_count NUMBER;
v_location VARCHAR2(32767);
BEGIN
IF p_location IS NOT NULL THEN
v_location := upper(p_location);
SELECT count(*) INTO v_person_count
FROM person WHERE upper(suburb) = v_location ;
SELECT count(*) INTO v_property_count
FROM property WHERE upper(suburb) = v_location ;
ELSE
SELECT count(*) INTO v_person_count
FROM person WHERE upper(suburb) IS NULL;
SELECT count(*) INTO v_property_count
FROM property WHERE upper(suburb) IS NULL;
END IF;
dbms_output.put_line('Number of People :' || v_person_count);
dbms_output.put_line('Number of Property :' || v_property_count);
END;
/
In MS SQL Server if I want to check the results from a Stored procedure I might execute the following in Management Studio.
--SQL SERVER WAY
exec sp_GetQuestions('OMG Ponies')
The output in the results pane might look like this.
ID Title ViewCount Votes
----- ------------------------------------------------- ---------- --------
2165 Indexed View vs Indexes on Table 491 2
5068 SQL Server equivalent to Oracle’s NULLS FIRST 524 3
1261 Benefits Of Using SQL Ordinal Position Notation? 377 2
(3 row(s) affected)
No need to write loops or PRINT statements.
To do the same thing in Oracle I might execute the following anonymous block in SQL Developer
--ORACLE WAY
DECLARE
OUTPUT MYPACKAGE.refcur_question;
R_OUTPUT MYPACKAGE.r_question;
USER VARCHAR2(20);
BEGIN
dbms_output.enable(10000000);
USER:= 'OMG Ponies';
recordCount := 0;
MYPACKAGE.GETQUESTIONS(p_OUTPUT => OUTPUT,
p_USER=> USER,
) ;
DBMS_OUTPUT.PUT_LINE('ID | Title | ViewCount | Votes' );
LOOP
FETCH OUTPUT
INTO R_OUTPUT;
DBMS_OUTPUT.PUT_LINE(R_OUTPUT.QUESTIONID || '|' || R_OUTPUT.TITLE
'|' || R_OUTPUT.VIEWCOUNT '|' || R_OUTPUT.VOTES);
recordCount := recordCount+1;
EXIT WHEN OUTPUT % NOTFOUND;
END LOOP;
DBMS_OUTPUT.PUT_LINE('Record Count:'||recordCount);
CLOSE OUTPUT;
END;
This outputs like
ID|Title|ViewCount|Votes
2165|Indexed View vs Indexes on Table|491|2
5068|SQL Server equivalent to Oracle’s NULLS FIRST|524|3
1261|Benefits Of Using SQL Ordinal Position Notation?|377|2
Record Count: 3
So the SQL version has 1 line and the oracle has 18 and the output is ugly. Its exacerbated if there are a lot of columns and/or the data is numeric.
What's odd to me about this is that if I write this statement in either SQL Developer or Management studio...
SELECT
ID,
Title,
ViewCount,
Votes
FROM votes where user = 'OMG Ponies'
The results are fairly similar. This makes me feel like I'm either missing a technique or using the wrong tool.
If GetQuestions is a function returning a refcursor, which seems to be what you have in the SQL Server version, then rather you may be able to do something like this:
select * from table(MyPackage.GetQuestions('OMG Ponies'));
Or if you need it in a PL/SQL block then you can use the same select in a cursor.
You can also have the function produce the dbms_output statements instead so they're always available for debugging, although that adds a little overhead.
Edit
Hmmm, not sure it's possible to cast() the returned refcursor to a usable type, unless you're willing to declare your own type (and a table of that type) outside the package. You can do this though, just to dump the results:
create package mypackage as
function getquestions(user in varchar2) return sys_refcursor;
end mypackage;
/
create package body mypackage as
function getquestions(user in varchar2) return sys_refcursor as
r sys_refcursor;
begin
open r for
/* Whatever your real query is */
select 'Row 1' col1, 'Value 1' col2 from dual
union
select 'Row 2', 'Value 2' from dual
union
select 'Row 3', 'Value 3' from dual;
return r;
end;
end mypackage;
/
var r refcursor;
exec :r := mypackage.getquestions('OMG Ponies');
print r;
And you can use the result of the call in another procedure or function; it's just getting to it outside PL/SQL that seems to be a little tricky.
Edited to add: With this approach, if it's a procedure you can do essentially the same thing:
var r refcursor;
exec mypackage.getquestions(:r, 'OMG Ponies');
print r;
SQL Developer automatically catches the output from running your stored procedures. Running the stored procedure directly from our procedure editor, you can see this behavior detailed in my post here
SQL Developer Tip: Viewing REFCURSOR Output
Now, if you want to run the refcursor as part of an anon block in our SQL Worksheet, you could do something similar to this
var rc refcursor
exec :rc := GET_EMPS(30)
print rc
--where GET_EMPS() would be your sp_GetQuestions('OMG Ponies') call. The PRINT command sends the output from the 'query' which is ran via the stored procedure, and looks like this:
anonymous block completed
RC
-----------------------------------------------------------------------------------------------------
EMPLOYEE_ID FIRST_NAME LAST_NAME EMAIL PHONE_NUMBER HIRE_DATE JOB_ID SALARY COMMISSION_PCT MANAGER_ID DEPARTMENT_ID
----------- -------------------- ------------------------- ------------------------- -------------------- ------------------------- ---------- ---------- -------------- ---------- -------------
114 Den Raphaely DRAPHEAL 515.127.4561 07-DEC-94 12.00.00 PU_MAN 11000 100 30
115 Alexander Khoo AKHOO 515.127.4562 18-MAY-95 12.00.00 PU_CLERK 3100 114 30
116 Shelli Baida SBAIDA 515.127.4563 24-DEC-97 12.00.00 PU_CLERK 2900 114 30
117 Sigal Tobias STOBIAS 515.127.4564 24-JUL-97 12.00.00 PU_CLERK 2800 114 30
118 Guy Himuro GHIMURO 515.127.4565 15-NOV-98 12.00.00 PU_CLERK 2600 114 30
119 Karen Colmenares KCOLMENA 515.127.4566 10-AUG-99 12.00.00 PU_CLERK 2500 114 30
Now, you said 10g. If you're in 12c, we have enhanced the PL/SQL engine to support implicit cursor results. So this gets a bit easier, no more setting up the cursor, you just make a call to get the data, as documented here:
http://docs.oracle.com/database/121/DRDAA/migr_tools_feat.htm#DRDAA230
/*
Create Sample Package in HR Schema
*/
CREATE OR REPLACE PACKAGE PRINT_REF_CURSOR
AS
PROCEDURE SP_S_EMPLOYEES_BY_DEPT (
p_DEPARTMENT_ID IN INTEGER,
Out_Cur OUT SYS_REFCURSOR);
END PRINT_REF_CURSOR;
CREATE OR REPLACE PACKAGE BODY PRINT_REF_CURSOR
AS
PROCEDURE SP_S_EMPLOYEES_BY_DEPT (
p_DEPARTMENT_ID IN INTEGER,
Out_Cur OUT SYS_REFCURSOR)
AS
BEGIN
OPEN Out_Cur FOR
SELECT *
FROM EMPLOYEES
WHERE DEPARTMENT_ID = p_DEPARTMENT_ID;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
DBMS_OUTPUT.Put_Line('SP_S_EMPLOYEES_BY_DEPT' || ',' || '-20000' || ',' );
WHEN OTHERS
THEN
DBMS_OUTPUT.Put_Line('SP_S_EMPLOYEES_BY_DEPT' || ',' || '-20001' || ',' );
END SP_S_EMPLOYEES_BY_DEPT;
END PRINT_REF_CURSOR;
/*
Fetch values using Ref Cursor and display it in grid.
*/
var RC refcursor;
DECLARE
p_DEPARTMENT_ID NUMBER;
OUT_CUR SYS_REFCURSOR;
BEGIN
p_DEPARTMENT_ID := 90;
OUT_CUR := NULL;
PRINT_REF_CURSOR.SP_S_EMPLOYEES_BY_DEPT ( p_DEPARTMENT_ID, OUT_CUR);
:RC := OUT_CUR;
END;
/
PRINT RC;
/************************************************************************/