I'm studying databases and am currently working on a object-relational DB project and I've encountered a small problem with the number of possible constraints in an object table. I'm using "Database Systems: The Complete Book" by Hector Garcia-Molina (and other authors) as a reference and there's a general SQL example like this:
CREATE TYPE StarType AS (
name CHAR(30),
address AddressType,
bestMovie REF(MovieType) SCOPE Movies
);
Now, I have a kind of a similar type in my project, as it also uses reference to another type within a type, but the clause for placing a reference there doesn't include SCOPE in Oracle (at least I haven't found it in the docs and it outputs an error). So I have a type like this:
CREATE OR REPLACE TYPE "ApplicationType" AS OBJECT (
"person" REF "PersonType",
"competition" REF "CompetitionType",
"dateApplied" DATE
);
/
...which works. But when I want to constrain the REF columns, I can constrain only one, as so:
CREATE TABLE "Applications" OF "ApplicationType" (
"person" SCOPE IS "People" /* or "competition" SCOPE IS "Competitions" */
)
OBJECT IDENTIFIER IS SYSTEM GENERATED;
Is there any way to give constraints to both REF columns?
This works just fine:
CREATE TABLE Applications OF ApplicationType (
person SCOPE IS People,
competition SCOPE IS Competitions
)
OBJECT IDENTIFIER IS SYSTEM GENERATED;
Maybe you tried creating the table using or instead of , for separating the constraints(as seen in your comment).
It's also easy to test your constraints. Just create these two additional dummy tables:
CREATE TABLE People2 OF PersonType
OBJECT IDENTIFIER IS SYSTEM GENERATED;
CREATE TABLE Competitions2 OF CompetitionType
OBJECT IDENTIFIER IS SYSTEM GENERATED;
Then:
INSERT INTO People VALUES('p1');
INSERT INTO People2 VALUES('p21');
INSERT INTO Competitions VALUES('c1');
INSERT INTO Competitions2 VALUES('c21');
COMMIT;
INSERT INTO Applications
VALUES
(
(SELECT REF(p) FROM People p WHERE person = 'p1'),
(SELECT REF(c) FROM Competitions2 c WHERE competition = 'c21'),
SYSDATE
);
results in an ORA-22889 since the refered value is not in the specified scoped table(which is Competitions, not the dummy Competitions2).
You can test similarly using People2 instead of People.
Related
The example used does not represent the domain I am actually solving for; it's just for example.
Background
I am defining table inheritance. Planning to use Table API packages & views to handle the necessary logic & presentation.
I also need to define multiple multi-level named hierarchies with disparate attributes.
Combining table inheritance (Location > State, City) & named hierarchies (State <- City), however, is where I've come across a design snag.
The Problem
I want to define a Unique Key enforcing Name of City is Unique within State, but the abstraction of the Name field is preventing that. I can handle it in PL/SQL if needed, but I would like to enforce it at the SQL level (ideally without an additional table) if at all possible.
Constraints
Other tables need to be able to refer to any level of the hierarchy generically (location).
Other tables also need to be able to refer to a specific level of the hierarchy.
Thoughts
I cannot do single table inheritance due to the complexity of the graph of relations.
I have thought about duplicating the Name field for ease, though I'd rather not violate Normal Forms.
Example
CREATE TABLE location
( id NUMBER ( 38) PRIMARY KEY
, name VARCHAR2(1000) NOT NULL
, type_id NUMBER ( 38) NOT NULL
--, other common/super attributes
);
CREATE TABLE state
( id NUMBER(38) PRIMARY KEY
REFERENCES location (id)
--, child attributes
);
CREATE TABLE city
( id NUMBER(38) PRIMARY KEY
REFERENCES location (id)
, state_id NUMBER(38) NOT NULL
REFERENCES state (id)
--, child attributes
);
I don't see why requiring city and state names to be unique (and therefore holding them in the relevant table) would violate any normal form. city name is not the same attribute as state name "New York" city != "New York" state for example.
Adding a "parent_location" to location table maybe works for you.
CREATE TABLE location
( id NUMBER ( 38) PRIMARY KEY
, name VARCHAR2(1000) NOT NULL
, type_id NUMBER ( 38) NOT NULL
, parent_location_id number( 38) NOT NULL
--, other common/super attributes
);
CREATE UNIQUE INDEX location_u2
ON location (parent_location_id, name);
CREATE TABLE city
( id NUMBER(38) PRIMARY KEY
REFERENCES location (id)
, --state_id gone away
--, child attributes
);
It also would prevent having two states with the same name.
The disadvantage is you'll need to use a dummy "0" location_id for the top node.
Best regards.
I am trying to create an oracle table of oracle-object type.
Here is how my object structure looks like
CREATE OR REPLACE TYPE PERS_T AS OBJECT
(
empno number(4)
, ename varchar2(10)
, job varchar2(9)
, hiredate DATE
, sal number(7,2)
, comm number(7,2)
, deptno number(2)
)NOT FINAL;
CREATE OR REPLACE TYPE EMP_T FORCE UNDER pers_t (
mgr pers_t
);
All these are fine, but what when I am trying to create a table of EMP_T type using
CREATE TABLE table_name(emp_type EMP_T);
I am getting error
SQL Error: ORA-30756: cannot create column or table of type that contains a supertype attribute
Is it possible in oracle to create table like this?
I don't think so. According to Oracle's own support database, the following applies to a ora-30756
Error Text, Cause and Action from Message File/s for ORA-30756
Versions 9.2, 10.1, 10.2, 11.1, 11.2, 12.1
Error: ORA-30756 cannot create column or table of type that contains a supertype attribute
Cause: The user tried to create a column or table of an object type that
contains a supertype attribute. This is not supported because it leads
to infinite recursion in our current storage model. Note that creating
a column of a type implies that we create columns corresponding to all
subtype attributes as well.
Action: Change the type definition to contain a supertype REF attribute
instead of the supertype object attribute.
You have created a super type PERS_T so I believe this is your problem. As the article states, you can create a reference attribute instead of an object attribute as a work around as #tbone explained - CREATE TABLE table_name(emp_type REF EMP_T);
You are not permitted to sub-type an IS-A relationship using the UNDER keyword simultaneously with a HAS-A relationship using the same type. This ultimately translates to creating an object that refers to itself and it would lead to infinite recursion. You can instead have a pointer to the object of the same type within your code using REF as follows:
CREATE OR REPLACE TYPE EMP_T UNDER PERS_T (
mgr REF PERS_T
);
create table table_name(emp_type emp_t);
desc table_name;
Name Null Type
-------- ---- -------
EMP_TYPE EMP_T()
IS-A and HAS-A relationships in Oracle type inheritance
IS-A and HAS-A relationships both enable polymorphism and reusability of code but they define fundamentally different relationships between two types.
IS-A
The UNDER keyword is intended to sub-type relationships using the IS-A inheritance model. For example, employee IS-A person who has an emp_id as defined using:
create type person_type as object(ssn number, address varchar2(100)) NOT FINAL;
-- Employee is a **person** with an emp_id.
create type employee_type under person_type(emp_id number)
The idea is that an object of employee_type is also an object of person_type but with the additional specification of the emp_id. This lets for the inheritance model of polymorphism by enabling code reuse via extension of the person_type supertype. Note that it is always possible to define another extension of the person_type student as follows:
-- Student is a person with a student_id
create type student_type under person_type(student_id number)
HAS-A
As such, Oracle does not force upon us any keywords when attempting to create a composite sub-type. It is not syntactically inappropriate to define a sub-type containing one or more super-types as freely as other native types. For example, it is legal to define a composite sub-type classroom_type by composing it with a teacher of type person, 3 students each of type person as well as a room number of type number such as:
create type classroom_type as object ( teacher person_type,
student1 person_type,
student2 person_type,
student3 person_type,
room_number number)
The HAS-A relationship exists between two objects when one object belongs-to the other i.e. the former object consists of the latter.
However, this practice can cause unnecessary replication of large objects as you are copying the data from one place to another. To make things more efficient, akin to the concept of passing by reference in programming, the REF modifier lets you pass a pointer to an object for use in various scenarios including composite sub-types. Therefore, the above DDL of the class_type can be rewritten as:
create type class_type as object ( teacher ref person_type,
student1 ref person_type,
student2 ref person_type,
student3 ref person_type,
room_number number)
and is much more efficient. It is useful to note that changes to the referenced object will automatically be propagated downstream.
In the example in the question OP wishes to use both an IS-A relationship as well as a HAS-A relationship simultaneously such as in
An employee "is a" person "has a" manager (who "is a" person)
which is a fair translation of a real-world concept. However, to make it legal without leading to infinite recursion, we modify it as:
An employee "is a" person "has a" manager (who "is a" 'reference to' a person)
I have type:
CREATE OR REPLACE TYPE something AS OBJECT(
name VARCHAR2(100),
nestedObjects objects
);
where objects type is table of references:
CREATE TYPE objects IS TABLE OF REF object;
and object is:
CREATE OR REPLACE TYPE object AS OBJECT (
number NUMBER
);
then I create table:
CREATE TABLE tab_something OF something
NESTED TABLE nestedObjects STORE AS tab_nestedObjects;
and I want select numbers from objects contained in something table, I try:
SELECT ts.name, cursor(
select deref(object).number
from TABLE(ts.nestedObjects) object
) FROM tab_something ts;
or:
SELECT DEREF(object).number
FROM tab_something ts, TABLE(ts.nestedObjects) object;
but it doesn't work. Oracle say 'object' identifier does not valid.
When nestedObjects consist of objects then it work well because there is no need to deal with references. but when it points to object references it does not work. Ho can I deal with it?
THANKS FOR HELP!
Ok I resolved:
SELECT DEREF(VALUE(object)).number
FROM tab_something ts, TABLE(ts.nestedObjects) object;
I apologize in advance for not knowing the terminology, but here it goes:
How do I extract a specialized UDO (UDO= user defined object & specialized= that 'extends' or is 'under' another UDO) that I inserted in a object table defined for a generalized UDO (generalized - higher in the same UDO hierarchy).
Details:
CREATE OR REPLACE TYPE GENERIC_UDO AS OBJECT (
atribute_1 TYPE,
...
atribute_n TYPE2, //TYPE2 is a nested table
) NOT FINAL NOT INSTANTIABLE;
CREATE OR REPLACE TYPE SPECIALIZED_UDO UNDER GENERIC_UDO (
atribute_1 TYPE,
...
atribute_q TYPE3, //TYPE3 is also a nested table
) FINAL INSTANTIABLE;
CREATE TABLE TBL_GENERIC_UDO OF GENERIC_UDO
( CONSTRAINT PK...)
... //other nested tables
NESTED TABLE atribute_n STORE AS atribute_n_nst;
So this works:
INSERT INTO TBL_GENERIC_UDO values (new TBL_GENERIC_UDO(...)); because TBL_GENERIC_UDO is 'under' GENERIC_UDO.
A 'select * from TBL_GENERIC_UDO' shows me as columns just the GENERIC_UDO atributes (as expected!)
So how do I extract or extract and convert, because I need to read back the inserted SPECIALIZED_UDO.
I payed a bit with:
VALUE(x): SELECT VALUE(tgo) FROM TBL_GENERIC_UDO tgo;
TREAT(x as Y): SELECT TREAT(VALUE(tgo) as SPECIALIZED_UDO) FROM TBL_GENERIC_UDO tgo;
and no good results. Actually in booth cases I get
SCHEMANAME.SPECIALIZED_UDO(values, values,..., null)
oracle.sql.STRUCT#54faa2
In the first case, when I did the insert, the corespondent value for atribute_q was set to null, in the second one it was a proper value, ex: SPECIALIZED_UDO('value1','value1',...)
I am asumming thats because TBL_GENERIC_UDO doesnt have a store clause for atribute_q from SPECIALIZED_UDO.
So anyone thoughts, explanations would be appreciated.
Solved it myself! Wasn't that hardcore as I expected. First I thought that Oracle will 'truncate' when inserting a specialized UDO into the table defined for a generic UDO, and in my case the specialized UDO had additionally another nested table as a atribute, but it doesn't.
Check code example, last line is the most important one:
CREATE OR REPLACE TYPE OBJ_TBL_TEXT IS TABLE OF VARCHAR2(100);
CREATE OR REPLACE TYPE OBJ_CONTAINER1 AS OBJECT (
ATRIBUTE NUMBER(6),
TBL_TEXT OBJ_TBL_TEXT
) NOT FINAL NOT INSTANTIABLE;
CREATE OR REPLACE TYPE OBJ_CONTAINER2 UNDER OBJ_CONTAINER1(
SECOND_TBL_TEXT OBJ_TBL_TEXT
) FINAL INSTANTIABLE;
CREATE TABLE TBL_CONTAINER1 OF OBJ_CONTAINER1
( CONSTRAINT PK_TBL_CONTAINER1 PRIMARY KEY(ATRIBUTE))
NESTED TABLE TBL_TEXT STORE AS TBL_TEXT_NST;
INSERT INTO TBL_CONTAINER1 VALUES (NEW OBJ_CONTAINER2(1,null,null));
INSERT INTO TBL_CONTAINER1 VALUES (NEW OBJ_CONTAINER2(2,null, OBJ_TBL_TEXT('VAL1','VAL2')));
INSERT INTO TBL_CONTAINER1 VALUES (NEW OBJ_CONTAINER2(3,OBJ_TBL_TEXT('VAL3','VAL4'), null));
INSERT INTO TBL_CONTAINER1 VALUES (NEW OBJ_CONTAINER2(4,OBJ_TBL_TEXT('VAL5','VAL6'), OBJ_TBL_TEXT('VAL7','VAL8')));
--------------------------------------------------------------------------------
SELECT * FROM TBL_CONTAINER1;
SELECT VALUE(tc) FROM TBL_CONTAINER1 tc;
SELECT TREAT(VALUE(tc) as OBJ_CONTAINER2).SECOND_TBL_TEXT FROM TBL_CONTAINER1 tc;
First select will display as columns just the atributes from the OBJ_CONTAINER1, like:
ATTRIBUTE| TBL_TEXT
--===============--
- 1 (null)
- 2 (null)
- 3 VARCHAR(VAL3,VAL4)
- 4 VARCHAR(VAL5,VAL6)
Second one looks weirder
VALUE(TC)
--=====--
DATAMODELER.OBJ_CONTAINER2(1,null,null)
oracle.sql.STRUCT#57ac8e
oracle.sql.STRUCT#1df6f
oracle.sql.STRUCT#f40801
VALUE - returns the object instance for a row of an object table as associated with the correlation_variable (table alias)
Third one lets me have access to nested attributes not mention in the nested clause in the creation of TBL_CONTAINER1.
TREAT(VALUE(tc) as OBJ_CONTAINER2).SECOND_TBL_TEXT
--==============================================--
(null)
VARCHAR(VAL1,VAL2)
(null)
VARCHAR(VAL7,VAL8)
It is exactly what I needed. Dont know yet how Oracle stores the additional attributes, and don't care. Though to share the solution...
I've seen discussions about this in the past, such as here. But I'm wondering if somewhere along the line, maybe 10g or 11g (we are using 11g), ORACLE has introduced any better support for "parameterized views", without needing to litter the database with all sorts of user-defined types and/or cursor definitions or sys_context variables all over.
I'm hoping maybe ORACLE's added support for something that simply "just works", as per the following example in T-SQL:
CREATE FUNCTION [dbo].[getSomeData] (#PRODID ROWID)
RETURNS TABLE AS
RETURN SELECT PRODID, A, B, C, D, E
FROM MY_TABLE
WHERE PRODID = #PRODID
Then just selecting it as so:
SELECT * FROM dbo.getSomeData(23)
No need for SYS_CONTEXT or cursor definitions.
You do need a type so that, when the SQL is parsed, it can determine which columns are going to be returned.
That said, you can easily write a script that will generate type and collection type definitions for one or more tables based on the data in user_tab_columns.
The closest is
create table my_table
(prodid number, a varchar2(1), b varchar2(1),
c varchar2(1), d varchar2(1), e varchar2(1));
create type my_tab_type is object
(prodid number, a varchar2(1), b varchar2(1),
c varchar2(1), d varchar2(1), e varchar2(1))
.
/
create type my_tab_type_coll is table of my_tab_type;
/
create or replace function get_some_data (p_val in number)
return my_tab_type_coll pipelined is
begin
FOR i in (select * from my_table where prodid=p_val) loop
pipe row(my_tab_type(i.prodid,i.a,i.b,i.c,i.d,i.e));
end loop;
return;
end;
/
SELECT * FROM table(get_Some_Data(3));
It is possible to define a kind of "parametrized" views in Oracle.
The steps are:
Define a package containing as public members that are in fact the needed parameters (there is no need for functions or procedures in that package),
Define a view that is based on that package members.
To use this mechanism one user should:
open a session,
assign the desired values to that package members,
SELECT data from the view,
do other stuff or close the session.
REMARK: it is essential for the user to do all the three steps in only one session as the package members scope is exactly a session.
There are TWO types of table-valued functions in SQL SERVER:
Inline table-valued function: For an inline table-valued function, there is no function body; the table is the result set of a single SELECT statement. This type can be named as
'parameterized view' and it has no equivalent in ORACLE as I know.
Multistatement table-valued function: For a multistatement table-valued function, the function body, defined in a BEGIN...END block, contains a series of Transact-SQL statements that build and insert rows into the table that will be returned.
The above sample (By Gary Myers) creates a table function of the second type and it is NOT a 'parameterized view'.