|
Az Oracle objektumrelációs lehetőségei |
CREATE TYPE t AS OBJECT (
attribútumok és metódusok listája
);
/
CREATE TYPE PointType AS OBJECT (
x NUMBER,
y NUMBER
);
/
Egy objektumtípus használható az egyéb típusokhoz hasonlóan az objektum- és táblatípusok definícióiban.
Például definiálhatunk így egy vonal típust:
CREATE TYPE LineType AS OBJECT (
end1 PointType,
end2 PointType
);
/
Ezután készíthetünk egy relációt, amely azonosítóval ellátott vonalak halmaza:
CREATE TABLE Lines (
lineID INT,
line LineType
);
DROP TYPE Linetype;Azonban egy típus eldobása előtt el kell dobnunk az összes táblát és típust, amely a típust használja. Tehát a fenti művelet nem sikerül, ha létezik a Lines tábla és használja a LineType-ot.
INSERT INTO Lines
VALUES(27, LineType(
PointType(0.0, 0.0),
PointType(3.0, 4.0)
)
);
Azaz konstruálunk két PointType értéket, ezeket az értékeket a LineType, konstruktora használja fel, az így kapott értéket pedig a 27-es egésszel összetéve kapjuk a Lines egy rekordját.
A metódusokban elérhető egy speciális rekordváltozó, a SELF, amellyel az aktuális rekordra hivatkozhatunk. Ha a SELF-et használjuk egy metódus definíciójában, akkor a környezetnek olyannak kell lennie, hogy egy konkrét rekordra hivatkozunk. A metódusok alkalmazására példákat találhatunk a lekérdezésekről és a sortípusokól szóló szakaszokban.
Például szeretnénk hozzáadni egy hossz függvényt a LineType-hoz. Ez a metódus a ,,jelenlegi'' vonal objektumra vonatkozik, de amikor kiszámítja a hosszt, megszorozza egy mérettényezővel. Átírjuk a LineType definícióját a következőre:
CREATE TYPE LineType AS OBJECT (
end1 PointType,
end2 PointType,
MEMBER FUNCTION length(scale IN NUMBER) RETURN NUMBER,
PRAGMA RESTRICT_REFERENCES(length, WNDS)
);
/
A típus minden metódusát egyetlen CREATE BODY utasításban definiáljuk, például:
CREATE TYPE BODY LineType AS
MEMBER FUNCTION length(scale NUMBER) RETURN NUMBER IS
BEGIN
RETURN scale *
SQRT((SELF.end1.x-SELF.end2.x)*(SELF.end1.x-SELF.end2.x) +
(SELF.end1.y-SELF.end2.y)*(SELF.end1.y-SELF.end2.y)
);
END;
END;
/
Például az alábbi lekérdezés meghatározza a Lines reláció vonalainak hosszát 2-es mérettényezővel (azaz valójában a hosszak kétszeresét adja vissza).
SELECT lineID, ll.line.length(2.0)
FROM Lines ll;
SELECT ll.line.end1.x, ll.line.end1.y
FROM Lines ll;
Kiírja minden vonal kezdőpontjának x és y koordinátáját.
SELECT ll.line.end2
FROM Lines ll;
Kiírja minden vonal végpontját, de PointType típusként, nem számpárként.
Például egy vonalra a kimenet lehet PointType(3,4).
Figyeljük meg, hogy a konstruktorok megjelenhetnek a kimenetben is, nem csak a bemenetben.
CREATE TABLE Lines1 OF LineType;Olyan, mint ha így definiáltuk volna Lines1-et:
CREATE TABLE Lines1 (
end1 PointType,
end2 PointType
);
viszont a length metódus is elérhető, ha lines1 valamelyik rekordjára hivatkozunk.
Például kiszámíthatjuk a vonalak átlaghosszát így:
SELECT AVG(ll.length(1.0))
FROM Lines1 ll;
CREATE TABLE Lines2 (
end1 REF PointType,
end2 REF PointType
);
A REF operátorral kaphatunk referenciát egy létező értékre.
Például ha van egy Points relációnk PointType típusú rekordokkal:
CREATE TABLE Points OF PointType;Ekkor így készíthetjük el a Lines2 relációt, amelynek elemei a balról jobbra futó vonalak (az első pont x koordinátája kisebb, mint a másodiké):
INSERT INTO Lines2
SELECT REF(pp), REF(qq)
FROM Points pp, Points qq
WHERE pp.x < qq.x;
Több fontos tiltás is van, amikor úgy tűnik, hogy lehetne referenciát állítani egy objektumra, de valójában nem lehet.
SELECT ll.end1.x, ll.end2.x
FROM Lines2 ll;
| a | b | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| - |
| ||||||||||||
| - |
| ||||||||||||
| - |
|
Ha szeretnénk egy attribútum típusaként relációt használni, akkor előbb definiálnunk kell egy típust az AS TABLE OF részmondattal. Például:
CREATE TYPE PolygonType AS TABLE OF PointType;
/
Ez azt jelenti, hogy a PolygonType típus egy reláció, amelynek rekordjai PointType típusúak, azaz két komponensük van, a valós szám értékű x és y.
Must deklarálhatunk olyan relációt, amelynek mezőinek értékei sokszögek, azaz pontok halmazai. Egy lehetséges deklaráció, amelyben a sokszögeket egy névvel és egy ponthalmazzal ábrázoljuk:
CREATE TABLE Polygons (
name VARCHAR2(20),
points PolygonType)
NESTED TABLE points STORE AS PointsTable;
Az ,,apró'' relációkat, amelyek az egyes sokszögeket reprezentálják, nem tároljuk közvetlenül a points attribútumok értékeiként, hanem egyetlen nagy táblában, amelynek nevét deklarálni kell (habár nem tudunk rá hivatkozni).
Ezt a deklarációt a tábla zárójelezett attribútumlistája után látható: a PointsTable nevet választottuk a PolygonType típusú relációk tárolására.
Itt egy utasítás, amely egy ,,négyzet'' nevű sokszöget szúr be, amelynek pontjai az egységnégyzet pontjai.
INSERT INTO Polygons VALUES(
'négyzet', PolygonType(PointType(0.0, 0.0), PointType(0.0, 1.0),
PointType(1.0, 0.0), PointType(1.0, 1.0)
)
);
A négyzet pontjait egy ilyen lekérdezéssel kaphatjuk meg:
SELECT points
FROM Polygons
WHERE name = 'négyzet';
Lehetséges egy beágyazott relációt a FROM részmondatban feltüntetni a THE kulcsszóval, amelyet egy relációt visszaadó alkérdésre alkalmazunk. A fenti lekérdezés egy ilyen példa, mivel egy beágyazott relációt ad vissza.
Például az alábbi lekérdezés megkeresi azokat a pontokat a négyzetben, amelyek a főátlón vannak (azaz x=y).
SELECT ss.x
FROM THE(SELECT points
FROM Polygons
WHERE name = 'négyzet'
) ss
WHERE ss.x = ss.y;
Ebben a lekérdezésben a beágyazott relációnak az ss álnevet adjuk, amelyet a SELECT és WHERE részmondatban használhatunk, mintha közönséges reláció lenne.
CREATE TYPE PolygonRefType AS TABLE OF REF PointType; /Ezután szükségünk van egy Polygons-szerű relációra, ahol a pontokat referenciatáblában tároljuk:
CREATE TABLE PolygonsRef (
name VARCHAR2(20),
pointsRef PolygonRefType)
NESTED TABLE pointsRef STORE AS PointsRefTable;
Emlékezzünk rá, hogy magukat a pontokat egy másik, PointType típusú relációban kell tárolnunk. Ezt a részt most kihagyjuk.
A fenti főátlóbeli pontos lekérdezést úgy írhatjuk át a referenciás változatra, hogy a COLUMN_VALUE kulccsszóval hivatkozunk a beágyazott tábla mezőjére.
Ilyen lesz a lekérdezés:
SELECT ss.COLUMN_VALUE.x
FROM THE(SELECT pointsRef
FROM PolygonsRef
WHERE name = 'négyzet'
) ss
WHERE ss.COLUMN_VALUE.x = ss.COLUMN_VALUE.y;
Például legyen egy LinesFlat relációnk, amelynek deklarációja:
CREATE TABLE LinesFlat(
id INT,
x1 NUMBER,
y1 NUMBER,
x2 NUMBER,
y2 NUMBER
);
és ez a reláció a vonalakat ,,régi'' stílusban tartalmazza, tehát egy azonosítóval és a végponttok koordinátáival.
Bemásolhatjuk az adatokat a Lines-ba és helyes szerkezetet adhatunk nekik:
INSERT INTO Lines
SELECT id, LineType(PointType(x1,y1), PointType(x2,y2))
FROM LinesFlat;
Ha beágyazott táblát tartalmazó táblába szeretnénk beszúrni, akkor trükközni kell.
Ha egy már létező beágyazott relációba akarunk beszúrni, akkor használhatjuk a THE kulcsszót.
Például ha a (2.0, 3.0) pontot szeretnénk beszúrni a ,,háromszög'' nevű sokszög beágyazott táblájába:
INSERT INTO THE(SELECT points
FROM Polygons
WHERE name = 'háromszög'
)
VALUES(PointType(2,0, 3.0));
Tegyük fel, hogy van egy ,,lapos'' relációnk, amely egy sokszög pontjait reprezentálja:
CREATE TABLE PolyFlat (
name VARCHAR2(20),
x NUMBER,
y NUMBER
);
Ha egy négyzet pontjait a PolyFlat reprezentálja, akkor bemásolhatjuk a Polygons-ba az alábbi módon:
INSERT INTO Polygons VALUES('négyzet',
CAST(
MULTISET(SELECT PointType(x, y)
FROM PolyFlat
WHERE name = 'négyzet'
)
AS PolygonType
)
);
Még bonyolultabb, ha a lapos PolyFlat-ből az összes sokszöget szeretnénk bemásolni a Polygons-ba.
Az alábbi majdnem működik
INSERT INTO Polygons
SELECT pp.name,
CAST(
MULTISET(SELECT PointType(x, y)
FROM PolyFlat qq
WHERE qq.name = pp.name
)
AS PolygonType
)
FROM PolyFlat pp;
Az a baj, hogy ha négy pont van, akkor négy rekordot szúr be a 'négyzet' névvel.
Az sem működik, hogy az első SELECT után írjuk a DISTINCT kulcsszót.
Olyan módszert kell találnunk, amely egy sokszögnevet csak egyszer szúr be, és ehhez egy ésszerű megoldás, hogy egy WHERE részmondatot adunk hozzá, amely megköveteli, hogy a PolyFlat x és y tagjai lexikografikusan elsők legyenek.
Íme egy működő beszúró parancs:
INSERT INTO Polygons
SELECT pp.name,
CAST(
MULTISET(SELECT PointType(x, y)
FROM PolyFlat qq
WHERE qq.name = pp.name
)
AS PolygonType
)
FROM PolyFlat pp
WHERE NOT EXISTS(
SELECT *
FROM PolyFlat rr
WHERE rr.name = pp.name AND
rr.x < pp.x OR
rr.x = pp.x AND rr.y < pp.y
);