Is this program correctly written with the array?
Program Malaria_Outbreak (input,output);
Var
BC:real;
LO:real;
count:integer;
Num:integer;
BloodTest:integer;
Registration:integer;
Clinic:string;
DoctorFee:integer;
Total:integer;
NMB_Payable:real;
Company:string;
Name:string;
Patient:Array[1..10] of string
Begin
clrscr;
BC:=(0.8);
LO:=(0.7);
Count:=(0);
Num:=(0);
BloodTest:=(Num * 700);
Registration:=(500);
Writeln('Please enter the name of the patient');
Readln(Name);
While (Name <> 'END')Do
Begin
For count:= 1 to 10 Do
Begin
Writeln('Please enter the clinic the patient attends');
Readln(Clinic);
If (Clinic = 'Type 1') Then
Begin
DoctorFee:=(800);
End;
If (Clinic = 'Type 2') Then
Begin
DoctorFee:=(1200);
End;
Writeln('The doctor fee for the patient is $',DoctorFee);
Writeln('Please enter the number of blood tests the patient has had');
Readln(Num);
BloodTest:=(Num * BloodTest);
Writeln('The blood test for the patient is $',BloodTest);
TMB:=(Registration + DoctorFee + BloodTest);
Writeln('The total medical bill for the patient is $',TMB);
Writeln('Please enter the insurance company the clinic is affiliated with');
Readln(Company);
If (Company = 'Blue Cross') Then
Begin
NMB_Payable:=(BC * TMB);
End;
If (Company = 'LOJ') Then
Begin
NMB_Payable:=(LO * TMB);
End;
Writeln('The net medical bill for the patient is $',NMB_Payable);
End;
Readln;
Readln;
End
Looks good, but you might want to include the ; after the datatype (string)
Patient : Array[1..10] of String;
There are some problems in the code.
Your code was not formatted. Especially the lack of indenting makes it hard to understand what's going on. (thanks to GolezTrol for fixing that)
You're missing a semi-colon (;) after Array[1..10] of string
Some end; statement is missing. Either While (Name <> 'END')Do begin or For count:= 1 to 10 Do begin should have a matching end; statement.
Variable Tmb is not declared.
Bloodtest will always be 0. It's initialized to 0, and the only time you write to Bloodtest is on this line: BloodTest := (Num * BloodTest);. That's probably not what you want to do.
DoctorFee is uninitialized unless the user types Type 1 or Type 2. NMB_Payable has a similar problem.
There's a variable Count that's initialized, but never used afterwards. Doesn't do any damage, but for readability I'd clean it up.
To answer your question: No, you're not using the array that's declared, and I don't think this program does what you want it to do.
If you explain what you're trying to accomplish, we can help you out with that.
I don't see where it's writing to the array at all, nor where it would make any use of the array in the first place. It's simply processing each item it gets, nothing is carried to be stored in an array in the first place.
It's also going to ask and bill each patient 10 times. I've heard of double-billing but this is crazy!
You should always run your code and see what actually happens. It's quite obvious you didn't.
Related
I am trying to write a block of SQL code that compares two dates from two tables. One table shows when an item was acquired (ci_acquired_date). Another table shows when an item was an assigned to an employee (date_acquired). My goal is to use a loop that loops through the inventory and compare the dates and see if any of the dates assigned to an employee is less than the date the item was acquired (because it is impossible to assign an employee an item that was never bought) and use DBMS to show me which item ID it is and the dates it was acquired and assigned.
This is my code:
declare
cursor task_five is
select a.date_assigned, a.ci_inv_id, b.ci_acquired_date
from jaherna42.employee_ci a
join jaherna42.ci_inventory b
on a.ci_inv_id = b.ci_inv_id
where a.user_or_support = 'USER';
row_one jaherna42.employee_ci%rowtype;
row_two jaherna42.ci_inventory%rowtype;
begin
for row_one in task_five
loop
if(row_one.date_assigned < row_two.ci_acquired_date)
then
dbms_output.put_line('The error is: ' || row_one.ci_inv_id);
dbms_output.put_line('The date assigned is: ' || row_one.date_assigned);
dbms_output.put_line('The date acquired is: ' || row_two.ci_acquired_date);
end if;
end loop;
end;
When I run it, the script output box would show
PL/SQL procedure successfully completed.
But there would be nothing showing up in my dbms output box. What is the error?
row_two is a local variable that is declared but no value is ever assigned to it. Thus, every field in the record is always null. When you compare a value row_one.date_assigned against a null, the comparison will always evaluate to false. So your if statement is always false and nothing will ever be printed.
I'm a bit surprised that the code you posted compiles. row_one is declared as a jaherna42.employee_ci%rowtype but the query you've defined doesn't appear to return all the columns from employee_ci. Perhaps you're just lucky that the order and types of the columns in your table matches the order and types in your query even though that query pulls data from multiple tables. But that's a lucky accident.
If you want to keep the cursor in the declaration section, you'd almost certainly want your local variable to be declared against the cursor's %rowtype not the table's. So something like this is potentially what you're really after.
declare
cursor task_five
is
select a.date_assigned, a.ci_inv_id, b.ci_acquired_date
from jaherna42.employee_ci a
join jaherna42.ci_inventory b
on a.ci_inv_id = b.ci_inv_id
where a.user_or_support = 'USER';
row task_five%rowtype;
begin
for row in task_five
loop
if(row.date_assigned < row.ci_acquired_date)
then
dbms_output.put_line('The error is: ' || row.ci_inv_id);
dbms_output.put_line('The date assigned is: ' || row.date_assigned);
dbms_output.put_line('The date acquired is: ' || row.ci_acquired_date);
end if;
end loop;
end;
Unless it's a requirement of your assignment, though, it would probably make sense to avoid the declarations entirely and just use an implicit cursor in the code. And to do the comparison in SQL rather than PL/SQL
begin
for row in (select a.date_assigned, a.ci_inv_id, b.ci_acquired_date
from jaherna42.employee_ci a
join jaherna42.ci_inventory b
on a.ci_inv_id = b.ci_inv_id
where a.user_or_support = 'USER'
and a.date_assigned < b.ci_acquired_date)
loop
dbms_output.put_line('The error is: ' || row.ci_inv_id);
dbms_output.put_line('The date assigned is: ' || row.date_assigned);
dbms_output.put_line('The date acquired is: ' || row.ci_acquired_date);
end loop;
end;
I'd also generally suggest using meaningful aliases in your query. Aliasing tables to a and b is like using single character variable names-- unlikely to help someone reading the code understand what it is doing. It would almost certainly be more useful to use some consistent naming convention where, say, employee_ci is always aliased to emp and ci_inventory is always aliased to inv.
I am trying to solve a problem in PL/SQL, but I am new to the language. I decided to solve it as I would solve it in another language, and so I learned something about how SQL declares and utilizes arrays in this thread:
Oracle PL/SQL - How to create a simple array variable?
I followed that example, but my code doesn't run. The error message is unhelpful. In particular it references line 21 whereas my code block begins on line 54. Here it is, in case it makes sense by error code, somehow:
ORA-06550: line 21, column 7:
PLS-00103: Encountered the symbol "IN" when expecting one of the following:
:= . ( # % ; 06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
And here is my code block. I am sure I made a bunch of mistakes since I am learning by code example here. If you could please point out any syntax or other errors I would very much appreciate it. Thank you
declare
vISBNa books.ISBN%type := '1059831198';
vISBNb books.ISBN%type := '0401140733';
vISBNc books.ISBN%type := '4981341710';
vISBNd books.ISBN%type := '8843172113';
vCATEGORYtemp books.CATEGORY%type;
vRETAILtemp books.RETAIL%type;
type arry is varray(4) of books.ISBN%type;
array arry := arry(vISBNa, vISBNb, vISBNc, vISBNd);
begin
for i in 1..array.count loop
select category
into vCATEGORYtemp
from books
where ISBN = i;
select retail
into vRETAILtemp
from books
where ISBN = i;
IF vCATEGORYtemp = 'COMPUTER' THEN
vRETAILtemp := vRETAILtemp * 0.7;
ELSIF vCATEGORYtemp = 'FITNESS' THEN
vRETAILtemp := vRETAILtemp * 0.6;
ELSIF vCATEGORYtemp = 'BUSINESS' THEN
vRETAILtemp := vRETAILtemp * 0.8;
ELSE
vRETAILtemp := vRETAILtemp * 0.9;
END IF;
dbms_output.put_line(vRETAILtemp);
end loop;
end;
The problem certainly has a trivial SQL solution, but since this is PL/SQL practice (specifically), here are a few suggestions for improvement. Note that the code is almost correct; in particular, it is not clear what, if anything, throws the exact error you mentioned.
In the loop, you can assign to both local variables in a single select statement, as shown below. Notice also the where clause, where you must compare ISBN from the table to array(i), not to i. (That is the only error I found!)
for i in 1 .. array.count loop
select category, retail
into vCATEGORYtemp, vRETAILtemp
from books
where ISBN = array(i);
Finally, the assignment to vRETAILtemp can be simplified, using a case expression (not a case statement, which is a construct very similar to the if... then... elsif... else... end construct):
vRETAILtemp := vRETAILtemp * case vCATEGORYtemp when 'COMPUTER' then 0.7
when 'FITNESS' then 0.6
when 'BUSINESS' then 0.8
else 0.9 end;
There are plenty of errors in there :
You have a type called arry but you have also a variable with the same name. PL/SQL doesn't like that.
In the for loop : You are trying to loop over the type and not the array
for i in 1..array.count loop
it should be :
for i in 1..arry.count loop
Generally speaking it's not common to use arrays in Oracle. Although it's possible but it's much more efficient to use SQL which is very simple and straightforward. Your code can be simplified to :
declare
Cursor Cur is select case
when vCATEGORYtemp = 'COMPUTER' then 0.7
when vCATEGORYtemp = 'FITNESS' then 0.6
when vCATEGORYtemp = 'BUSINESS' then 0.8
else vCATEGORYtemp = 'COMPUTER' then 0.9
end * vRETAILtemp as Output
from books
where ISBN in ('1059831198','0401140733','4981341710','8843172113');
R Cur%Rowtype;
begin
Open Cur;
loop
fetch Cur into R;
exit when Cur%notfound;
dbms_output.put_line(R.Output);
end loop;
Close Cur;
end ;
I think you have to review the basics of coding which are :
Making the efforts of naming the objects properly. When you have two things called array and arry you save 0.5 seconds writing your code but you have great chances to spend hours trying to investigate what the problem is in case of a bug.
In coding, we almost never write a big code and then check if it compiles. Is should be incremental especially as beginner. Write two lines and check it compiles, etc. Otherwise, it's complicated to know where the problem is coming from.
In Oracle, you can use SQL Developer, and put your code inside a Function/Procedure or Package. At least in case of a compilation problem, it will tell you the right line which has a problem.
Hope this helps.
You can do this more simply (but still using PL/SQL for this learning exercise):
begin
for r in (
select * from books
)
loop
r.retail := r.retail *
case r.category
when 'COMPUTER' then 0.7
when 'FITNESS' then 0.6
when 'BUSINESS' then 0.8
else 0.9
end;
dbms_output.put_line(r.isbn||' '||rpad(r.category,10)||' '||r.retail);
end loop;
end;
Or using an array:
declare
type book_tt is table of books%rowtype;
l_books book_tt;
begin
select *
bulk collect into l_books
from books b;
for i in 1..l_books.last loop
l_books(i).retail := l_books(i).retail *
case l_books(i).category
when 'COMPUTER' then 0.7
when 'FITNESS' then 0.6
when 'BUSINESS' then 0.8
else 0.9
end;
dbms_output.put_line(l_books(i).isbn||' '||rpad(l_books(i).category,10)||' '||l_books(i).retail);
end loop;
end;
(I've cheated a little bit by using select * and books%rowtype, because this is just an example and I am lazy. For real code it's usually better to list the actual columns you want explicitly.)
Test data:
create table books
( isbn varchar2(10)
, category varchar2(20)
, retail number(6,2) );
insert all
into books values ( '1059831198', 'COMPUTER', 10)
into books values ( '0401140733', 'CATS', 10)
into books values ( '4981341710', 'FITNESS', 10)
into books values ( '8843172113', 'BUSINESS', 10)
select * from dual;
Output:
1059831198 COMPUTER 7
0401140733 CATS 9
4981341710 FITNESS 6
8843172113 BUSINESS 8
I am doing a program in which the user inputs the wage, name and the number of working hours per month for a certain number of employees. This piece of code is supposed to recieve Nemp employees and then ask for Nemp names. The problem is, it always skips the first name, it displays 'Employee name:' twice and doesn't allow the user to insert the first one. I don't understand why this is happening, any help would be greatly appreciated!
program test;
uses crt;
var
i, Nemp : integer;
employee: array of string;
BEGIN
read(Nemp);
SetLength (employee, Nemp);
for i:=1 to Nemp do
Begin
writeln ('Employee name: ');
readln (employee[i]);
end;
END.
Dynamic arrays are zero based. You should loop from zero to Nemp-1. Or loop from zero to High(employee).
And as #Rudy and #trincot points out, to read the length of the employee array, use ReadLn(Nemp) to avoid unwanted input effects.
A tip:
Enable range and overflow check in the compiler when debugging. That would have detected the error at the high range.
DECLARE
avg_sal number(8,2);
c number(8);
total number(8,2);
at number(8);
a number(8);
rt number(8);
r number(8);
y number(8);
yr number(8);
c number(8);
ch number(8);
FUNCTION cmd_int(amt number,rate number,intrest number)
RETURN number
IS
cint number(8,2);
BEGIN
END;
FUNCTION smp_int(amt number,rate number,intrest number)
RETURN number
IS
sint number(8,2);
BEGIN
sint:=(amt*rate*intrest)/100;
return total/c;
END;
BEGIN
dbms_output.put_line('Enter amount :'||:a);
at:=:a;
dbms_output.put_line('Enter rate :'||:r);
rt:=r;
dbms_output.put_line('Enter year :'||:y);
yr:=y;
dbms_output.put_line('1 Compound Intrest');
dbms_output.put_line('2 Simple Intrest');
dbms_output.put_line('Enter your choice :'||:c);
ch:=c;
CASE
WHEN ch:=1 THEN
ci:=cmd_int(at,rt,yr);
dbms_output.put_line('Compound Intrest :'||ci);
WHEN ch:=2 THEN
si:=smp_int(at,rt,yr);
dbms_output.put_line('Simple Intrest :'||si);
END;
The above is the code which I want to execute there are two functions cmd_int and smp_int so can I able to execute more than one function inside one PL/SQL Block? Thank you!
Basically i want to execute 1st part when my choice is 1 and second part when my choice is 2.
Yes, you can define and call as many functions and procedures as you like (and subblocks - up to a limit anyway). The functionality of PL/SQL would be rather curtailed if you couldn't.
But the rest of the code has to be valid. You're missing an end case:
...
CASE
WHEN ch:=1 THEN
ci:=cmd_int(at,rt,yr);
dbms_output.put_line('Compound Intrest :'||ci);
WHEN ch:=2 THEN
si:=smp_int(at,rt,yr);
dbms_output.put_line('Simple Intrest :'||si);
END CASE;
END;
And AT is a reserved word in PL/SQL so you cannot have a variable with that name, you will need to change it.
You also seem to be confusing PL/SQL local variables (e.g. r) with bind variables (:r), and you're trying to have interaction between the block and the user, which PL/SQL is not designed for. Once the PL/SQL block is valid your client will either complain that not all variables are bound, or will prompt for them all before executing the block, and you will only see the 'prompt' strings from dbms_output after the block has executed.
Your application or client needs to gather all the values outside the PL/SQL block; Michael Schaefers has shown a common method for SQL Developer or SQL*Plus. You can still combine that with a PL/SQL block if that is part of your assignment.
To technically answer the question of the headline: yes, you can use more than one function inside a PL/SQL block, as the following example code shows:
set serveroutput on;
DECLARE
FUNCTION cmd_int(amt number,rate number,intrest number)
RETURN number
IS
cint number(8,2);
BEGIN
return 4;
END;
FUNCTION smp_int(amt number,rate number,intrest number)
RETURN number
IS
sint number(8,2);
BEGIN
return 5;
END;
BEGIN
dbms_output.put_line('Result 1: ' || cmd_int(1,2,3));
dbms_output.put_line('Result 2: ' || smp_int(1,2,3));
END;
/
Executing this block yields to
Result 1: 4
Result 2: 5
Now to your problem:
I suggest you create two separate functions cmd_int and smp_int via CREATE OR REPLACE FUNCTION ... that do, what you want. Since these two are used in the same logical context you can also create a package CREATE PACKAGE INTEREST and define both functions within this package.
Then, to ask for user input and to actally use the functions I suggest you stick to an sqlplus script using the ACCEPT command or to handle everything in your client application (should you have one).
See Oracle Documenation on CREATE FUNCTION, CREATE PACKAGE and sqlplus ACCEPT
The basic Idea of an sqlplus script would be
SET SERVEROUTPUT ON;
ACCEPT a NUMBER PROMPT 'Enter amount: ';
ACCEPT r NUMBER PROMPT 'Enter rate: ';
ACCEPT y NUMBER PROMPT 'Enter year: ';
ACCEPT c NUMBER PROMPT '1 Compount Interest, 2 Simple Interest: ';
SELECT CASE WHEN &&c = 1 THEN cmd_int(a,r,y) ELSE smp_int(a,r,y) END AS Interest FROM DUAL;
More infos about using sqlplus can be found here
Yes. You can define more functions and procedures within a pl/sql block.
I'm working with stored procedures in netezza.
I want to loop over a range of values.
The upper bound on the loop is passed as a variable into the sproc by the user.
i.e. EXECUTE SPROC(12);
so problem is that Netezza (aginity workbench) won't accept this input variable as the upper bound on the loop.
i.e.
DECLARE
x alias as $1.
begin
for i in 1..x loop
...do stufff...
end loop;
end;
I know that this can be solved using loop and exit style loop but It's eating me up as to why i can't do the above given that the documentation suggests that it's possible to do so.
Anyone know why this doesn't work or how to make it work?
Thanks.
Clancy.
Please find below working example -
CREATE OR REPLACE PROCEDURE generateTime(integer)
LANGUAGE NZPLSQL RETURNS varchar(255) AS
BEGIN_PROC
DECLARE
p_abc integer;
p_bcd integer;
p_var1 ALIAS FOR $1;
BEGIN
p_bcd := ISNULL(p_var1, 10);
raise notice 'p_bcd=%',p_bcd;
FOR p_abc in 0..(p_bcd)
LOOP
raise notice 'Hello World %', p_abc;
END LOOP;
END;
END_PROC;
Hope this will help.