I am new to Oracle and have just read that the scalar variable has no internal component whereas composite variable has an internal component.
Could you please explain what is this internal component?
How does it work? What is its purpose ?
You need to read the documentation about PL/SQL Records and Collections:
http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/composites.htm
A composite variable's internal components are simply the structure that makes up the variable itself.
e.g.
In a collection, the internal components always have the same data
type, and are called elements. You can access each element of a
collection variable by its unique index, with this syntax:
variable_name(index). To create a collection variable, you either
define a collection type and then create a variable of that type or
use %TYPE.
In a record, the internal components can have different data types,
and are called fields. You can access each field of a record variable
by its name, with this syntax: variable_name.field_name. To create a
record variable, you either define a RECORD type and then create a
variable of that type or use %ROWTYPE or %TYPE.
For example, if I create a record type:
TYPE person_rectype IS RECORD (
forename VARCHAR2(30),
surname VARCHAR2(30),
sex VARCHAR2(1),
dob DATE
);
then declare a variable of that type:
applicant_rec person_rectype;
The variable applicant_rec has the internal components forename, surname, sex and dob which are VARCHAR2 and DATE data types.
Hope it helps...
Related
I have a table like this:
CREATE TABLE T_C_EVO_GAME_CONFIG_CHANGE_LOG(
F_TABLE_MODIFIED VARCHAR2(40),
F_OPERATION_PERFORMED VARCHAR2(30),
F_ROWS_ALTERED INTEGER,
F_LAST_UPDATED_BY VARCHAR2(200),
F_LAST_UPDATED_DATE TIMESTAMP);
I am trying to build a type with the same structure:
create or replace type TYPE_EVOL_CONFIG_CHANGE_LOG as object
(
F_TABLE_MODIFIED T_C_EVO_GAME_CONFIG_CHANGE_LOG.TABLE_MODIFIED%TYPE ,
F_OPERATION_PERFORMED T_C_EVO_GAME_CONFIG_CHANGE_LOG.OPERATION_PERFORMED%TYPE,
F_ROWS_ALTERED T_C_EVO_GAME_CONFIG_CHANGE_LOG.ROWS_ALTERED%TYPE ,
F_LAST_UPDATED_BY T_C_EVO_GAME_CONFIG_CHANGE_LOG.LAST_UPDATED_BY%TYPE ,
F_LAST_UPDATED_DATE T_C_EVO_GAME_CONFIG_CHANGE_LOG.LAST_UPDATED_DATE%TYPE
);
Getting the below error message in creating the Type :
Error(3,25): PLS-00201: identifier 'T_C_EVO_GAME_CONFIG_CHANGE_LOG.TABLE_MODIFIED' must be declared.
Previously I tried to create the Type without using %TYPE and just simply copying the parameter definition and it worked.
But I don't want to make any changes in Type when I make any changes in Table.
The %TYPE syntax is for use in PL/SQL declarations. Unfortunately we cannot use it when creating SQL objects. Same goes for %rowtype.
It would be highly neat if we could, because one common use of create or replace type would be to build table APIs as you want to do. However, it would be too complicated to manage referencing constructs in the data dictionary; bear in mind that Types can be used to define other objects including Table columns.
So alas, you need to declare the Type with explicit datatypes for its attributes:
create or replace type TYPE_EVOL_CONFIG_CHANGE_LOG as object
(
F_TABLE_MODIFIED VARCHAR2(40) ,
F_OPERATION_PERFORMED VARCHAR2(30),
F_ROWS_ALTERED INTEGER ,
F_LAST_UPDATED_BY VARCHAR2(20) ,
F_LAST_UPDATED_DATE DATE
);
Obviously you also need to sync it manually whenever the structure of any T_C_EVO_GAME_CONFIG_CHANGE_LOG column changes. But you would have to do this anyway if you added or dropped a column.
Alternatively you can define the type as a PL/SQL record in a package. That would allow you to use the referencing syntax.
create or replace package game_config as
TYPE_EVOL_CONFIG_CHANGE_LOG is record
(
F_TABLE_MODIFIED T_C_EVO_GAME_CONFIG_CHANGE_LOG.F_TABLE_MODIFIED%TYPE ,
F_OPERATION_PERFORMED T_C_EVO_GAME_CONFIG_CHANGE_LOG.F_OPERATION_PERFORMED%TYPE,
F_ROWS_ALTERED T_C_EVO_GAME_CONFIG_CHANGE_LOG.F_ROWS_ALTERED%TYPE ,
F_LAST_UPDATED_BY T_C_EVO_GAME_CONFIG_CHANGE_LOG.F_LAST_UPDATED_BY%TYPE ,
F_LAST_UPDATED_DATE T_C_EVO_GAME_CONFIG_CHANGE_LOG.F_LAST_UPDATED_DATE%TYPE
);
-- or even
TYPE TAB_EVOL_CONFIG_CHANGE_LOG is table of T_C_EVO_GAME_CONFIG_CHANGE_LOG%rowtype;
end;
It depends how you want to use the Type in your broader application.
I have a stored procedure(Oracle 11g) and inside the Procedure I have this line.
v_prazo_subs_ans ts_odo.odo_controle_sistema.val_parametro%type;
Where I have:
My variable: v_prazo_subs_ans
My table: ts_odo.odo_controle_sistema
The field of my table: val_parametro
What does %type after the name of the field mean?
%TYPE
Represents the datatype of a previously declared collection, cursor
variable, field, object, record, database column, or variable.
in other words instead of using VARCHAR2, NUMBER, etc... you can just say the parameter of my procedure has same type as a column. this is really useful when you need change type of column in your table, you don't need make any changes in your pl/sql code
see docs here:
Constant and Variable Declaration
Procedure Declaration
When creating a type of object, is it possible to declare the type as TABLE.COLUMNNAME%TYPE?
e.g.
CREATE OR REPLACE TYPE PROJECT_TYPE IS OBJECT
(
project_id project.project_id%TYPE,
project_desc project.project_desc%TYPE
);
or I have to specify the type and width at the time of creation? Reason why this question is if table is altered then I have to change to data type and width of type OBJECT as well?
It is, unfortunately, not possible. You have to provide actual type, you can not reference a %TYPE of a table's column.
The reason for that is that both %TYPE and %ROWTYPE are PL/SQL constructs, which are not supported in SQL.
i want to retrieve type of elements varray stores through type attribute or ANY work around.
for example our type is defined like this
CREATE TYPE "READINGS" AS VARRAY (200) OF NUMBER(21, 6);
(readings is varray with elements of type number(21,6))
READINGS is a column in a table INTERVALS. INTERVALS is a central table and we have batch processes on INTERVALS which execute sql store procedures. In store procedure we have hard coded variable declarations mapping to the READING VArray type element type which is NUMBER(21, 6) for example the store procedure has variable declarations like
CONSUMPTION NUMBER(21, 6);
whenever Varray definition is changed or varray is dropped and recreated with different size and precision, ex instead on number(21,6) is changed to number(25,9) we need to change our variable declarations in all batch process store procedures.
All i am looking for is making CONSUMPTION variable declaration, refer to element type of VArray. I want something like this
CONSUMPTION INTERVALS.READINGS.COLUMN_TYPE%TYPE;
(i want some thing like this, refer to the type of elements stored by varray)
Why are you creating a table with a VARRAY column in the first place? It would generally make far more sense to create a separate table for READINGS with a foreign key that lets you relate the rows back to the INTERVALS table. You could then easily enough declare columns of type READINGS.COLUMN_NAME%TYPE.
Collections are wildly useful in PL/SQL. I've never seen a case where they improved on a standard normalized approach to data modeling. I have seen multiple cases where incorporating collections into your data model ends up making your data model harder to work with and your code harder to write and maintain.
If you don't want to fix the data model, you can
Declare a SUBTYPE or a packaged variable of type NUMBER(21, 6) that you use as the type for your variable declarations. You'll have to change this definition if and when you change the declaration of the VARRAY type.
Create an object type with a single attribute (a NUMBER(21,6)) and define the VARRAY based on that object type. Then you can declare instances of the object type in your code.
This is not a required solution, but you can get string of type definition for further using it in dynamic SQL
SELECT
regexp_substr(text, 'VARRAY.*?OF\s+(.+?)(;|\s)*$', 1, 1, 'i', 1)
FROM user_source
WHERE name = 'READINGS'
I was using flat stored procedures (flat means not contained in objects) in oracle to update my tables. For example I have a table Person with columns Id, FirstName, LastName, Address, Salary. I made a flat procedure Person_UpdFirstName and this procedure has two parameters: Id, FirstName. Inside the procedure, I find the row in the Person table that matches with the parameter Id and update the FirstName with the parameter FirstName. Usual stuff, nothing new.
Now, I am using oracle objects. I have an object PersonType, this is a udt. This object has same fields as columns in the table Person. I have put all of the procedures related to the Person table inside the PersonType object, that is, instead of using flat procedures I start using member procedures. None of the member procedures has any parameter, they take values from the fields of the object. For example, in the case of Person_UpdFirstName flat procedure, now I have a member procedure UpdFirstName. This member procedure do not take any parameter, it uses the Id and FirstName fields of the object itself, and update the Person table as before.
The problem is, when I was using flat procedures, I was passing parameters such as Id, FirstName, so in a large system with hundreds of tables, I cannot make a mistake in passing parameters to the stored procedure because number and type of parameters in each stored procedure is fixed. Now that I am using objects, I have to remember what fields of the object to be filled, there is no built in check in the system. This is fine as long as the fields in the table Person are non-nullable because it would throw an exception anyways but if the fields in the table are nullable, or when I am comparing values, then I can have lots of logical errors.
My question is, is there some built-in way to close this door of potential errors. I have some rough solutions but not sure:
Some kind of partial objects. My member methods should be forced to take in parameter those partial objects. For example I have a partial object PersonUpdFirstNameType, this has only one field FirstName and then my UpdFirstName member method take this as a parameter. Ofcourse its cumbersome to make a separate partial-type for each operation on a table. I don't really like this solutionI don't pass objects from c# to oracle procedures, instead I pass variables in parameters and then manually build (or not build) oracle objects as needed.
I have found out a way to map oracle objects with c# classes. For this, I don't have to use any ORM tool. I just have to add up a few attributes on c# classes and c# fields of those classes and implement a few interfaces. So, I can actually pass c# objects to oracle procedures and use "." syntax in oracle procedures to access the fields which contains the actual data.
I think the problem I am asking is a general oop problem, so its not specific to any particular language. The general problem, is suppose you have a class C with fields F1, F2, F3, F4, F5 and methods M1, M2, M3. M1 do some operation on some of the fields, M2 do some operation on some other fields, M3 do some operations on some fields which may also be acted upon by M1 or M2. A client code is making objects of C and can fill any number (including zero) of the parameters before calling any method. What if the client code call a method before putting values in the fields required by the method. In C#, I think this is handled by compiler by throwing exceptions if you not initialize the fields first; you can also leave the fields empty in definition of class such as "int i" without putting any value in i so that if a method is called now compiler throw an exception. There is no such support in dbms because of the nullable fields. If lets say you are comparing Id of a table-row against Id of an object-field and you forgot to put any value in the object-field then the table-row's id is compared against null and no row is matched and therefore no update happens (suppose you want to update rows which match the id, usual update operation).
I just want to know if there is some built-in check in the system to handle such cases.
I don't have any idea how to have an Error in compilation time, and I don't know of any other OO language that gives such a feature either (how can the compiler tell when or where the attribute was initiated ?)
What you can do is have Exceptions in runtime (somewhat like NullPointerException or ArgumentNullException).
For example:
create or replace type person_o as object
(
id number,
fname varchar2(32),
lname varchar2(32),
member procedure update_lname
);
/
create or replace type body person_o is
member procedure update_lname is
begin
if self.lname is null then
Raise_application_error(-20000, 'null attribute');
end if;
update persons_table set last_name = self.lname where id = self.id;
commit;
end;
end;
/