Pl SQL Oracle PLS-00103: Encountered the symbol "CREATE" - sql

this code takes input for zipcode, city, and state and then inserts that into the table created Address. Prior to inserting data it will check if the zipcode is already in the table, if so calling procedure(error) to display an error code.
Im getting an error code pls-00103: encountered the symbol "CREATE" when trying to execute the code. Here is my code so far. Thanks for any help in advance.
drop table address;
create table address(zipcode NUMBER, city varchar2(30), state varchar2(20));
create or replace procedure error as
begin
dbms_output.put_line('Error Zip Code already found in table');
end error;
declare
zzip number;
ccity varchar2(30);
sstate varchar2(30);
create or replace procedure location(p_zipcode NUMBER,
p_city varchar2,
p_state varchar2) is
zip address.zipcode%type;
cit address.city%type;
st address.state%type;
begin
select count(*) from address into zip where zipcode = zip;
if any_rows_found then
error;
else
Insert into address values(zip, cit, st);
end if;
end location;
begin
select &zipcode into zzip from dual;
select &city into ccity from dual;
select &state into sstate from dual;
procedure location(zzip, ccity, sstate);
end;
/

I'm not sure what you're trying to do, but the following may be closer to what you had in mind:
drop table address;
create table address(zipcode NUMBER, city varchar2(30), state varchar2(20));
declare
zzip number;
ccity varchar2(30);
sstate varchar2(30);
procedure error is
begin
dbms_output.put_line('Error Zip Code already found in table');
end error;
procedure location(p_zipcode NUMBER, p_city varchar2, p_state varchar2) is
zip_count NUMBER;
begin
select count(*)
into zip_count
from address
where zipcode = p_zipcode;
if zip_count > 0 then
error;
else
Insert into address
(zipcode, city, state)
values
(p_zipcode, p_city, p_state);
end if;
end location;
begin
select &zipcode into zzip from dual;
select &city into ccity from dual;
select &state into sstate from dual;
location(zzip, ccity, sstate);
end;
/
Best of luck.

I dont know if I understand your problem correctly, but there are certain correction I'd like to address to answer your problem
First, if you are going to create a procedure/function, do it in a separate worksheet then compile it. Dont compile it together with other anonymous blocks because most of the time, if you dont end your other blocks with '/', errors will surely generate.
Second, your DECLARE statement is misplaced, if you are going to make an anonymous block, make sure DECLARE, BEGIN and END are in line, dont create a procedure/function inside an anonymous block.
Third, you are declaring variables in your procedures and using them but doesnt have an initial value, so it will just pass a null value to the DML statement in your procedure. just use the parameter directly.
Fourth, avoid creating a procedure that only contains dbms_output.put_line. Its silly.
Lastly, your anonymous block that should be calling your procedure, uses '&', please avoid using '&' inside pl/sql as '&' is a feature in SQL*Plus and doesnt have any meaning in PL/SQL, instead, you can use ':' as for binding variables. But you use '&' not in binding variables so you should remove that;
Try this:
drop table address;
/
create table address(zipcode NUMBER, city varchar2(30), state varchar2(20));
/
create or replace procedure location(p_zipcode NUMBER,
p_city varchar2,
p_state varchar2) is
zip address.zipcode%type;
begin
select count(*)
from address
into zip
where zipcode = p_zipcode
and city =p_city
and state = p_state;
if zip > 0 then
dbms_output.put_line('Error Zip Code already found in table');
else
Insert into address values(p_zipcode, p_city, p_state);
end if;
end location;
/
begin
location(:zzip, :ccity, :sstate);
end;

Related

Can we call procedure inside function in oracle PL/SQL? If not then why?

In some places I heard that we cannot call procedure inside function in oracle PL/SQL. May I know why is it so?
Also why cant we call a procedure within a SELECT statement whereas we can call a function in the same SELECT statement.
Yes you can call a procedure from a function in Oracle PL/SQL.
You can't call a procedure from a SELECT statement because it doesn't return a value. A function can be called from a SELECT because it does:
select empno, calc_salary_function(empno) salary
from emp;
Calling a procedure from a SELECT makes no sense really:
select empno, fire_employee(empno) -- Will fail
from emp;
What would you expect to see in the second column of the results?
Procedure is not used to return a value. Procedures performs group of operations to achieve a task, where as function is used to return a value.
Procedure is executed as an operation, function can be used in the select statement derive a value
you can call procedure from function.
create table test(a varchar2(30));
create or replace procedure pro_insert_values
as
begin
insert into test values('anything');
end;
create or replace function fn_get_data
return number
as
begin
pro_insert_values;
return 1;
end;
sql>variable x number
exec :x:=fn_get_data
You can use below code to validate that procedure can be called from inside function.
create table country_name(id number, country varchar2(100));
insert all
into country_name values(1, 'INDIA')
into country_name values(2, 'UK')
SELECT * FROM DUAL;
/
CREATE OR REPLACE FUNCTION COUNTRY_FOUND_NOTFOUND(V_NAME VARCHAR2)
RETURN VARCHAR2
AS
V_COUNTRY VARCHAR2(100);
BEGIN
BEGIN
SELECT COUNTRY INTO V_COUNTRY FROM country_name WHERE COUNTRY = V_NAME;
RETURN 'FOUND';
EXCEPTION
WHEN NO_DATA_FOUND THEN
INSERT_COUNTRY(V_NAME);
RETURN 'NOT FOUND';
END;
RETURN 'NOT FOUND';
END;
/
CREATE OR REPLACE PROCEDURE INSERT_COUNTRY(V_NAME IN VARCHAR2)
AS
V_ID NUMBER;
BEGIN
SELECT MAX(ID) INTO V_ID FROM COUNTRY_NAME;
INSERT INTO COUNTRY_NAME VALUES(V_ID+1, V_NAME);
END;
/
SET SERVEROUTPUT ON;
DECLARE
V_NAME VARCHAR2(100);
BEGIN
V_NAME := COUNTRY_FOUND_NOTFOUND('SPAIN');
DBMS_OUTPUT.PUT_LINE(V_NAME);
END;

how to populate database using procedures

I have about 15 different Tables filled with different data and different entity relationships.
I need to create a script which will populate my database with the content of those tables.
After script is finished, i run it in cmd, using sqlplus and later START path to file
i have two different sql files, one named db_spec.sql and another db_body.sql.
In my db_body.sql i've created a procedure to store data from two tables which have 1:N relationship.
First table
CREATE TABLE LOCATION (
ID_LOCATION INTEGER NOT NULL,
LOCATION_NAME VARCHAR2 (20) NOT NULL,
POSTCODE INTEGER NOT NULL
);
ALTER TABLE LOCATION
ADD (CONSTRAINT PK_LOCATION PRIMARY KEY (ID_LOCATION));
Second table
CREATE TABLE ADDRESS (
ID_ADDRESS INTEGER NOT NULL,
STREET VARCHAR2 (20) NOT NULL,
HOUSE_NUMBER INTEGER NOT NULL,
FK_ID_LOCATION INTEGER NOT NULL
);
ALTER TABLE ADDRESS
ADD (CONSTRAINT PK_ADRESS PRIMARY KEY (ID_ADRESS));
ALTER TABLE ADRESS
ADD (CONSTRAINT FK_ADRESS_ID_LOCATION FOREIGN KEY
(FK_ID_LOCATION) REFERENCES LOCATION(ID_LOCATION));
Now i need to populate them using data. Let's say
LOCATION_NAME = "London"
POSTOCDE = "394505" ...and so on
I've created this script, but as i run it, nothing shows up, so there's obviously some error in it.
db_spec.sql script
CREATE OR REPLACE PACKAGE apartment AS
PROCEDURE fill_location(location_number NUMBER);
PROCEDURE fill_address(number_of_addresses NUMBER);
END apartment;
db_body.sql script
SET SERVEROUTPUT ON
SET LINESIZE 400
SET TIMING ON
CREATE OR REPLACE PACKAGE BODY address AS
PROCEDURE fill_location(location_number NUMBER) IS
p_location_name VARCHAR2(20);
p_postcode NUMBER (10,2);
BEGIN
FOR num IN 1..location_number LOOP
p_location_name := 'Location';
p_postcode := dbms_random.value(1000,9600);
p_postcode := p_postcode ||' '|| TO_CHAR(num);
INSERT INTO LOCATION (ID_LOCATION, LOCATION, POSTCODE)
VALUES (num, p_location_name, p_postcode);
dbms_output.put_line(num);
END LOOP;
END fill_location;
PROCEDURE fill_address(number_of_adresses NUMBER)IS
p_street_name VARCHAR(20);
p_house_number NUMBER (10,2);
p_id_address NUMBER(10);
CURSOR data IS
SELECT ID_LOCATION
FROM LOCATION;
BEGIN
FOR num_loop IN data LOOP
FOR num IN 1..number_of_adresses LOOP
p_street_name := 'Ulica';
p_house_number := dbms_random.value(1,99);
p_street_name := p_street_name ||' '|| TO_CHAR(num);
SELECT NVL(MAX(p_id_address)+1,1)
INTO p_id_address
FROM ADDRESS;
INSERT INTO ADDRESS (ID_ADDRESS, FK_ID_LOCATION, STREET, HOUSE_NUMBER)
VALUES (p_id_address, num_loop.ID_LOCATION, p_street_name, p_house_number);
dbms_output.put_line(num_loop.ID_LOCATION);
END LOOP;
END LOOP;
END fill_address;
END;
SHOW ERRORS;
Can you guys please help me fix the problem so the code will run and work fine? Any input is appreciated!
Btw oprema_stanovanja.polni_kraj = address.fill_location
Unless I'm missing it... COMMIT;
Based on the screenshot you issued, add a / to the end of your body and procedure definitions.
Firstly, the package posted doesn't compile without errors.
You would need to fix the following first:
Terminate FOR LOOP statements with END LOOP
Terminate BEGIN of procedures with END <procedure_name_here>
PL/SQL variable names need to be different than the actual column name to avoid ambiguity in the INSERT statement. [ You can't have LOCATION the column name and PL/SQL variable with the same name. Oracle cannot resolve which is to be used where in the INSERT]
To get you started, I have fixed the fill_location procedure below. Proceed similarly for the other procedure.
create or replace package body apartment as
procedure fill_location(location_number number) is
p_location_name varchar2(20);
p_postcode number(10,2);
begin
for num in 1.. location_number loop
p_location_name := 'location';
p_postcode := dbms_random.value(1000,9600);
p_location_name := p_location_name ||' '|| to_char(num);
insert into location (id_location, location_name, postcode)
values (num, p_location_name, p_postcode);
dbms_output.put_line(num);
end loop;
end fill_location;
Fix the above errors/suggestions until you get 'PL/SQL successfully compiled`. If you get 'compiled with errors' use "show errors" to find and fix any other errors.

Calling a persistent package variable

Ive created a package and a package body that run but I'm unsure of how i can test the value held in my persistent package variable.
Here is the package code, its fairly simple just a delete procedure and create function which are in the package body.
CREATE OR REPLACE PACKAGE customers AS
FUNCTION create_customer (Country VARCHAR2, First_Name VARCHAR2, Last_name VARCHAR2,
Birth_date VARCHAR2, Customer_Type VARCHAR2, Address VARCHAR2)
RETURN VARCHAR2;
PROCEDURE remove_customer (customer_id VARCHAR2);
FUNCTION count_customer RETURN NUMBER;
END customers;
And now here is the package body
create or replace package body customers AS
total_customers NUMBER;
FUNCTION CREATE_CUSTOMER(
Country IN VARCHAR2 ,First_Name IN VARCHAR2 ,Last_Name IN VARCHAR2 ,Birth_Date IN VARCHAR2 ,Customer_Type IN VARCHAR2 ,Address IN VARCHAR2
) return VARCHAR2 IS
new_customer_id VARCHAR2(8);
BEGIN
SELECT custid_seq.NEXTVAL
INTO new_customer_id
FROM DUAL;
INSERT INTO customer (Customer_id, Country, First_Name, Last_name, Birth_date, Customer_Type, Address)
VALUES (new_customer_id, Country, First_Name, Last_name, Birth_date, Customer_Type, Address);
total_customers := total_customers + 1;
RETURN (new_customer_id);
end;
PROCEDURE remove_customer (customer_id VARCHAR2) IS
BEGIN
DELETE FROM customer
WHERE customer.customer_id = remove_customer.customer_id;
total_customers := total_customers - 1;
END;
BEGIN
select count(*) into total_customers from customer;
END;
The total_customers is my persistent package variable and only local to this package I just want to know how i can test to see the value currently held in the variable.
Thanks for the help
In your package specification add:
function get_total_customers return number;
In your package body add:
function get_total_customers return number is
begin
return total_customers;
end get_total_customers;
Then from SQL you can select customers.get_total_customers from dual.
The alternative is to have the package variable defined in the specification rather than the body, but then it could be changed externally as well as read.
Either way, the value will only be meaningful within a session; if you set it in one session then that won't be visible in another session. Both can calculate it, but then if you call create_customer() in one session, the other will still report the old total.
Put your variable to package spec, test it. Then put it back into the body or leave it in the spec. Keep it simple...

Stored Function in Oracle not Inserting Values into the desired table

Here is my Code given below. I am trying to add 5 parameters which the function takes into the table Employee. But I am not successful in doing it and have tried a lot of things.
Error
ORA-01858: a non-numeric character was found where a numeric was
expected ORA-06512: at "xxxxxxx.A1SF_ADDEMP", line 14
01858. 00000 - "a non-numeric character was found where a numeric was expected"
*Cause: The input data to be converted using a date format model was
incorrect. The input data did not contain a number where a number was
required by the format model.
*Action: Fix the input data or the date format model to make sure the
elements match in number and type. Then retry the operation.
Plus how do I test a Stored function that has a Insert/Update or Delete Statement in it?
Execution Statement
Select A1SF_ADDEMP('Adesh', '33', 'M', 8000, '26/03/1990')
From dual;
Code
CREATE OR REPLACE Function A1SF_ADDEMP
(pEmpName In Varchar2,
pTaxFileNo In Varchar2,
pGender In Varchar2,
pSalary In Number,
pBirthdate In Varchar2
) Return Varchar2
Is
tEmpId Number(38,0);
tBirthDate Date;
BEGIN
tEmpId := A1Seq_Emp.nextval;
tBirthdate := to_date('pBirthdate','dd/mm/yyyy');
Insert Into Employee(EmpId, EmpName, TaxFileNo, Gender, Salary, Birthdate)
Values (tEmpId, pEmpName, pTaxFileNo, pGender, pSalary, tBirthdate);
Commit;
Return null;
END;
Firstly you cannot call a function with DML in it in a select statement. You have to assign the output to a variable in a PL/SQL block, something like:
declare
l_output number;
begin
l_output := my_function(variable1, variable2);
end;
It's bad practice to do DML in a function; partly because it causes the errors you're coming across. You should use a procedure as detailed below. The other reason for this is that you're as always returning null there's no need to return anything at all!
create or replace procedure my_procedure ( <variables> ) is
begin
insert into employees( <columns> )
values ( <values > );
end;
The specific reason for your error is this line:
tBirthdate := to_date('pBirthdate','dd/mm/yyyy');
pBirthdate is already a string; by putting a ' around it you're passing the string 'pBirthdate' to the function to_date and Oracle can't convert this string into a day, month or year so it's failing.
You should write this as:
tBirthdate := to_date(pBirthdate,'dd/mm/yyyy');
You also don't need to specify number(38,0), you can just write number instead.
It is possible to return a value from a procedure using the out keyword. If we assume that you want to return empid you could write is as something like this:
create or replace procedure A1SF_ADDEMP (
pEmpName in varchar2
, pTaxFileNo in varchar2
, pGender in varchar2
, pSalary in number
, pBirthdate in varchar2
, pEmpid out number
) return varchar2 is
begin
pempid := A1Seq_Emp.nextval;
Insert Into Employee(EmpId, EmpName, TaxFileNo, Gender, Salary, Birthdate)
Values ( pEmpId, pEmpName, pTaxFileNo, pGender
, pSalary, to_date(pBirthdate,'dd/mm/yyyy');
end;
To just execute the procedure call it like this:
begin
A1SF_ADDEMP( EmpName, TaxFileNo, Gender
, Salary, Birthdate);
commit;
end;
If you want to return the empid then you can call it like this:
declare
l_empid number;
begin
l_empid := A1SF_ADDEMP( EmpName, TaxFileNo, Gender
, Salary, Birthdate);
commit;
end;
Notice how I've moved the commit to the highest level, this is to avoid committing stuff in every procedure when you might have more things you need to do.
Incidentally, if you're using Oracle 11g then there's no need to assign the value A1Seq_Emp.nextval to a variable. You can just insert it directly into the table in the values list. You, of course won't be able to return it, but you could return A1Seq_Emp.curval, as long as there's nothing else getting values from the sequence.
You should use a procedure (instead of a function) if you are not returning any values.
If you look at the line mentioned in the error message you can spot your error:
tBirthdate := to_date('pBirthdate','dd/mm/yyyy');
You are passing the string literal 'pBirthdate' to the to_date() call. But you want to pass the parameter, so it should be
tBirthdate := to_date(pBirthdate,'dd/mm/yyyy');
(note the missing single quotes arount pBirthdate).
So as a procedure the whole thing would look like this:
CREATE OR REPLACE PROCEDURE A1SF_ADDEMP
(pEmpName In Varchar2,
pTaxFileNo In Varchar2,
pGender In Varchar2,
pSalary In Number,
pBirthdate In Varchar2
)
IS
BEGIN
Insert Into Employee(EmpId, EmpName, TaxFileNo, Gender, Salary, Birthdate)
Values (A1Seq_Emp.nextval, pEmpName, pTaxFileNo, pGender, pSalary, to_date(pBirthdate,'dd/mm/yyyy'));
Commit;
END;
To run it:
execute A1SF_ADDEMP('Adesh', '33', 'M', 8000, '26/03/1990');
In your situation you need to use procedure with out parameter where your out param is your returning param that contains your desirable value. This is I think best practice in this situation when you want to use DML in select statement.
Second way to do it but its not a good practice is to use one function like yours but before return a value you need to use if statement to check what is the value and if value is what you desire to call in this if statement PRAGMA AUTONOMOUS_TRANSACTION procedure which will do your DML independently of function calling to it. For more information about pragma transactions your can read here:
http://docs.oracle.com/cd/B19306_01/appdev.102/b14261/autonotransaction_pragma.htm
Best Regards and hope I was helpful

Calling a member procedure in Oracle 11g

Lets say i have:
create type address as object (
line1 varchar2(50),
city varchar2(50),
member procedure insert_address(line1 varchar2, city varchar2)
)
/
create table address_table of address;
create type body address as
member procedure insert_address(line1 varchar2, city varchar2) is
begin
insert into address_table values (line1, city);
commit;
end insert_address;
end;
/
How do i call insert_address?
By doing the following i get invalid number or types of arguments
begin
address.insert_address('123 my road','london');
end;
i can do this and it works, but seems like a bad idea:
declare
v_address address := new address(null,null);
begin
v_address.insert_address('123 my road','london');
end;
Thanks
Use static instead of member for your procedure:
static procedure insert_address(line1 varchar2, city varchar2)
Then you can call it on the object type instead of the instance:
address.insert_address('123 my road','london');
See Using PL/SQL Object Types for more information.
As you have built it (which is bizarrely), the procedure insert_address can only be called in the context of an object of type address, and must be called with parameters line1 and city, with values that have no connection with the object you called it "for". This is how I would build the table and code, and use it:
create table address_table (line1 varchar2(50), city varchar2(50));
create package address_pkg as
procedure insert_address(p_line1 varchar2, p_city varchar2);
end;
/
create package body address_pkg as
procedure insert_address(p_line1 varchar2, p_city varchar2) is
begin
insert into address_table (line1, city) values (p_line1, p_city);
end;
end;
/
exec address_pkg.insert_address ('123 my road', 'london');
With your funkier model, it seems that the insert_address procedure should insert "the address object itself" into the table. Something like:
create type address as object (
line1 varchar2(50),
city varchar2(50),
member procedure insert_address
)
/
create table address_table of address;
create type body address as
member procedure insert_address is
begin
insert into address_table values (line1, city);
commit;
end insert_address;
end;
/
Then the insert would be like:
declare
v_address address := new address('123 my road','london');
begin
v_address.insert_address;
end;