Einige neue Oracle9i Feature - Lambertz

Werbung
Oracle 9i
New Feature
Stand: 16.06.2004
Autor: R. Lambertz
Oracle 9i – New feature
________________________________________________________________________________________________________________
1
ALLGEMEIN __________________________________________________________ 3
2
UNDO_RETENTION TIME ______________________________________________ 3
3
FLASHBACK QUERY ___________________________________________________ 3
4
VIEW CONSTRAINTS ___________________________________________________ 5
5
MATERIALIZED VIEWS (MV) ___________________________________________ 5
5.1
Materialized Views COMPLETE REFRESH __________________________________ 6
5.2
Materialized Views FAST REFRESH ________________________________________ 6
5.3
Materialized Views FAST REFRESH ON COMMIT____________________________ 6
5.3.1
5.4
6
ENABLE QUERY REWRITE ___________________________________________________ 7
Materialized Views FAST REFRESH ON COMMIT für Joins____________________ 8
TABLE PARTITIONING _________________________________________________ 9
6.1
Hash Partitioning ________________________________________________________ 11
6.2
List Partitioning _________________________________________________________ 11
7
INDEX ORGANIZED TABLE (IOT)_______________________________________ 12
7.1
Bitmap secondary index on ________________________________________________ 15
7.2
Hash Partitioning on______________________________________________________ 16
8
EXTERNAL TABLES (ETL) _____________________________________________ 16
8.1
ETL-Table Function ______________________________________________________ 18
8.2
ETL-Multi Table Insert ___________________________________________________ 20
8.3
ETL-Multi Table Merge___________________________________________________ 20
9
CHANGE DATA CAPTURE _____________________________________________ 21
10
EXPLAIN PLAN ZU ABGEARBEITETEN STATEMENTS __________________ 22
11
RESUMABLE SPACE MANAGEMET ___________________________________ 23
12
PL/SQL Erweiterungen ________________________________________________ 24
12.1 SELECT FOR UPDATE WAIT ____________________________________________ 24
12.2 Verbesserung des Bulk Bind Verfahrens _____________________________________ 24
12.3 CASE Konstruckte _______________________________________________________ 25
Seite 2/ 26
Oracle 9i – New feature
________________________________________________________________________________________________________________
1
ALLGEMEIN
Oracle 9i wurde um einige neue Funktionen und Möglichkeiten erweitert, die
alle aufgezählt und beschrieben ein Buch gestalten kann. Hier werden nur
einige Neuerungen gezeigt, die gerade für Entwickler von Interesse sein
können.
2
UNDO_RETENTION TIME
Mit Hilfe dieses Parameters kann man den Fehler „SnapShot to old“ vermeiden.
ALTER SYSTEM SET UNDO_RETENTION = <seconds>;
Durch diese Angabe wird Oracle angewiesen, alle Rollbackinformationen für
eine Dauer von <seconds> Sekunden vorzuhalten.
Dieser Trick sollte nur eingesetzt werden, wenn langfristig geöffnete Cursor für
die Verarbeitung nicht vermieden werden können. Je größer die garantierte
Vorhaltedauer eingestellt wird, umso eher läuft das Plattensystem voll, da im
schlechtesten Fall sehr viele RollBackSegmente erzeugt werden.
Flashback query’s können nur dann eingesetzt werden, wenn die Datenbank Instanz
mit dem neuen „Automatic Undo Management“ verwaltet wird, was das traditionelle
RollBack Management ersetzt.
3
FLASHBACK QUERY
Bei dem Begriff „Flashback Query“ handelt es sich um die Möglichkeit, auf
Daten zurückzugreifen, welche mittlerweile verändert sind oder gar gelöscht
wurden. Diese Rückbetrachtung findet für Daten statt, welche mit einem DML
Statement bearbeitet und mit COMMIT festgeschrieben wurden.
Der Trick liegt darin, daß Oracle Rollback Informationen weiterhin vorhält, auch
wenn die zugehörige Transaktion mit COMMIT schon abgeschlossen ist und
diese Rolback-Informationen eigentlich verworfen werden könnten.
Flashback query’s können nur dann eingesetzt werden, wenn die Datenbank
Instanz mit dem neuen „Automatic Undo Management“ verwaltet wird, was das
traditionelle RollBack Management ersetzt.
Alte Rollbackinformationen leben nicht „ewig“. Die Vorhaltedauer wird gesteuert
über den Parameter UNDO_RETENTION, der standardmäßig in der initSID.ora
Datei gesetzt ist. Er kann auch innerhalb einer Session verändert werden kann,
wie das Beispiel zeigt:
ALTER SYSTEM SET UNDO_RETENTION = <seconds>;
Seite 3/ 26
Oracle 9i – New feature
________________________________________________________________________________________________________________
Nachfolgend wird der Einsatz einer Flashback Query gezeigt:
19:52:48 llam@ALVT > create table lc_vertraege as select * from vertraege;
Table created.
real: 10
19:56:16 llam@ALVT > alter system set undo_retention=90;
--1,5 Minuten
System altered.
real: 60
19:56:46 llam@ALVT > delete from lc_vertraege where ver_id = 100037112;
1 row deleted.
real: 30
19:56:54 llam@ALVT > commit;
Commit complete.
real: 10
19:58:00 llam@ALVT > select * from lc_vertraege as
of timestamp to_timestamp ('10-MAR-03 19:56', 'dd-mon-yy hh24:mi' )
where ver_id = 100037112;
*
ERROR at line 1:
ORA-08176: consistent read failure; rollback data not available
real: 31
19:58:01 llam@ALVT >
Die gesetzte UNDO_RETENTION Time war zu klein gewählt und die
Rollbackinformationen nicht mehr verfügbar.
19:58:11 llam@ALVT > alter system set undo_retention=3600;
System altered.
real: 60
19:59:05 llam@ALVT > delete from lc_vertraege where ver_id = 100037110;
1 row deleted.
real: 10
19:59:19 llam@ALVT > commit;
Commit complete.
real: 10
19:59:23 llam@ALVT > select
from
as of
where
*
lc_vertraege
timestamp to_timestamp ('10-MAR-03 19:58', 'dd-mon-yy hh24:mi' )
ver_id = 100037110;
VER_ID VER_MAN_ID VER_VERTRAGSNR
V KZ_HERKUNFT
PSEUDO VER_VERTRAGSNR_ALT
---------- ---------- -------------------- - ---------------- ---------- -------------------100037110
50049 306812889
FP
0
1 row selected.
real: 20
19:59:47 llam@ALVT >
Somit wurden Daten selektiert, die um 19:59h gelöscht wurden!
Seite 4/ 26
Oracle 9i – New feature
________________________________________________________________________________________________________________
4
VIEW CONSTRAINTS
Mit Einführung von View Constraints, Primary Key, Foreign Key oder Check
Constraints erstellt werden. Hier ein Beispiel:
DROP TABLE lc$test;
CREATE TABLE lc$test( PAR_ID
NUMBER( 9 ),
PAR_ANR_ID NUMBER( 9 ),
PAR_NAME
VARCHAR2( 32 ) );
INSERT
INSERT
INSERT
INSERT
INSERT
INSERT
INSERT
INSERT
INSERT
INSERT
INTO
INTO
INTO
INTO
INTO
INTO
INTO
INTO
INTO
INTO
lc$test
lc$test
lc$test
lc$test
lc$test
lc$test
lc$test
lc$test
lc$test
lc$test
VALUES(100104248,
VALUES(100104249,
VALUES(100104250,
VALUES(100104251,
VALUES(100104252,
VALUES(100104253,
VALUES(100104254,
VALUES(100104255,
VALUES(100104256,
VALUES(100104234,
50244,
50244,
50244,
50245,
50244,
50245,
50244,
50244,
50245,
50244,
'InvestRent Tarif55 W' );
'Investrent T55' );
'InvestRent Tarif 55E' );
'InvestRent Tarif 57V' );
'Partner' );
'Person' );
'Investrent Tarif 57' );
'InvestRent K Tarif 57' );
'Person' );
'UFW-Kindergeld' );
CREATE OR REPLACE VIEW lc$test_v( PAR_ID, PAR_ANR_ID, PAR_NAME,
CONSTRAINT lc$test_pk PRIMARY KEY (PAR_ANR_ID) RELY DISABLE NOVALIDATE )AS
SELECT PAR_ID, PAR_ANR_ID, PAR_NAME FROM LC$TEST;
CREATE OR REPLACE VIEW lc$test_v( PAR_ID, PAR_ANR_ID UNIQUE RELY DISABLE
NOVALIDATE, PAR_NAME )AS
SELECT PAR_ID, PAR_ANR_ID, PAR_NAME FROM LC$TEST;
insert into lc$test_v values( 100104246, 50245, 'InvestRent Tarif 55A' );
5
MATERIALIZED VIEWS (MV)
In den früheren Oracle Versionen <8i nannte man diese Technik SNAPSHOT.
Ein SnapShot stellte sich dar als Tabelle, für die bei der Erstellung ein Zeitpunkt
angegeben wurde, zu dem der Inhalt –basierend auf dem Select Statement für
diesen SnapShot- erneuert wird
CREATE SNAPSHOT LOG ON emp;
-- einmaliges Refresh, Typ FAST
CREATE SNAPSHOT emp_sf
REFRESH FAST NEXT sysdate + 7 AS
SELECT * FROM scott.emp;
-- Refresh jeden Montag, nicht FAST
CREATE SNAPSHOT all_emps
REFRESH START WITH ROUND(SYSDATE + 1) + 11/24
NEXT NEXT_DAY(TRUNC(SYSDATE, 'MONDAY') + 15/24 AS
SELECT * FROM fran.emp@dallas
UNION
SELECT * FROM marco.emp@balt;
Diese Elemente findet man heute für das Erstellen einer MV wieder.
Seite 5/ 26
Oracle 9i – New feature
________________________________________________________________________________________________________________
Es gibt unzählige Schalter bei der Erstellung einer MV, die an dieser Stelle nicht
alle besprochen werden. Grundsätzlich gilt es die Art des Refresh zu
differentzieren.
5.1 Materialized Views COMPLETE REFRESH
Wie der Name schon vermuten läßt, werden alle Daten in diesem View
erneuert, wie sie durch das Select Statement beschrieben sind. Besonders bei
großen Quelltabellen ist hier eine lange Laufzeit einzurechnen. Unter der
Betrachtung, daß sich in diesem VIEW allerdings nur sehr wenige Daten
geändert haben oder hinzu geschrieben wurden, ist diese Methode nicht
sonderlich effizient.
5.2 Materialized Views FAST REFRESH
Um nur die realen Änderungen in die MV aufzunehmen kann der Fast Refresh
anstatt eines Complete Refresh genutzt werden. Diese funktioniert allerdings
nur wenn:
¾ Für die Quelltabelle ein Primary Key existiert
¾ Ein MV Log erstellt wurde, indem die Änderungen von Oracle protokolliert
werden
¾ Wenn die MV aus einer Tabelle selektiert wird
5.3 Materialized Views FAST REFRESH ON COMMIT
Mit den zuvor beschriebenen Aktualisierungen eines MV muß immer der
Refresh Zeitpunkt angegeben werden. Durch die Angabe FAST REFRESH ON
COMMIT werden die Änderungen aus der Quelltabelle direkt übernommen in
die MV, wenn der COMMIT durchgeführt wurde. Auch diese Technik
funktioniert nur, wenn:
¾ Für die Quelltabelle ein Primary Key existiert
¾ Ein MV Log basierend auf dem PRIMARY KEY erstellt wurde
¾ Wenn die MV aus einer Tabelle selektiert wird
13:42:53 lambertz@ODB92 > CREATE MATERIALIZED VIEW LOG ON adr_name WITH ROWID INCLUDING NEW
VALUES;
13:42:53 lambertz@ODB92 >
13:42:54 lambertz@ODB92 > CREATE MATERIALIZED VIEW adr_name_mv
13:42:54
2
BUILD IMMEDIATE
13:42:54
3
REFRESH FAST ON COMMIT
13:42:54
4
AS SELECT *
13:42:54
5
FROM adr_name
13:42:54
6
WHERE name like 'Meier%' ;
FROM adr_name
*
ERROR at line 5:
Seite 6/ 26
Oracle 9i – New feature
________________________________________________________________________________________________________________
ORA-23415: materialized view log for "LAMBERTZ"."ADR_NAME" does not record the primary key
Hier wurde das MVLog mit der Aufgabe erstellt, alle Änderungen basierend auf
der ROWID zu protokollieren. Das ist für einen MV von Typ FAST REFRESH
ON COMMIT nicht zulässig.
13:42:54 lambertz@ODB92 > DROP MATERIALIZED VIEW LOG ON adr_name;
13:43:42 lambertz@ODB92 > CREATE MATERIALIZED VIEW LOG ON adr_name WITH PRIMARY KEY
INCLUDING NEW VA
LUES;
13:43:42 lambertz@ODB92 > CREATE MATERIALIZED VIEW adr_name_mv
13:43:42
2
BUILD IMMEDIATE
13:43:42
3
REFRESH FAST ON COMMIT
13:43:42
4
AS SELECT *
13:43:42
5
FROM adr_name
13:43:42
6
WHERE name like 'Meier%' ;
13:44:53 lambertz@ODB92 >
1* select count(*) from
13:55:44 lambertz@ODB92 >
88
13:55:45 lambertz@ODB92 >
1* select count(*) from
13:55:54 lambertz@ODB92 >
13:56:45 lambertz@ODB92 >
13:56:51 lambertz@ODB92 >
89
13:57:07 lambertz@ODB92 >
13:58:26 lambertz@ODB92 >
13:58:30 lambertz@ODB92 >
89
13:58:40 lambertz@ODB92 >
13:59:36 lambertz@ODB92 >
14:00:38 lambertz@ODB92 >
14:00:41 lambertz@ODB92 >
88
14:00:47 lambertz@ODB92 >
adr_name_mv
/
l
adr_name_mv
insert into adr_name values( 280100, 'Meiermann' );
commit;
select count(*) from adr_name_mv;
insert into adr_name values( 280101, 'Müller' );
commit;
select count(*) from adr_name_mv;
delete from adr_name where id = 280101;
update adr_name set name = 'Müller' where id = 280100;
commit;
select count(*) from adr_name_mv;
Somit ist erkennbar, daß die MV adr_name_mv aktualisiert wurde, indem die
Änderung in der Tabelle adr_name festgeschrieben wurden.
5.3.1 ENABLE QUERY REWRITE
Diese Parametersequence wird bei der Anlage der MV angegeben, funktioniert
allerdings nur, wenn die Datenbank QUERY_REWRITE=TRUE gesetzt ist. Es wird ein
Select auf die Tabelle ADR_NAME abgesetzt und der zugehörige Explain Plan
betrachtet:
13:49:20 lambertz@ODB92 > @xtree
Enter name of file with contains SQL: 0
Generating Execution plan for
1* select * from adr_name where Name like 'Meier%';
SELECT STATEMENT
OPIMIZER=CHOOSE cost= 98
Seite 7/ 26
Oracle 9i – New feature
________________________________________________________________________________________________________________
Execution Plan
Cost
--------------------------------------------------------------------------- --------1.1 TABLE ACCESS FULL LAMBERTZ.ADR_NAME
98
TABLE ACCESS
FULL
ADR_NAME
An diesem Ausführungsplan ist nichts außergewöhnliches. Nun wird eine MV
mit der Parametersequence ENABLE QUERY REWRITE erstellt und wieder ein
Ausführungsplan erstellt:
13:44:02 lambertz@ODB92 > DROP MATERIALIZED VIEW LOG ON adr_name;
13:44:35 lambertz@ODB92 > CREATE MATERIALIZED VIEW LOG ON adr_name WITH PRIMARY KEY
INCLUDING NEW VA
LUES;
13:44:35 lambertz@ODB92 > DROP MATERIALIZED VIEW adr_name_mv;
13:44:36 lambertz@ODB92 >
13:44:36 lambertz@ODB92 > CREATE MATERIALIZED VIEW adr_name_mv
13:44:36
2
BUILD IMMEDIATE
13:44:36
3
REFRESH FAST ON COMMIT
13:44:36
4
ENABLE QUERY REWRITE
13:44:36
5
AS SELECT *
13:44:36
6
FROM adr_name
13:44:36
7
WHERE name like 'Meier%' ;
13:44:36 lambertz@ODB92 >
13:44:37 lambertz@ODB92 > @xtree
Enter name of file with contains SQL: 0
Generating Execution plan for
1* select * from adr_name where Name like 'Meier%';
SELECT STATEMENT
OPIMIZER=CHOOSE cost= 2
Execution Plan
Cost
--------------------------------------------------------------------------- --------1.1 TABLE ACCESS FULL LAMBERTZ.ADR_NAME_MV
2
TABLE ACCESS
FULL
ADR_NAME_MV
Nun zeigt der Ausführungsplan, daß der CBO für die Ausführung des
Statements die MV „gesehen“ und genutzt hat!!
5.4 Materialized Views FAST REFRESH ON COMMIT für Joins
Bisherige Beispiele zeigen nur die MV, welche auf einer Quelltabelle basieren.
Nun wird versucht, diese Einschränkung zu umgehen – mit einer View. Sie wird
zusätzlich mit einem erforderlichen Primary Key versehen wird:
Seite 8/ 26
Oracle 9i – New feature
________________________________________________________________________________________________________________
19:06:14 lambertz@ODB92 > CREATE VIEW v_adr_name(name, vorname, strasse,
19:06:14
2
CONSTRAINT v_adr_name_pk
19:06:14
3
PRIMARY KEY( name, vorname, strasse) RELY DISABLE NOVALIDATE )AS
19:06:14
4 SELECT n.name, a.VORNAME, a.STRASSE
19:06:14
5
FROM adr_adressen a, adr_name n
19:06:14
6
WHERE a.name_id = n.id
19:06:14
7
AND n.name LIKE 'Meier%'
19:06:14
8
AND n.name IS NOT NULL
19:06:14
9
AND a.VORNAME IS NOT NULL
19:06:14 10
AND a.STRASSE IS NOT NULL;
View created.
Elapsed: 00:00:00.00
19:06:48 lambertz@ODB92 > CREATE MATERIALIZED VIEW LOG ON v_adr_name WITH PRIMARY KEY
INCLUDING NEW
VALUES;
CREATE MATERIALIZED VIEW LOG ON v_adr_name WITH PRIMARY KEY INCLUDING NEW VALUES
*
ERROR at line 1:
ORA-00942: table or view does not exist
Elapsed: 00:00:00.00
19:06:49 lambertz@ODB92 >
Angeblich existiert die View „v_adr_name“ nicht, was falsch ist, wie folgende Kontrolle
zeigt:
19:06:59 lambertz@ODB92 > desc v_adr_name
Name
----------------------------------------------------------------NAME
VORNAME
STRASSE
19:07:01 lambertz@ODB92 >
Aber es gibt noch eine Einschränkung für MV. Eine WHERE Bedingung kann
keine nicht konstanten Elemente enthalten wie z.B. SYSDATE. Also eine MV zu
erstellen, welche immer nur Werte der letzen 6 Monate enthält, ist nicht möglich
6
TABLE PARTITIONING
Tables in der Form:
CREATE TABLE adr_part_table
(
anrede
vorname
name
strasse
plz
ort
telefon
vorwahl
rufnummer
VARCHAR2(10),
VARCHAR2(25),
VARCHAR2(90),
VARCHAR2(60),
NUMBER(5),
VARCHAR2(60),
VARCHAR2(15),
NUMBER(10),
VARCHAR2(30)
Seite 9/ 26
Oracle 9i – New feature
________________________________________________________________________________________________________________
)
PARTITION BY RANGE (name)
(
PARTITION eins_bis_f VALUES LESS THAN ('G')
TABLESPACE mass_dat,
PARTITION g_bis_k VALUES LESS THAN ('L')
TABLESPACE mass_dat,
PARTITION l_bis_r VALUES LESS THAN ('S')
TABLESPACE mass_dat,
PARTITION s_bis_max VALUES LESS THAN (MAXVALUE)
TABLESPACE mass_dat
)
/
sind schon seit Oracle 8 bekannt. Neu am Partitionieren von Tabellen unter 9i
ist, wie einzelne Partitionen gebildet werden.
Durch die Angabe von Partitionen bei der Erstellung einer Tabelle, werden von
Oracle so viele Tabellen erzeugt, wie Partitionen angegeben werden. Auch die
Verwaltung erfolgt durch Oracle.
Wenn eine Abfrage abgesetzt wird wie folgt:
SELECT * FROM adr_part_table WHERE name LIKE ‘Meier‘;
bildet Oracle die Ergebnismenge ausschließlich aus der Partition:
PARTITION l_bis_r VALUES LESS THAN ('S').
Der Explain Plan zeigt das deutlich:
Das hat natürlich gerade Tabellen mit großem Datenvolumen enorme Vorteile.
Seite 10/ 26
Oracle 9i – New feature
________________________________________________________________________________________________________________
Aber – wenn ein Update auf den Primary Key erfolgt, der zur Folge hat, daß der
Datensatz in eine andere Partition übertragen werden müßte, wird für den
„neuen“ Eintrag in der neuen Partion ein Eintrag (so eine Art Zeigert) erzeugt,
der auf den echten Datendsatz in der ursprünglichen Partition verweist. Der
Datensatz selbst verbleibt in der ursprünglichen Tabelle.
Somit ist ekennbar, daß es zu einem Organisations-chaos für Oracle führt,
wenn zu häufig der Primary Key geändert wird.
6.1 Hash Partitioning
Mit der HASH Partitionierung werden nur die Anzahl der Partitionen angegeben
und Oracle verwaltet die Füllstände der einzelnen Partition Tabelle
selbstständig.
CREATE TABLE hash_products
( product_id
NUMBER(6)
, product_name
VARCHAR2(50)
, product_description VARCHAR2(2000)
, category_id
NUMBER(2)
, weight_class
NUMBER(1)
, warranty_period
INTERVAL YEAR TO MONTH
, supplier_id
NUMBER(6)
, product_status
VARCHAR2(20)
, list_price
NUMBER(8,2)
, min_price
NUMBER(8,2)
, catalog_url
VARCHAR2(50)
, CONSTRAINT
product_status_lov
CHECK (product_status in ('orderable'
,'planned'
,'under development'
,'obsolete')
) )
PARTITION BY HASH (product_id)
PARTITIONS 5
STORE IN (tbs_1, tbs_2, tbs_3, tbs_4);
6.2 List Partitioning
War es unter Oracle 8 nur möglich Bereiche einzelner Partitionen zu
bestimmen, wie folgendes Beispiel zur Anlage einer solchen Tabelle zeigt,
CREATE TABLE range_sales
( prod_id
NUMBER(6)
, cust_id
NUMBER
, time_id
DATE
, channel_id
CHAR(1)
, promo_id
NUMBER(6)
, quantity_sold NUMBER(3)
, amount_sold
NUMBER(10,2)
)
PARTITION BY RANGE (time_id)
(PARTITION SALES_Q1_1998 VALUES LESS
PARTITION SALES_Q2_1998 VALUES LESS
PARTITION SALES_Q3_1998 VALUES LESS
PARTITION SALES_Q4_1998 VALUES LESS
PARTITION SALES_Q1_1999 VALUES LESS
PARTITION SALES_Q2_1999 VALUES LESS
PARTITION SALES_Q3_1999 VALUES LESS
PARTITION SALES_Q4_1999 VALUES LESS
PARTITION SALES_Q1_2000 VALUES LESS
PARTITION SALES_Q2_2000 VALUES LESS
THAN
THAN
THAN
THAN
THAN
THAN
THAN
THAN
THAN
THAN
(TO_DATE('01-APR-1998','DD-MON-YYYY')),
(TO_DATE('01-JUL-1998','DD-MON-YYYY')),
(TO_DATE('01-OCT-1998','DD-MON-YYYY')),
(TO_DATE('01-JAN-1999','DD-MON-YYYY')),
(TO_DATE('01-APR-1999','DD-MON-YYYY')),
(TO_DATE('01-JUL-1999','DD-MON-YYYY')),
(TO_DATE('01-OCT-1999','DD-MON-YYYY')),
(TO_DATE('01-JAN-2000','DD-MON-YYYY')),
(TO_DATE('01-APR-2000','DD-MON-YYYY')),
(TO_DATE('01-JUL-2000','DD-MON-YYYY')),
Seite 11/ 26
Oracle 9i – New feature
________________________________________________________________________________________________________________
PARTITION SALES_Q3_2000 VALUES LESS THAN (TO_DATE('01-OCT-2000','DD-MON-YYYY')),
PARTITION SALES_Q4_2000 VALUES LESS THAN (MAXVALUE));
können unter Oracle 9 auch Fixwerte angegeben werden:
CREATE TABLE list_customers
( customer_id
NUMBER(6)
, cust_first_name
VARCHAR2(20)
, cust_last_name
VARCHAR2(20)
, cust_address
CUST_ADDRESS_TYP
, nls_territory
VARCHAR2(30)
, cust_email
VARCHAR2(30))
PARTITION BY LIST (nls_territory) (
PARTITION asia VALUES ('CHINA', 'THAILAND'),
PARTITION europe VALUES ('GERMANY', 'ITALY', 'SWITZERLAND'),
PARTITION west VALUES ('AMERICA'),
PARTITION east VALUES ('INDIA'),
PARTITION rest VALUES (DEFAULT));
Eine Mischung aus RANGE und LIST ist möglich!
7
INDEX ORGANIZED TABLE (IOT)
IOT sind Tabellen, die nach dem H*Tree Verfahren organisiert sind. Vereinfacht
also ein Index, in dem alle Attribute einer Tabelle enthalten sind – außer der
ROWID, die gibt es in einer IOT nicht. Eine IOT kann nur mit einem Primary
Key erstellt werden. Hier ein Beispiel:
CREATE TABLE adr_iot
(
anrede
VARCHAR2(10),
vorname
VARCHAR2(25),
name
VARCHAR2(90),
strasse
VARCHAR2(60),
plz
NUMBER(5),
ort
VARCHAR2(60),
telefon
VARCHAR2(15),
vorwahl
NUMBER(10),
rufnummer
VARCHAR2(30),
CONSTRAINT "ADR_IOT_PK" PRIMARY KEY( plz, name, rufnummer )
)
ORGANIZATION INDEX
OVERFLOW TABLESPACE mass_dat
TABLESPACE mass_dat;
Eine solche Tabelle ist nicht zu empfehlen, wenn regelmäßig große
Datenbestände aus dieser Tabelle entfernt werden, weil diese Daten in dem
Binärbaum erhalten bleiben und nur als gelöscht markiert werden. Das hat zur
Folge, daß eine solche Tabelle an Größe immer nur zunimmt und in
regelmäßigen Abständen reorganisiert werden muß.
Folgendes Statement verursacht einen Fehler, weil kein PK angegeben wurde:
Seite 12/ 26
Oracle 9i – New feature
________________________________________________________________________________________________________________
15:15:39 llam@WINA > CREATE TABLE lc$fonds
15:15:40
2 AS SELECT * FROM fonds
15:15:40
3 ORGANIZATION INDEX
15:15:40
4 MAPPING TABLE
15:15:40
5 TABLESPACE aspflv_d01;
ORGANIZATION INDEX
*
ERROR at line 3:
ORA-00933: SQL command not properly ended
real: 10
15:15:40 llam@WINA >
Der Ausführungsplan enthält einen FULL INDEX SCAN, was sich nur ändert,
wenn über den Primary Key eingeschränkt wird:
Seite 13/ 26
Oracle 9i – New feature
________________________________________________________________________________________________________________
Wenn also keine Einschränkung für die Tabelle über den Primary Key erfolgt,
ist der Nutzen einer IOT dahin. Mit der Version 8i wurde die Möglichkeit
geschaffen, einen zusätzlichen Index auf eine IO Table anzulegen; jedoch nur
als B*Tree Index. Der benötigt allerdings eine RowID, welche in einer IOT nicht
vorhanden sind. Oracle hat dazu „logische ROWID‘s“ eingeführt. Nun zeigt sich
der Explain:
Seite 14/ 26
Oracle 9i – New feature
________________________________________________________________________________________________________________
Und ob diese Verwurstung wirklich performanter ist, wage ich allerdings zu
bezweifeln.
7.1 Bitmap secondary index on
Mit Oracle 9i ist das Erstellen eines Bitmap Index auf eine IOT auch möglich.
Damit diese Technik funktioniert, muß für den Bitmap Index eine „mapping
table“ vorliegen, welche die logischen RowID’s aus der IOT enthält. Die
mapping table kann direkt beim Erstellen des Index erzeugt werden.
CREATE TABLE adr_iot
(
anrede
VARCHAR2(10),
vorname
VARCHAR2(25),
name
VARCHAR2(90),
strasse
VARCHAR2(60),
plz
NUMBER(5),
ort
VARCHAR2(60),
telefon
VARCHAR2(15),
vorwahl
NUMBER(10),
rufnummer
VARCHAR2(30),
CONSTRAINT "ADR_IOT_PK" PRIMARY KEY( plz, name, rufnummer )
)
ORGANIZATION INDEX
OVERFLOW TABLESPACE mass_dat
MAPPING TABLE TABLESPACE mass_dat;
CREATE BITMAP INDEX adr_iot_bidx ON adr_iot( plz );
Seite 15/ 26
Oracle 9i – New feature
________________________________________________________________________________________________________________
Und so sieht ein Ausführungsplan dazu aus:
7.2 Hash Partitioning on
Auch für eine IOT können Partitionen beschrieben werden, wenn die Tabelle
erzeugt wird. Neu ist, daß nun entsprechend dem Partitionieren sequentieller
Tabellen, „hash partitioning“ für IOT‘s genutzt werden kann.
8
EXTERNAL TABLES (ETL)
Externe Tabellen sind „flat files“, welche als Metadaten im Data Dictionary einer
Oracle Datenbank verwaltet werden. Die Daten in dem „flat file“ entsprechen
dem Format, wie sie auch für den Einsatz mit dem SQL*Loader bekannt sind.
Jedoch ist das Anlegen eines Oracle Directory erforderlich. Beispiel:
CREATE DIRECTORY ETL_Files AS 'oracle/export/file';
CREATE TABLE LC_ETL( Tex VARCHA2(33), Datum DATE )
ORGANISATION EXTERNAL( TYPE oracle_loader
DEFAULT DIRECTORY 'oracle/export/file'
ACCESS PARAMETERS
( RECORDS DELIMITED BY NEWLINE
BADFILE 'lc_etl.bad'
FIELDS TERMINATED BY ";"
LDRTRIM( Tex CHAR(255),
Datum CHAR(255) DATE_FORMAT DATE
MASK 'dd.mm.yyyy' ))
LOCATION( 'lc_etl.tbl' ))
REJECT LIMIT UNLIMITED;
Seite 16/ 26
Oracle 9i – New feature
________________________________________________________________________________________________________________
Der User, welcher eine ETL anlegen möchte, muß das Grant „create dictonary“
verfügen - GRANT CREATE ANY DIRECTORY TO UserName;
Unstimmigkeiten in der Beschreibung der LoaderDaten werden nicht mit dem
CREATE der Tabelle erkannt, sondern erst, wenn die Tabelle angesprochen
wird:
SQL> select count(*) from lc_etl;
select count(*) from lc_etl
*
ERROR at line 1:
ORA-29913: error in executing ODCIEXTTABLEOPEN callout
ORA-29400: data cartridge error
KUP-00554: error encountered while parsing access parameters
KUP-01005: syntax error: found "identifier": expecting one of: "enclosed,
exit,
(, ltrim, lrtrim, ldrtrim, missing, notrim, optionally, rtrim, reject"
KUP-01008: the bad identifier was: /
KUP-01007: at line 4 column 52
KUP-00031: concat
KUP-00031: concat
ORA-06512: at "SYS.ORACLE_LOADER", line 14
ORA-06512: at line 1
SQL>
Werden zum Zeitpunkt des eigentlichen Ladevorgangs Daten aus der
LoaderDatei abgelehnt, ist dies nur aus dem LogFile ersichtlich. Somit sollte
ETL immer zu beginn mit einem COUNT überprüft werden.
Das Erstellen einer fehlerfreien ETL kann aussehen:
CREATE TABLE LC_ETL( eingabedatum
DATE,
vertragsnr
VARCHAR2(20), /* NOT NULL Angaben
sind nicht möglich */
rolle
VARCHAR2(2),
name
VARCHAR2(70),
dok_art
VARCHAR2(2),
dok_nr
VARCHAR2(20),
ausstell_datum
DATE,
ausstell_behoerde
VARCHAR2(32),
geburtsort
VARCHAR2(32),
wirtschaftszweig1
VARCHAR2(3),
wirtschaftszweig2
VARCHAR2(3),
gv_nationalitaet
VARCHAR2(32),
gv_geburtsdatum
DATE,
gv_geschlecht
VARCHAR2(1),
gv_fiskalnummer
VARCHAR2(25),
gv_adresse
VARCHAR2(70),
gv_plz
VARCHAR2(7),
gv_wohnort
VARCHAR2(32) )
ORGANIZATION EXTERNAL( TYPE oracle_loader
DEFAULT DIRECTORY ETL_files
ACCESS PARAMETERS(
RECORDS DELIMITED BY NEWLINE
BADFILE 'etl.bad'
Seite 17/ 26
Oracle 9i – New feature
________________________________________________________________________________________________________________
LOGFILE 'etl.log'
FIELDS TERMINATED BY ";"
LDRTRIM( eingabedatum DATE "DD.MM.YYYY",
vertragsnr
CHAR(20),
rolle
CHAR(2),
name
CHAR(32),
dok_art
CHAR(2),
dok_nr
CHAR(20),
ausstell_datum
DATE "DD.MM.YYYY",
ausstell_behoerde
CHAR(32),
geburtsort
CHAR(32),
wirtschaftszweig1
CHAR(3),
wirtschaftszweig2
CHAR(3),
gv_nationalitaet
CHAR(32),
gv_geburtsdatum
DATE "DD.MM.YYYY",
gv_geschlecht
CHAR(1),
gv_fiskalnummer
CHAR(25),
gv_adresse
CHAR(70),
gv_plz
CHAR(7),
gv_wohnort
CHAR(32)
TERMINATED BY WHITESPACE )
)
LOCATION( 'etl.tbl' ))
REJECT LIMIT UNLIMITED;
Fehlerfrei ist dieses CREATE Statement schon, aber..
SQL> select count(*) from lc_etl;
select count(*) from lc_etl
*
ERROR at line 1:
ORA-29913: error in executing ODCIEXTTABLEOPEN callout
ORA-29400: data cartridge error
KUP-04063: unable to open log file LC_ETL.log
OS error No such file or directory
ORA-06512: at "SYS.ORACLE_LOADER", line 14
ORA-06512: at line 1
SQL>
..die LOG Datei kann nicht erstellt werden, weil in der Datenbank der Parameter
utl_file_dir nicht gesetzt ist, oder auf kein gültiges Verzeichnis verweiset
(fehlende Schreibrechte, usw.)
8.1 ETL-Table Function
Eine Table Function kann nicht nur in Verbindung mit einer „realen Tabelle“,
sondern auch mit einer External Table eingesetzt werden. Table Funktions
werden gerne eingesetzt, um selektierten Daten direkt zu transformieren und
über die Tabel Function auszugeben. Beispiel:
DROP TYPE t_sqltext
/
CREATE TYPE t_sqltext AS OBJECT( str VARCHAR2( 4000 ),
address VARCHAR2( 16 ) )
/
DROP TYPE t_sqltext_set
Seite 18/ 26
Oracle 9i – New feature
________________________________________________________________________________________________________________
/
CREATE TYPE t_sqltext_set AS TABLE OF t_sqltext
/
CREATE OR REPLACE PACKAGE sqltext IS
TYPE t_refcur_sqlt IS REF CURSOR RETURN v$sqltext%ROWTYPE;
FUNCTION str( pi_sql_text IN v$sqltext.sql_text%TYPE ) RETURN t_sqltext_set
PIPELINED;
FUNCTION str ( pi_sql_text IN sqltext.t_refcur_sqlt ) RETURN t_sqltext_set
PIPELINED;
END;
/
CREATE OR REPLACE PACKAGE BODY sqltext IS
------------------- Funktion 1
----------------FUNCTION str ( pi_sql_text IN sqltext.t_refcur_sqlt )
RETURN t_sqltext_set PIPELINED IS
v_sql_text VARCHAR2( 4000 );
v_out_sql t_sqltext := t_sqltext( NULL, NULL );
v_in_rec pi_sql_text%ROWTYPE;
CURSOR cur2( ci_address IN v$sqltext.address%TYPE,
ci_hash_value IN v$sqltext.hash_value%TYPE ) IS
SELECT a.sql_text
FROM v$sqltext a
WHERE a.address = ci_address
AND a.hash_value = ci_hash_value
ORDER BY piece;
BEGIN
LOOP
FETCH pi_sql_text INTO v_in_rec;
EXIT WHEN pi_sql_text%NOTFOUND;
v_SQL_text := NULL;
FOR cur2_rec IN cur2( v_in_rec.address, v_in_rec.hash_value )LOOP
v_SQL_text := v_SQL_text || cur2_rec.SQL_text;
END LOOP;
v_out_sql.address := v_in_rec.address;
v_out_sql.str := v_SQL_text;
PIPE ROW( v_out_sql );
END LOOP;
CLOSE pi_sql_text;
RETURN;
END;
------------------- Funktion 2
----------------FUNCTION str( pi_sql_text IN v$sqltext.sql_text%TYPE )
RETURN t_sqltext_set PIPELINED IS
v_sql_text VARCHAR2( 4000 );
v_out_sql t_sqltext := t_sqltext( NULL, NULL );
CURSOR cur1 IS
SELECT DISTINCT a.address, a.hash_value
FROM v$sqltext a
WHERE a.sql_text LIKE pi_sql_text
AND UPPER(a.sql_text) NOT LIKE '%V$SQL_TEXT%'
AND UPPER(a.sql_text) NOT LIKE '%V$SQLTEXT%';
CURSOR cur2( ci_address IN v$sqltext.address%TYPE,
Seite 19/ 26
Oracle 9i – New feature
________________________________________________________________________________________________________________
ci_hash_value IN v$sqltext.hash_value%TYPE ) IS
SELECT a.sql_text
FROM v$sqltext a
WHERE a.address = ci_address
AND a.hash_value = ci_hash_value
ORDER BY piece;
BEGIN
FOR cur1_rec IN cur1 LOOP
v_SQL_text := NULL;
FOR cur2_rec IN cur2( cur1_rec.address, cur1_rec.hash_value )LOOP
v_SQL_text := v_SQL_text || cur2_rec.SQL_text;
END LOOP;
v_out_sql.address := cur1_rec.address;
v_out_sql.str := v_SQL_text;
PIPE ROW( v_out_sql );
END LOOP;
RETURN;
END;
END;
/
SELECT * FROM TABLE( sqltext.str('% dual%')); /* Kann zu Error 22905 führen*/
SELECT str FROM TABLE( sqltext.str('% dual%')); /* nicht, wenn qualifiziert */
SELECT sqltext.str('% dual%') FROM dual;
SELECT sql_text, address
FROM TABLE( sqltext.str( CURSOR( SELECT sql_text,
address
FROM v$sqltext
WHERE sql_text
LIKE '%FROM BEARBEITUNGSNACHWEISE%')));
SELECT *
FROM TABLE( sqltext.str( CURSOR( SELECT sql_text,
address
FROM v$sqltext
WHERE sql_text
LIKE '%FROM BEARBEITUNGSNACHWEISE%')));
8.2 ETL-Multi Table Insert
INSERT ALL
WHEN product_id IN ( SELECT product_id
FROM promotional_items)
INTO promotion_sales VALUES( product_id, list_price)
WHEN order_mode = ’online’
INTO web_orders VALUES( product_id, order_total)
SELECT product_id, list_price, order_total, order_mode
FROM orders_ext
WHERE order_total > 1;
8.3 ETL-Multi Table Merge
MERGE INTO customer c
USING customer_merge m
ON (c.customer_id = m.src_customer_id)
Seite 20/ 26
Oracle 9i – New feature
________________________________________________________________________________________________________________
WHEN MATCHED THEN
UPDATE SET c.cust_address = m.cust_address
WHEN NOT MATCHED THEN
INSERT ( Customer_id, cust_first_name,… )
VALUES ( src_customer_id, src_first_name,… );
9
CHANGE DATA CAPTURE
BEGIN
dbms_logmnr_cdc_publish.drop_change_table
( owner => 'hr',
change_table_name => 'departments_changes',
force_flag => 'y');
END;
BEGIN
dbms_logmnr_cdc_publish.create_change_table(
owner => 'hr',
change_table_name => 'departments_changes',
change_set_name => 'SYNC_SET',
source_schema => 'hr',
source_table => 'departments',
column_type_list => 'DEPARTMENT_ID NUMBER(4), DEPARTMENT_NAME VARCHAR2(30),
MANAGER_ID NUMBER(6), LOCATION_ID NUMBER(4), ANREDE VARCHAR2(10)',
capture_values => 'both',
rs_id => 'y',
row_id => 'y',
user_id => 'n',
timestamp => 'n',
object_id => 'n',
source_colmap => 'y',
target_colmap => 'y',
options_string => NULL );
END;
SQL> desc hr.departments
Name
----------------------------------------DEPARTMENT_ID
DEPARTMENT_NAME
MANAGER_ID
LOCATION_ID
Null?
-------NOT NULL
NOT NULL
Type
---------------------------NUMBER(4)
VARCHAR2(30)
NUMBER(6)
NUMBER(4)
SQL> insert into hr.departments values( 999, 'LC$', 200, 1700 );
1 row created.
SQL> commit;
Commit complete.
SQL> select * from hr.departments_changes;
OP
CSCN$ COMMIT_TI
RSID$ ROW_ID$
-- ---------- --------- ---------- -----------------SOURCE_COLMAP$
-------------------------------------------------------------------------------TARGET_COLMAP$
Seite 21/ 26
Oracle 9i – New feature
________________________________________________________________________________________________________________
-------------------------------------------------------------------------------DEPARTMENT_ID DEPARTMENT_NAME
MANAGER_ID LOCATION_ID ANREDE
------------- ------------------------------ ---------- ----------- ---------I 2.8147E+14 01-JAN-00
1 AAAG8IAAEAAAAA2AAA
FE
FE
999 LC$
200
1700
SQL> delete from hr.departments where department_id =999;
1 row deleted.
SQL> commit;
Commit complete.
SQL> select * from hr.departments_changes;
OP
CSCN$ COMMIT_TI
RSID$ ROW_ID$
-- ---------- --------- ---------- -----------------SOURCE_COLMAP$
-------------------------------------------------------------------------------TARGET_COLMAP$
-------------------------------------------------------------------------------DEPARTMENT_ID DEPARTMENT_NAME
MANAGER_ID LOCATION_ID ANREDE
------------- ------------------------------ ---------- ----------- ---------I 2.8147E+14 01-JAN-00
1 AAAG8IAAEAAAAA2AAA
FE
FE
999 LC$
200
1700
OP
CSCN$ COMMIT_TI
RSID$ ROW_ID$
-- ---------- --------- ---------- -----------------SOURCE_COLMAP$
-------------------------------------------------------------------------------TARGET_COLMAP$
-------------------------------------------------------------------------------DEPARTMENT_ID DEPARTMENT_NAME
MANAGER_ID LOCATION_ID ANREDE
------------- ------------------------------ ---------- ----------- ---------D 2.8147E+14 01-JAN-00
2 AAAG8IAAEAAAAA2AAA
00
00
999 LC$
200
1700
SQL>
Damit ein User solche CDC Tabellen erzeugen kann, muß JAVA in der Datenbank
arbeiten.
10 EXPLAIN PLAN ZU ABGEARBEITETEN STATEMENTS
Bis war es nur möglich, einen Ausfürungsplan für eine Statement vor der
eigentlichen Ausführung zu erstellen. Ob dieser Plan auch zur Ausführung
genutzt wurde, ist nicht sichergestellt.
Seite 22/ 26
Oracle 9i – New feature
________________________________________________________________________________________________________________
Nun wird eine neue View von Oracle bereitgestellt mit dem Namen
V$SQL_PLAN. Mit Angabe der SQL_Adresse kann der Ausführungsplan
eingesehen werden, der tatsächlich zur Ausführung des Statements eingesetzt
wurde. Die SQL Adresse (ADDRESS) kann aus der View V$SQLAREA ermittelt
werden unter Angabe des Statements (Attribut SQL_TEXT).
SELECT address
FROM v$sqlarea
WHERE sql_text = 'Select * from dual';
SELECT *
FROM v$sql_plan
WHERE address = ( SELECT address
FROM v$sqlarea
WHERE sql_text ='Select * from dual' );
SELECT SUBSTR( lpad(' ',2*( LEVEL -1 )) || level ||'.'|| DECODE( id, 0, 0,
position) || ' ' || operation || ' ' || options || ' ' ||
DECODE( object_name, NULL, NULL, object_owner || '.' || object_name) ||
DECODE( id, 0, 'Mode=' || SUBSTR( optimizer, 1, 20 ) || ' Cost (max) =
' || position , NULL ), 1, 90) "Execution Plan",
DECODE( cost, NULL, NULL, SUBSTR( TO_CHAR( cost ), 1, 10 ) ) "Cost",
DECODE( cost, NULL, NULL, SUBSTR( TO_CHAR( IO_cost ), 1, 10 ) ) "IO
Cost",
DECODE( cost, NULL, NULL, SUBSTR( TO_CHAR( CPU_cost ), 1, 10 ) ) "CPU
Cost",
DECODE( cost, NULL, NULL, SUBSTR( TO_CHAR( partition_id ), 1, 10 ) )
"Part. ID",
DECODE( cost, NULL, NULL, SUBSTR( TO_CHAR( CARDINALITY ), 1, 10 )) "AVG
Rows",
DECODE( cost, NULL, NULL, SUBSTR( TO_CHAR( search_columns ), 1, 10 ))
"search_columns",
DECODE( cost, NULL, NULL, SUBSTR( TO_CHAR( partition_start ), 1, 10 ) )
"Part. Start",
DECODE( cost, NULL, NULL, SUBSTR( TO_CHAR( partition_stop ), 1, 10 ) )
"Part. Stop",
DECODE( cost, NULL, NULL, SUBSTR( TO_CHAR( bytes), 1, 10 ) ) "Bytes"
FROM v$sql_plan
WHERE address = ( select address
from v$sqlarea
where sql_text ='Select * from dual' )
CONNECT BY PRIOR id=parent_id
START WITH id=1
11 RESUMABLE SPACE MANAGEMET
Jedem ist das sicherlich schon einmal vorgekommen, dass unerwartet der
Space bzw. Plattenplatz nicht mehr ausreicht, ein RollBack erfolgt und eine
Fehlermeldung ausgegeben wird.
Besonders tragisch:
•
wenn z.B. eine Abfrage schon seit mehreren Stunden das Result Set
bildet und plötzlich der TempSpace nicht ausreicht.
•
wenn nach meheren Stunden eines ziemlich aufwendigen UPDATE die
Rollback segmente nicht erweitert werden können
Seite 23/ 26
Oracle 9i – New feature
________________________________________________________________________________________________________________
Um solche Speicher Probleme in den Griff zu bekommen, hat Oracle ein neues
Package eingeführt mit Namen „DBMS_RESUMABLE“. Mit hilfe dieses
Package kann innerhalb eines definierten Zeitraum die durch den Space
Management ausgelösten Unterbrechung der Verarbeitung wieder
aufgenommen werden.
Wie stellt sich das im konkreten dar?
EXEC DBMS_RESUMABLE.SET_TIMEOUT( 14400 ); -- 4Stunden
Ein Update führt zu einem Fehler ORA-01650. Nun erfolgt kein Rollback,
sondern die Transaktion verweilt in dem Zustand bis
•
die TimeOut Grenze erreicht wurde. Es erfolgt ein Rollback
•
der Fehler korrigiert wurde; vergrößern der TableSpace
•
der Resumable abgebrochen wird mit EXEC
DBMS_RESUMABLE.ABORT;
Feine Sache..
12 PL/SQL Erweiterungen
12.1 SELECT FOR UPDATE WAIT
Bisher gab es nur die Vartianten:
¾
SELECT * FROM Tabelle_name FOR UPDATE
¾
SELECT * FROM Tabelle_name FOR UPDATE NO WAIT
Für den ersten Fall kann das Select Statment nicht ausgeführt werden, wenn
die mit der FOR UPDATE Klausel angeforderten Satzsperren nicht ausgeführt
werden können. Es entsteht ein LockWait.
Im zweiten Fall wird das SELECT Stament sofort beendet, ohne ausgeführt
worden zu sein, wenn die Satzsperren nicht aufgebaut werden können.
Die Neuerung besteht nun in der Angabe der WAIT Dauer in Sekunden. Ist
innerhalb dieser Zeitspanne das Sperren der angeforderten Sätze nicht
möglich, erfolgt der Abbruch des Statements; ohne das dieses abgearbeitet
wurde.
¾
SELECT * FROM Tabelle_name FOR UPDATE WAIT 3
-- 3 Sekunden Wartezeit
12.2 Verbesserung des Bulk Bind Verfahrens
Mit der 8i Version wurde die „massenorientierte Denke“ bei Oracle für
INSERT, UPDATE oder DELETE Statements eingeführt. Diese Technik
Seite 24/ 26
Oracle 9i – New feature
________________________________________________________________________________________________________________
basiert auf „Array‘s“, also einer Zusammenfassung von Daten, die dann als
Paket in die Datenbank eingestellt und verarbeitet werden.
Tritt in der Schleife des folgenden Beispiel ein Fehler bei der Verabeitung auf,
wird direkt ein Exception ausgelöst; vielleicht schon nach dem ersten
DELETE.
DECLARE
TYPE t_ver_id IS TABLE OF vertraege.ver_id%TYPE;
v_get_ver_id t_ver_id := t_ver_id(1);
BEGIN
V_get_ver_id.EXTEND(3);
FORALL v_count IN 1..3
DELETE FROM vertraege
WHERE ver_id = v_get_ver_id(v_count);
END;
Anders in dem weiteren Beispiel. Hier werden erst alle Schleifendurchläufe
abgeschlossen. Im Anschluß wird das Exception ausgelöst:
DECLARE
TYPE t_ver_id IS TABLE OF vertraege.ver_id%TYPE;
v_get_ver_id t_ver_id := t_ver_id(1);
BEGIN
V_get_ver_id.EXTEND(3);
FORALL v_count IN 1..3
SAVE EXCEPTIONS
-- das ist neu
DELETE FROM vertraege
WHERE ver_id = v_get_ver_id(v_count);
EXCEPTION
WHEN Others THEN
FOR v_count_err IN 1..SQL%BULK_EXCEPTIONS.COUNT LOOP
DBMS_OUTPUT.PUT_LINE('Record Number: ' || TO_CHAR( v_count_err ));
DBMS_OUTPUT.PUT_LINE('ErrorCode: ' ||
TO_CHAR(SQL%BULK_EXCEPTIONS(v_count_err).error_code ));
DBMS_OUTPUT.PUT_LINE('ErrorMessage: ' ||
TO_CHAR(SQL%BULK_EXCEPTIONS(v_count_err).error_Index ));
END LOOP;
END;
12.3 CASE Konstruckte
Eigentlich ist es längst überfällig, daß Oracle in der PL/SQL CASE
Expressions zuläßt. Nun gibt es in 9i direkt zwei Varianten das CASE. Einmal
die klassige, wie sie aus 3GL Programmiersprachen bekannt ist:
DECLARE
v_case_wert NUMBER(1) :=1;
BEGIN
CASE v_case_wert
WHEN .1 THEN
EXIT 1;
WHEN .2 THEN
Seite 25/ 26
Oracle 9i – New feature
________________________________________________________________________________________________________________
EXIT 2;
ELSE
EXIT 3;
END CASE;
END;
Aber auch:
DECLARE
v_case_wert NUMBER(1) :=1;
BEGIN
CASE
WHEN v_case_wert=1 THEN
EXIT 1;
WHEN v_case_wert=2 THEN
EXIT 2;
ELSE
EXIT 3;
END CASE;
END;
Die zweite Variante ist, eine Wertzuweisung direkt per CASE gesteuert:
DECLARE
v_case_wert NUMBER(1) :=1;
v_ret NUMBER(2);
BEGIN
v_ret := CASE v_case_wert
WHEN .1 THEN 10
WHEN .2 THEN 20
ELSE 30
END;
END;
Seite 26/ 26
Herunterladen