Type body created with compilation error - oracle

customer_ty object I have created has a nested table including
CREATE TYPE deposit_ty as object(
depNo number,
depCategory ref depcategory_ty,
amount number,
period number
)
/
CREATE TYPE deposit_tbl as table of deposit_ty
/
CREATE TYPE customer_ty as object(
custId varchar2(4),
custName varchar2(10),
address address_ty,
dob date,
deposits deposit_tbl
)
/
I have written a code to compute the total amount deposited by each client. Here is the code I've written;
alter type customer_ty
add member function totDeposits return number cascade
/
create or replace type body customer_ty as
member function totDeposits
return number is
total number;
BEGIN
select sum(d.amount) into total
from table(self.deposits) d;
group by self.custId,self.custName
return total;
END totDeposits;
END;
/
But I get a warning saying that the "type body created with compilation errors". What can I do to get rid of this?

If you do show errors immediately after you get the 'created with compilation errors' message, you'll see something like:
LINE/COL ERROR
-------- ------------------------------------------------------------------------------
8/5 PLS-00103: Encountered the symbol "GROUP" when expecting one of the following:
You can also query the user_errors or all_errors view to see the outstanding errors against any PL/SQL objects.
In this case it's a simple typo; you have the semicolon in the wrong place; instead of:
select sum(d.amount) into total
from table(self.deposits) d;
group by self.custId,self.custName
it should be:
select sum(d.amount) into total
from table(self.deposits) d
group by self.custId,self.custName;
In your version the ... d; is terminating that SQL statement - which would be invalid now because that truncated statement doesn't have a group by clause; but it doesn't get as far as complaining about that because it sees the group by ... as a separate statement, and that isn't the start of anything PL/SQL recognises as a query, statement, control loop etc., so it gives up.

Related

How to use sequence in a function in PLSQL?

In my schema, I have a reference number column in my settlement
table with a null value of varchar2 type, that I want to update it continuously.
Then I've created a sequence called ref_seq_num using a built-in function.
I want to use it (ref_seq_num) within my function get_ref_num to update the sequence ref. number to my settlement table,
which the return type also is varchar2 and I have a function like below
CREATE OR REPLACE FUNCTION get_ref_num RETURN settlement.ref_nr %TYPE IS
v_ref_seq settlement.ref_nr%TYPE;
BEGIN
v_ref_seq := to_char(sysdate, 'YYYYMMDD')||LPAD(ref_seq_num.nextval, 8,'0');
RETURN v_ref_seq;
END get_ref_num;
However, I bum into this error message 1/55 PLS-00302: component 'ref_nr' must be declared. I also tried changing the data type to varchar2 and error message is PLS-00215: String length constraints must be in range (1 .. 32767) How can I fix it?
According to your code, it seems that there's a table whose name is SETTLEMENT, but it doesn't contain the REF_NR column.
The following example shows how to do that:
SQL> create sequence ref_seq_num;
Sequence created.
A table that does contain the REF_NR column (which is then used in the function):
SQL> create table settlement (ref_nr varchar2(20));
Table created.
Your code, unmodified:
SQL> CREATE OR REPLACE FUNCTION get_ref_num RETURN settlement.ref_nr %TYPE IS
2 v_ref_seq settlement.ref_nr%TYPE;
3 BEGIN
4 v_ref_seq := to_char(sysdate, 'YYYYMMDD')||LPAD(ref_seq_num.nextval, 8,'0');
5 RETURN v_ref_seq;
6 END get_ref_num;
7 /
Function created.
Testing:
SQL> select get_ref_num from dual;
GET_REF_NUM
--------------------------------------------------------------------------------
2019050400000001
SQL>
If you have a column called ref_nr within settlement table, you code must work properly. I think the problem in the second case raises due to missing data precision part ( should be such as varchar2(16) ) for defining the variable as v_ref_seq varchar2. I would prefer using a numeric type such as number or int to hold the values for ref_nr, since they are all numeric, and this data type protects the data remain as numeric. Whenever you need to query you may use to_char function preventing exponential display( select to_char(ref_nr) from settlement ).
Moreover, if you use Oracle 12c version, you don't need to create such an extra function, just alter your table so that being sequence as your default for the column :
alter table settlement
modify ref_nr default to_char(sysdate, 'yyyymmdd')||lpad(ref_seq_num.nextval, 8,'0');

Oracle SQL: Stored procedure - object invalid

I was just about to create a store procedure and it worked so far.
(For learning purposes I want to hand a credit card number over to a stored procedure which should return the associated customer identification number.)
But when I wanted to test this procedure using
BEGIN CC_TO_CID(:p1, :p2);
END;
(the input data was submitted via a dialogue of my SQL IDE)
it just returned:
SQL Error [6550][65000]: ORA-06550: Row 1, Column 7: PLS-00905
Object xyz.CC_TO_CID is invalid ORA-06550: Row 1, Column 7:
PL/SQL: Statement ignored
This basically means that my stored procedure isn't well formatted but I really don't have any clue.
My stored procedure:
CREATE OR REPLACE PROCEDURE CC_TO_CID(in_cc_nr IN NUMBER(16,0), out_cid OUT NUMBER) IS
BEGIN
SELECT PM.CUSTOMER_ID INTO cid FROM "2_PAYMENT_M" PM,
"2_CREDITCARD" CC
WHERE CC.CC_NR=in_cc_nr AND CC.PAYMENT_M_NR=PM.PAYMENT_M_NR;
END;
My table structure with some test data:
Table: "2_CREDITCARD"
CC_NR PAYMENT_M_NR NAME CVV EXPIRES
------------------ -------------- -------------- ----- ---------------------
5307458270409047 1 Haley Harrah 52 2019-11-01 00:00:00
Table: "2_PAYMENT_M"
PAYMENT_M_NR CUSTOMER_ID CREATED TRANSACTION_LIMIT
-------------- ------------- --------------------- -------------------
1 100 2018-01-21 00:00:00 1.000
Thanks in advance!
I appreciate any useful hints.
You will have seen an error when you compiled the procedure, but it would probably have been quite generic. Your client may support show errors, or you can query the user_errors view to see the details.
You can’t give a size or precision restriction for the data type of a formal parameter to a function or procedure, so NUMBER(10,0) should just be NUMBER; and you have got the name of the argument wrong in your into clause.
CREATE OR REPLACE PROCEDURE CC_TO_CID(in_cc_nr IN NUMBER, out_cid OUT NUMBER) IS
BEGIN
SELECT PM.CUSTOMER_ID
INTO out_cid
FROM "2_PAYMENT_M" PM
JOIN "2_CREDITCARD" CC
ON CC.PAYMENT_M_NR=PM.PAYMENT_M_NR
WHERE CC.CC_NR=in_cc_nr;
END;
I’ve switched to ANSI join syntax because... well, just because. Untested as I don’t have your tables; if it still gets errors then check user_errors again.

How should I use an object type in an insert DML statement?

I have created two TYPE objects to try out OOP processing in PL/SQL.
I tried to use my type o_customers in my INSERT statement, but I could not do it.
There is a Customers table. It has same columns as o_customers.
create or replace type o_customers as object (
id number,
name varchar2(40),
age number,
address o_addressC,
salary number
);
create or replace type o_addressC as object (
mahalle varchar(30),
apartman varchar(15),
ilce varchar(15),
apt_no number
);
declare
adres o_addressC;
musteri o_customers;
begin
adres := o_addressC('selami ali mah','çınar apt',' üsküdar',19);
musteri:= o_customers(10,'UĞUR SİNAN SAĞIROĞLU',26,adres,1000);
insert into customers values (musteri);
end;
" There is a customers table. it has same columns with o_customers"
In OOP it is not enough for objects to have the same structure to be compatible in a programming context: they must be the same type, or related to each other through inheritance.
So you need to create the table using that type:
SQL> create table customers of o_customers
2 /
Table created.
SQL> desc customers
Name Null? Type
---------------------- -------- -------------
ID NUMBER
NAME VARCHAR2(40)
AGE NUMBER
ADDRESS O_ADDRESSC
SALARY NUMBER
SQL>
Now your insert statement will work:
SQL> declare
2 adres o_addressC;
3 musteri o_customers;
4 begin
5 adres := o_addressC('selami ali mah','cınar apt','uskudar',19);
6 musteri:= o_customers(10,'UĞUR SİNAN SAĞIROĞLU',26,adres,1000);
7 insert into customers values(musteri);
8 end;
9 /
PL/SQL procedure successfully completed.
SQL> select * from customers;
ID NAME AGE
---------- ---------------------------------------- ----------
ADDRESS(MAHALLE, APARTMAN, ILCE, APT_NO)
------------------------------------------------------------------------------------------------------------------------------------------------------
SALARY
----------
10 UĞUR SİNAN SAĞIROĞLU 26
O_ADDRESSC('selami ali mah', 'c??nar apt', ' uskudar', 19)
1000
SQL>
Incidentally I had to make minor changes to the inserted values because the posted statement hurled
declare
*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
ORA-06512: at line 6
This is because your o_addressC type attributes are too small for strings with multi-byte characters.
Unless customers is an object table (create table customers of o_customers), you'll need to refer to the object's properties explicitly:
insert into customers
( id, name, age, address, salary)
values
( musteri.id, musteri.name, musteri.age, musteri.address, musteri.salary );
By the way, o_customer (no 's') would make more sense than o_customers for an object name.

Create Type based on an exiting Table

As the title said : I want to create a type in oracle based on an existing Table.
I did as follow :
create or replace type MY_NEW_TYPE as object( one_row EXISTING_TABLE%rowtype);
The Aim is to be able to use this into a function which will return a table containing sample row of the table EXISTING_TABLE :
create or replace function OUTPUT_FCT() return MY_NEW_TYPE AS
...
If you only need to create a function that returns a row from your table, you could try something like the following, without creating types.
setup:
create table EXISTING_TABLE( a number, b varchar2(100));
insert into EXISTING_TABLE values (1, 'one');
function:
create or replace function OUTPUT_FCT return EXISTING_TABLE%rowtype AS
retVal EXISTING_TABLE%rowType;
begin
select *
into retVal
from EXISTING_TABLE
where rownum = 1;
--
return retVal;
end;
function call
SQL> begin
2 dbms_output.put_line(OUTPUT_FCT().a);
3 dbms_output.put_line(OUTPUT_FCT().b);
4 end;
5 /
1
one
However, I would not recommend such an approach, because things like select * can be really dangerous; I would much prefer defining a type with the fields I need, and then explicitly query my table for the needed columns.
No, you can't do that, you'll get a compilation error:
create or replace type my_new_type as object(one_row t42%rowtype);
/
Type MY_NEW_TYPE compiled
Errors: check compiler log
show errors
Errors for TYPE STACKOVERFLOW.MY_NEW_TYPE:
LINE/COL ERROR
-------- -----------------------------------------------------------------------
0/0 PL/SQL: Compilation unit analysis terminated
1/36 PLS-00329: schema-level type has illegal reference to MYSCHEMA.T42
You will need to specify each field in the object type, and you will have to specify the data types manually too - you can't use table.column%type either.
You could create the type dynamically based on column and data type information from the data dictionary, but as this will (hopefully) be a one-off task and not something you'd do at runtime, that doesn't really seem worth it.
You can create a PL/SQL table type based on your table's rowtype, but you would only be able to call a function returning that from PL/SQL, not from plain SQL - so you couldn't use it in a table collection expression for example. If you were only returning a single sample row you could return a record rather than a table, but the same applies. You can also have a function that returns a ref cursor which could match the table's structure, but you wouldn't be able to treat that as a table either.
Read more about object type creation in the documentation. Specifically the attribute and datatype sections.

procedure problem

create or replace
PROCEDURE XXB_RJT_HEADER_PROCEURE
(
V_PROD_ID IN NUMBER,
V_WARE_ID IN XXB_RJT_HEADER.WAREHOUSE_ID% TYPE,
V_PAY_METH IN XXB_RJT_HEADER.PAYMENT_METHOD% TYPE,
V_PAY_STAT IN XXB_RJT_HEADER.PAYMENT_STATUS% TYPE,
V_ORD_ID IN XXB_RJT_HEADER.ORDER_ID% TYPE,
V_ORD_DT IN XXB_RJT_HEADER.ORDER_DATE% TYPE )
AS
V_PROD_NM VARCHAR2(50);
V_WAR_NM VARCHAR2(15);
BEGIN
SELECT PRODUCT_CAT
INTO V_PROD_NM
FROM xxb_rjt_inventory
WHERE XXB_RJT_INVENTORY.product_id= V_prod_id;
SELECT WAREHOUSE_NAME
INTO V_WAR_NM
FROM xxb_rjt_inventory
WHERE XXB_RJT_INVENTORY.product_id= V_prod_id;
INSERT
INTO XXB_RJT_HEADER
( /*second error*/
warehouse_id,
PAYMENT_METHOD,
payment_status,
product_name,
order_id,
wareshouse_name,
order_date
)
VALUES
(
V_warehouse_id,
v_pay_meth, /*First error*/
V_pay_stat,
V_prod_nm,
V_ord_id,
V_war_nm,
V_ord_dt
);
END XXB_RJT_HEADER_PROCEURE;
when i compile this i get the following errors
Error(37,7): PL/SQL: ORA-00984: column not allowed here
Error(24,65530): PL/SQL: SQL Statement ignored
thanks for the help in advance
"V_warehouse_id" is no declared anywhere.
Your ORA-00984 error means:
A column name was used in an
expression where it is not permitted,
such as in the VALUES clause of an
INSERT statement.
Check the VALUES part of the INSERT to be sure none of the arguments are columns.
Once you fix that, see if the other error goes away. "PL/SQL: SQL Statement ignored" seems to appear after there's already another error.
You can rewrite is in something like (untested):
create or replace
PROCEDURE XXB_RJT_HEADER_PROCEURE
(
V_PROD_ID IN xxb_rjt_inventory.product_id%type,
V_WARE_ID IN XXB_RJT_HEADER.WAREHOUSE_ID% TYPE,
V_PAY_METH IN XXB_RJT_HEADER.PAYMENT_METHOD% TYPE,
V_PAY_STAT IN XXB_RJT_HEADER.PAYMENT_STATUS% TYPE,
V_ORD_ID IN XXB_RJT_HEADER.ORDER_ID% TYPE,
V_ORD_DT IN XXB_RJT_HEADER.ORDER_DATE% TYPE )
AS
BEGIN
INSERT
INTO XXB_RJT_HEADER
(
warehouse_id,
PAYMENT_METHOD,
payment_status,
product_name,
order_id,
wareshouse_name,
order_date
)
select
V_ware_id,
v_pay_meth,
V_pay_stat,
product_cat,
V_ord_id,
warehouse_name,
V_ord_dt
from xxb_rjt_inventory
where product_id= V_prod_id;
END XXB_RJT_HEADER_PROCEURE;
This means two less sql statements and two less variables to declare. Also change the name of the procedure, you write proceure instead of procedure. I also changed the type of the first parameter of your procedure.

Resources