Néhány apró specialitás, amikről érdemes egy-két szót ejteni. >> A DUAL tábla >> Csoportfüggvények >> Felső-N elemzés >> A NULL érték >> Külső join művelet (+) >> JOIN új szintaxis >> Hierarchikus lekérdezések (A PRIOR művelet) >> nézet, INLINE nézet >> MERGE A DUAL tábla ------------ Van a SYS user tulajdonában egy DUAL nevű tábla, aminek egyetlen oszlopa van: DUMMY VARCHAR2(1) és a táblának egyetlen sora van. A táblához létezik egy publikus szinonima szinten DUAL névvel (a DBA_SYNONYMS táblában az OWNER mezőben 'PUBLIC' szerepel, továbbá a DBA_OBJECTS-ben is) Mivel minden SELECT utasításnak legalább egy táblát le kell kérdeznie (vagyis valamit be kell írni a FROM után), a fenti tábla hasznos olyan beépített függvények hívása esetén, amelyeknél valójában nincs szükségünk táblára. Ennek segítségével lehet például lekérdezni a rendszeridőt, vagy az aktuális user nevét. SELECT SYSDATE FROM DUAL; SELECT USER FROM DUAL; Ezt a táblát sokszor az oracle eszközök is használják, pl. FORMS, SQLPLUS, ezért ne változtassunk rajta. Az oracle-ben ha valamilyen rendszeradatot szeretnénk lekérdezni akkor mindig ezt a DUAL táblát kell használnunk. Rendszer információkat a következő SQL függvényekkel kérhetünk: SYSDATE, USER, UID, USERENV ... stb. Csoportfüggvények ----------------- egyszeres mélységben egymásba ágyazhatók. (8.0-tól) SELECT MAX(AVG(sal)) FROM emp GROUP BY deptno; Felső-N elemzés (9-estől) ------------------------- SELECT sal FROM (SELECT ename, sal FROM orauser.emp ORDER BY sal) WHERE ROWNUM <= 4 UNIQUE a DISTINCT szinonimájaként használható (8.0-tól) A NULL érték ------------- Kifejezésekben használható a NULL, ennek segítségével lehet pl. null értéket betenni a táblába. pl. INSERT INTO T1 VALUES(NULL); vagy UPDATE T1 SET O1 = NULL WHERE ... A NULL használata azonban néha kellemetlen meglepetésekhez is vezethet, lásd az alábbi műveleteknél. Műveletek ANY, ALL WHERE kif <= ANY (SELECT ...) WHERE kif <= ALL (SELECT ...) Ha a SUBSELECT végeredménye üres akkor ANY esetén a logikai kifejezés értéke FALSE, ALL esetén pedig TRUE. NOT IN Ha a NOT IN utáni listában a NULL is szerepel akkor egyetlen sort sem kapunk vissza eredményül. Ennek az az oka, hogy a Not In úgy értékelődik ki mintha a !=ALL feltételt írtuk volna le. Így a kifejezés NOT IN (a, b, null) feltétel ekvivalens az alábbival: kif != a AND kif !=b AND kif != Null A fenti eset viszonylag ritkán fordul elő egy lekérdezésben, előfordul viszont gyakran, hogy a NOT IN után egy SUBSELECT szerepel. A kiértékelés ekkor is ugyanúgy történik, ami elég megtévesztő eredményhez vezethet. pl SELECT kifejezés FROM tábla WHERE O1 NOT IN (SELECT ...) Ha a SUBSELECT-ben szerepel a null érték is akkor a NOT IN "ismeretlen" (UNKNOWN, ez nem ugyanaz, mint a hamis) értékkel fog kiértékelődni. Márpedig egy sor csak akkor kerül be a lekérdezés végeredményébe, ha a WHERE utáni logikai kifejezés igaz értékkel értékelődik ki rá. A fentiek azt a filozófiát tükrözik, hogy a NULL értéket "ISMERETLEN" értéknek kell tekinteni, vagyis az értéke lehet, hogy egyenlő az O1 oszlopbeli értékkel, de lehet, hogy nem. A következő példák szintén a NULL érték "ISMERETLEN" mivoltát támasztják alá. WHERE (1, 'A') NOT IN ((1, 'B'), (2, NULL)) -> TRUE WHERE (1, 'A') NOT IN ((1, 'B'), (1, NULL)) -> UNKNOWN Külső join művelet: (+) -------------------------- A (+) csak a WHERE után használható. A műveletet megelőző oszlop lesz a külső join oszlop. Vagyis ezen oszlop értékei helyett fog NULL értékeket szerepeltetni az Oracle a lekérdezésben, amennyiben a másik táblabeli sornak nincs párja. pl. SELECT ename, dname FROM emp, dept WHERE dept.deptno = emp.deptno (+) Ez vissza fogja adni azt az osztályt is, amelyiken nem dolgozik senki. Ha a két táblát összekapcsoló JOIN feltétel több oszlopot is tartalmaz, akkor a "(+)" jelet minden oszlop után ki kell tenni. A "(+)" jelet csak oszlopra lehet alkalmazni nem pedig tetszőleges kifejezésre, de egy kifejezésben szerepelhet "(+)" jellel megjelölt oszlop. "(+)" jellel megjelölt oszlop nem szerepelhet IN összehasonlításban (kivéve ha egy konstans van a zárójelben), nem hasonlítható össze SUBSELECT-tel, és a rá vonatkozó feltételt nem lehet OR-ral összekapcsolni más feltétellel. Egy SELECT utasításban egy táblát csak egy másikkal lehet külső join művelet segítségével összekötni. Nem lehet egyszerre mindkét táblára külső joint alkalmazni. Nem megengedett utasítások az alábbiak!!! SELECT ename, dname FROM emp, dept WHERE dept.deptno = emp.deptno (+) OR emp.deptno=10 SELECT ename FROM emp WHERE emp.deptno (+) IN (SELECT deptno FROM dept) SELECT ename FROM emp WHERE emp.deptno (+) IN (10, 20) De megengedett az alábbi utasítás: SELECT ename FROM emp WHERE emp.deptno (+) IN (10) Példa: Van két táblánk A(O1, O2) és B(O1, O2). SELECT * FROM A, B, WHERE A.O1 = B.O1 (+) SELECT * FROM A, B, WHERE A.O1 = B.O1 (+) AND B.O1 (+) > 5 Ha az utóbbi lekérdezés második feltételéből elhagyjuk a (+)-t akkor a lekérdezés nem ad vissza egy sort sem. Ez is mutatja, hogy a külső join oszlopot tartalmazó táblát azokban az összehasonlításokban ahova a (+) jelet kitettük, a rendszer szükség eseten kiegészíti egy "Joker" sorral, ami tetszőleges feltételnek eleget tesz. A szükség esetén itt azt jelenti, hogy csak akkor egészíti ki ha nincs olyan "rendes" sor, ami a feltételnek eleget tesz. Egy lekérdezésen belül több táblára vonatkozóan is megadható külső join, pl. az alábbi lekérdezés visszaadja azokat a vevőket is, akik semmit sem rendeltek és azokat a rendeléseket is, amiknek meg egyetlen tételsora sincs. SELECT * FROM vevo, rendeles, tetelek WHERE vevo.vkod = rendeles.vkod (+) AND rendeles.rkod = tetelek.rkod (+) JOIN új szintaxis ------------------ (9-estől használható az alábbi szintaxis a join műveletekre. Az OUTER kulcsszó opcionális) SELECT emp.* FROM emp JOIN dept ON emp.deptno <= dept.deptno SELECT emp.* FROM emp CROSS JOIN dept SELECT emp.* FROM emp NATURAL JOIN dept -- azonos nevű oszlopok alapján SELECT emp.* FROM emp JOIN dept USING (deptno) -- a megadott oszlopok alapján SELECT emp.* FROM emp NATURAL LEFT OUTER JOIN dept OUTER JOIN (9-estől használható az alábbi szintaxis, sőt FULL OUTER JOIN is) SELECT dept.deptno, ename, dname FROM emp RIGHT OUTER JOIN dept ON emp.deptno = dept.deptno A külső join-olt táblának fogja a rendszer az összes sorát visszaadni, vagyis mintha a másik táblát egészítenénk ki "Joker" sorral. A fentivel ekvivalens az alábbi régi módszer: SELECT dept.deptno, ename, dname FROM emp, dept WHERE emp.deptno (+) = dept.deptno Érdemes megnézni az alábbi két ekvivalens lekérdezést is. SELECT dept.deptno, ename, dname, sal FROM emp, dept WHERE emp.deptno (+) = dept.deptno AND dept.deptno > 20 AND sal(+) > 1500 SELECT dept.deptno, ename, dname, sal FROM emp RIGHT OUTER JOIN dept ON emp.deptno = dept.deptno AND sal > 1500 WHERE dept.deptno > 20 Vagyis a fentieket összefoglalva, a WHERE utáni AND-el összekötött feltételek közül azokat, amelyekben szerepel a (+) jel JOIN feltételnek tekinti a rendszer, a többi feltételt pedig meghagyja a WHERE után, lásd az alábbi két ekvivalens példát. SELECT dept.deptno, ename FROM emp, dept WHERE emp.deptno (+) = dept.deptno AND ename (+) LIKE '%A%' AND dept.deptno >= 30 SELECT dept.deptno, ename FROM emp RIGHT OUTER JOIN dept ON (emp.deptno = dept.deptno AND ename LIKE '%A%') WHERE dept.deptno >= 30 Az alábbi példa azt mutatja meg, hogy hogyan lehet függvényben használni a (+) jelet. Az utána következő a vele ekvivalens másik szintaxist mutatja. SELECT dept.deptno, ename FROM emp, dept WHERE emp.deptno (+) = dept.deptno AND INSTR(ename (+),'A') > 0 AND dept.deptno >= 30 SELECT dept.deptno, ename FROM emp RIGHT OUTER JOIN dept ON (emp.deptno = dept.deptno AND INSTR(ename,'A') > 0) WHERE dept.deptno >= 30 Az alábbit viszont már nem lehetne a (+)-os szintaxissal kifejezni SELECT c.customer_id, c.o.order_id -- így is lehet az oszlopra hivatkozni (!) FROM oe.customers c FULL OUTER JOIN oe.orders o ON c.customer_id = o.customer_id WHERE c.customer_id BETWEEN 113 and 116 ORDER BY c.o.order_id NULLS FIRST Próbáljuk ki az alábbi lekérdezésben a (+) jelet áttenni a másik oldalra, illetve a LEFT|RIGHT|FULL kulcsszavakat. create table outer1(o1 number, o2 VARCHAR2(10)); insert into outer1 values(1, 'Egy'); insert into outer1 values(2, 'Ketto'); create table outer2(o1 number, o2 VARCHAR2(10)); insert into outer2 values(1, 'Egy'); insert into outer2 values(3, 'Harom'); select * from outer1 t1, outer2 t2 where t1.o1 (+) = t2.o1; select * from outer1 full outer join outer2 on outer1.o1 = outer2.o1; Hierarchikus lekérdezések, (A PRIOR művelet) --------------------------------------------- A PRIOR művelet arra való, hogy hierarchikus szerkezetű adatokat is le tudjunk kérdezni egy SELECT utasítás segítségével. A hierarchia gyökerét (gyökereit) a START WITH -del kell megadni. A szülő és gyermek sorok közti kapcsolatot a CONNECT BY clause-zal. Ebben használhatjuk a PRIOR műveletet, amelyik a szülő sorra utal. Az ilyen jellegű lekérdezések eseten a kiértékelés sorrendje a következő: 1. A START WITH alapján kiválasztja a gyökér sorokat. 2. A CONNECT BY alapján a gyökerek gyerekeit választja ki. 3. A további leszármazottakat választja ki mélységi keresési stratégiával. 4. A WHERE alapján kiszűri a sorokat, de az eldobott sorok leszármazottait nem dobja el. Vagyis a feltételt minden egyes sorra külön-külön értékeli ki. 5. A sorokat a preorder bejárás szerinti sorrendben adja vissza. Ha egy SELECT hierarchikus struktúrát kérdez le, akkor nem használhat join műveletet. A CONNECT BY után akár több feltételt is használhatunk. De ha kört talál a struktúrában akkor hibát ad. pl. CONNECT BY PRIOR empno=mgr AND salary > commission Itt az empno a szülő sorra vonatkozik, a többi a gyerek sorra. Használhatjuk a LEVEL pszeudo oszlopot, amelyik a gyökér sorokra egyet ad vissza, a többiekre, pedig a megfelelő mélységet. pl. SELECT LPAD(' ', 2*(LEVEL-1)) || ename, empno, mgr, job FROM emp START WITH job='PRESIDENT' CONNECT BY PRIOR empno = mgr ORDER BY (9-estől SIBLINGS, NULLS FIRST, NULLS LAST) SELECT LPAD(' ', 2*LEVEL)||ename FROM emp START WITH job= 'PRESIDENT' CONNECT BY PRIOR empno=mgr ORDER SIBLINGS BY ename [NULLS FIRST | LAST] NÉZET ----- Egyszerű és összetett nézet. Törlés, módosítás, beszúrás nézetbe. Előzőek letiltása: WITH READ ONLY. Csak olyan beszúrások és módosítások engedélyezése, ami a nézeten belül marad -> WITH CHECK OPTION CREATE OR REPLACE VIEW empvu20 AS SELECT * FROM emp WHERE deptno = 20 WITH CHECK OPTION CONSTRAINT empvu20_ck (DBA_CONSTRAINTS-ben V-típusú lesz a fenti) Beszúrás INLINE nézetbe (8.0-tól) ------------------------ INSERT INTO (SELECT empno, ename, hiredate, sal FROM emp WHERE deptno < 10) VALUES (9999, 'Taylor', TO_DATE('07-JUN-99', 'DD-MON-YY'), 5000) Az alábbi beszúrás már nem fog sikerülni az ellenőrző feltétel miatt: INSERT INTO (SELECT empno, ename, hiredate, sal FROM emp WHERE deptno < 10 WITH CHECK OPTION) VALUES (9999, 'Taylor', TO_DATE('07-JUN-99', 'DD-MON-YY'), 5000) Nézet létrehozása létező tábla nélkül (8.0-tól) CREATE OR REPLACE FORCE VIEW v1 AS SELECT * FROM Egy részeredmény többszöri felhasználása a lekérdezésben -> WITH WITH dept_costs AS ( SELECT department_name, SUM(salary) dept_total FROM oe.employees e, oe.departments d WHERE e.department_id = d.department_id GROUP BY department_name), avg_cost AS ( SELECT SUM(dept_total)/COUNT(*) avg FROM dept_costs) SELECT * FROM dept_costs WHERE dept_total > (SELECT avg FROM avg_cost) MERGE utasítás, ami az UPDATE és INSERT kombinálása (9-estől) -------------- MERGE INTO tabla T1 USING masik_tabla T2 ON (T1.o = T2.o) WHEN MATCHED THEN UPDATE SET T1.o1 = T2.o1, T1.o2 = T2.o2, ... WHEN NOT MATCHED THEN INSERT VALUES(T2.o1, T2.o2, ...);