Lokale KI modelliert Datenbank
Thomas Bartel. 15.Juni 2026
Müssen wir noch Modellieren oder kann das die KI? (Bild KI)
Warum eine lokale KI?
Kann eine KI zur Modellierung einer Datenbank verwendet werden? Sicherlich. Beschreibe dein Datenmodell und ein paar Regeln in einem Prompt und stecke den Output, den clode, gemini oder wer auch immer generiert, in dein DB Tool - it works's. Wie verhindere ich aber, dass ich der KI meine Firmengeheimisse preisgebe? Indem ich eine lokale KI verwende (hoffend, dass diese nicht nach hause telefoniert). Es mögen auch Kostengründe dafür sprechen, lokal zu bleiben, die lokale KI arbeitet ohne Abogebühren. Ich brauche dafür einen Rechner mit entsprechend Wums, bei mir ein Mac Mini mit M4 Pro und 64 GB RAM. Darauf lasse ich quen und mistral gegeneinander antreten. Ollama als runtime für die KI:
brew install ollama
ollama pull qwen3-coder-next
ollama run qwen3-coder-next
ollama pull mixtral
ollama run mixtral
Quen versa Mistral
Als Aufgabe beschreibe ich in meinem Prompt ein Datenmodell, das etwa dem bekannten Modell aus dem HR Schema entspricht. Ich beauftrage ein Script zur Generierung, dazu einen Accesslayer zu schreiben und Testdaten zu generieren. Nach einigen Iterationen hat mistral bei mir verspielt, unter anderem weil mistral auf der Verwendung von Backslashes vor Underscores in Bezeichnern beharrt und dann noch hallunziniert, genau dies nicht zu tun:>>> Das ist immer noch voll von Backslashes, name ist nicht durch VARCHAR2(50) ersetzt. Was muss ich in meinem Prompt anpassen?.
Es tut mir leid für die Verwirrung. Sie haben Recht, der generierte Code enthält immer noch Backslashes und das Attribut "NAME" wurde nicht durch
VARCHAR2(50) ersetzt. Das Problem liegt bei den Anführungszeichen um die Domaintypen in der Triggerdefinition. Ich werde das Skript entsprechend anpassen:
-------------------
-- DROP SEQUENCES --
-------------------
DROP consequence KOSTENSTELLE\_SEQ;
DROP consequence ABTEILUNG\_SEQ;
DROP consequence ROlle\_SEQ;
DROP consequence BENUTZER\_SEQ;
DROP consequence ROLLEN\_BENUTZER\_SEQ;
...
CREATE TABLE KOSTENSTELLE (
ID NUMBER(10) PRIMARY KEY,
KOSTENSTELLENAME VARCHAR2(50) NOT NULL,
BESCHREIBUNG VARCHAR2(255),
CREATED\_BY Name NOT NULL,
CREATED\_DATE TIMESTAMP NOT NULL,
MODIFIED\_BY Name,
MODIFIED\_DATE TIMESTAMP
);
...
Hallunzinierende KI (Bild KI)
Prompten statt Modellieren
In mehreren kleinen Iterationen habe ich folgendes Prompt verfeinert, die Beschreinbung ist recht umgangsspachlich, einigermaßen konkret, auf keinen Fall perfekt:
Du bist Datenbankentwickler und generierst ein Datenmodell für Oracle 26 AI.
Domaintypen:
ID ist vom Typ Integer.
Geld ist vom Type Number(15,2)
Description ist vom Typ Varchar2(255)
Name vom Typ Varchar2(50)
Datum ist vom Typ Date
Ein Zeitstempel vom Typ Timestamp
zipcode ist vom typ VARCHAR2(5)
Für alle Tabellen gilt:
Jede Tabelle hat eine Spalte ID. Jeder Fremdschlüssel heisst „Referenzierte Tabelle“ gefolgt vom Underscore „_“ und „ID“.
Für jede Tabelle gibt es eine Sequence, die die ID befüllt, sie heisst „Tabellenname“_seq. Sie startet mit 1.
Für jede Tabellen gibt es 4 Auditspalten. Created_By vom Typ Name, Created Date vom Type Zeitstempel. Created_By vom Typ Name,
Modified Date vom Type Zeitstempel und Modified_By vom Type Name. Type Name bedeutet, dass der Domaintyp entsprechen substituiert wird.
Alle Tabellenname, Attribute usw. werden in Großbuchstaben gescripted. Verwende Keine Hochkommas um die Namen.
Underscores sollen nicht maskiert werden.
Jede Tabelle erhält einen Insert und einen Update Trigger Namen „Tabellennname“_INS_TRG bzw. „Tabellennname“_UPD_TRG,
der die Created bzw. Modified_by Auditspalten versorgt. Created bzw Modified_by wird aus der User bzw. Apexuser gefüllt.
Für Apex$$User verwende: „ NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER);“
Attribute werden in der Notation Name (Typ) oder Name (Domaintyp), wenn ein Domaintyp angegeben ist,
verwende den entsprechenden Datenbanktyp aus der Beschreibung oben unter Domaintypen
(also Geld wird NUMBER(15,2) , Name wird VARCHAR2(50) wie oben hinterlegt).
Alle Attribute werden groß geschrieben und vor einem Underscore in einem Attributnamen steht kein Backslash.
Das gilt auch für Triioggernamen, Constraintnamen , Prozedurnamen, Patametername usw.
Ein NN hier dem Typ bedeutet Not Null
U hinter dem Typ bedeutet Unique und dann soll ein entsprechender Index angelegt werden
Ein Fremdschlüssel ist immer mit einem Index verbunden, sowohl auf dem Master als auch auf der Detailtabelle
Für jede Tabelle gibt es eine Insert Function Namen INS_“Tablename“ ,
die als Input Parameter eine Rowtype Variable der Tabelle nimmt
und im Erfolgsfall die ID des eingefügten Datensätzes als Retourniert liefert.
Im Fehlerfall wird -1 geliefert und im Out Parameter errcode der SQL code der zugrunde liegenden Exception.
Für jede Tabelle je eine Delete Procedure über jeden eindeutigen Schlüssel der Tabelle.
Für jede Tabelle einen Update Procedure, die einen Parameter vom Type Tabelle.$Rowtype entgegennimmt.
Alle Prozeduren/Funktionen zu einer Tabelle werden in einem Package „Tablename“_access zusammengefasst.
Folgende Tabellen sollen erzeugt werden.
Abteilung bestehend aus
Abteilung( Name ) NN U und
Beschreibung ( Description )
Manager_id ist ein Fremschlüssel auf Mitarbeiter
Fremdschlüssel auf Standort
Region
Region(name)NN U
Beschreibung ( Description )
Land
Land(name)NN U
Fremdschlüssel auf Region
Standort
Standort(Name) NN
Strasse(Name)
Plz(zipcode)
Ort(Name)
Fremdschlüssel auf Land
Mitarbeiter
Name(Name)NN
Vorname(Name)NN
Email(Description)
Fon(name)
Mobil(Name)
Beschaeftigt_seit(datum)
Manager_id ist ein Fremschlüssel auf Mitarbeiter
Fremdschlüssel auf Job
Fremdschlüssel auf Abteilung
Gehalt(geld)
Job
Titel(name)
Midestgehalt(geld)
Maximalgehalt(geld)
Job_history
Fremdschlüssel auf Mitarbeiter
Fremdschlüssel auf Job
Fremdschlüssel auf Abteilung
beginn_datum(Datum)
ende_datum(Datum)
Zur Vermeidung von Henne Ei Problemen, erzeuge die Tabellen zuerst ohne Fremdschlüssel,
wenn alle Tabellen angelegt sind, erweitere sie um die Fremdschlüssel.
Wenn du im Aufräumblock am Anfang Statements mit Execute Immediate absetzt,
dann kapsele die in einen BEGIN, EXCEPTION END Block, damit das Aufräumen nicht abgebrochen wird.
Erzeuge auch ein Script mit Testdaten. Regionen DACH plus Erdteile, 20 Länder ,
ein bis 3 Standorte pro Land, 100 Abteilungen, 1000 Mitarbeiter mit ein bis drei Historieneinträgen.
Output
Den Output habe ich gecutted in das sqltool meiner wahl gepasted. Bis auf den Insert der Testdaten in die Mitarbeitertabelle lief alles ohne Fehler.
quen generiertes Datenmodell
Hier der Output von quen:
DECLARE
PROCEDURE safe_drop(p_sql IN VARCHAR2) IS
BEGIN
EXECUTE IMMEDIATE p_sql;
EXCEPTION
WHEN OTHERS THEN NULL;
END;
BEGIN
-- Packages
FOR rec IN (SELECT object_name FROM user_objects WHERE object_type = 'PACKAGE' AND object_name LIKE '%_ACCESS') LOOP
safe_drop('DROP PACKAGE ' || rec.object_name);
END LOOP;
-- Triggers
FOR rec IN (SELECT trigger_name FROM user_triggers WHERE trigger_name LIKE '%_INS_TRG' OR trigger_name LIKE '%_UPD_TRG') LOOP
safe_drop('DROP TRIGGER ' || rec.trigger_name);
END LOOP;
-- FK Constraints
FOR rec IN (SELECT constraint_name, table_name
FROM user_constraints
WHERE constraint_type = 'R'
AND table_name IN ('ABTEILUNG','MITARBEITER','JOB_HISTORY','STANDORT','LAND')) LOOP
safe_drop('ALTER TABLE ' || rec.table_name || ' DROP CONSTRAINT ' || rec.constraint_name);
END LOOP;
-- Indexe (exkl. PK/UK)
FOR rec IN (SELECT index_name, table_name FROM user_indexes
WHERE table_name IN (
'ABTEILUNG','REGION','LAND','STANDORT','MITARBEITER','JOB','JOB_HISTORY'
)
AND index_name NOT LIKE '%_PK' AND index_name NOT LIKE '%_UK' AND index_name NOT LIKE 'SYS%') LOOP
safe_drop('DROP INDEX ' || rec.index_name);
END LOOP;
-- Unique constraints (exkl. PK)
FOR rec IN (SELECT constraint_name, table_name FROM user_constraints
WHERE constraint_type = 'U'
AND table_name IN ('ABTEILUNG','REGION','LAND','JOB')) LOOP
safe_drop('ALTER TABLE ' || rec.table_name || ' DROP CONSTRAINT ' || rec.constraint_name);
END LOOP;
-- Sequenzen
FOR rec IN (SELECT sequence_name FROM user_sequences
WHERE sequence_name IN (
'ABTEILUNG_SEQ','REGION_SEQ','LAND_SEQ','STANDORT_SEQ',
'MITARBEITER_SEQ','JOB_SEQ','JOB_HISTORY_SEQ'
)) LOOP
safe_drop('DROP SEQUENCE ' || rec.sequence_name);
END LOOP;
-- Tabellen
FOR rec IN (SELECT table_name FROM user_tables
WHERE table_name IN (
'JOB_HISTORY','MITARBEITER','JOB','STANDORT','LAND','REGION','ABTEILUNG'
)) LOOP
safe_drop('DROP TABLE ' || rec.table_name || ' CASCADE CONSTRAINTS');
END LOOP;
END;
/
-- ========== DOMAINS & SEQUENCES ========
CREATE SEQUENCE ABTEILUNG_SEQ START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE;
CREATE SEQUENCE REGION_SEQ START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE;
CREATE SEQUENCE LAND_SEQ START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE;
CREATE SEQUENCE STANDORT_SEQ START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE;
CREATE SEQUENCE MITARBEITER_SEQ START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE;
CREATE SEQUENCE JOB_SEQ START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE;
CREATE SEQUENCE JOB_HISTORY_SEQ START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE;
-- ========== PHASE 1: TABELLEN OHNE FK ========
-- 1. REGION
CREATE TABLE REGION (
ID INTEGER NOT NULL,
REGION VARCHAR2(50) NOT NULL,
BESCHREIBUNG VARCHAR2(255),
CREATED_BY VARCHAR2(50),
CREATED TIMESTAMP,
MODIFIED_BY VARCHAR2(50),
MODIFIED TIMESTAMP,
CONSTRAINT REGION_PK PRIMARY KEY (ID)
);
-- 2. LAND
CREATE TABLE LAND (
ID INTEGER NOT NULL,
LAND VARCHAR2(50) NOT NULL,
REGION_ID INTEGER,
CREATED_BY VARCHAR2(50),
CREATED TIMESTAMP,
MODIFIED_BY VARCHAR2(50),
MODIFIED TIMESTAMP,
CONSTRAINT LAND_PK PRIMARY KEY (ID)
);
-- 3. STANDORT
CREATE TABLE STANDORT (
ID INTEGER NOT NULL,
STANDORT VARCHAR2(50) NOT NULL,
STRASSE VARCHAR2(50),
PLZ VARCHAR2(5),
ORT VARCHAR2(50),
LAND_ID INTEGER,
CREATED_BY VARCHAR2(50),
CREATED TIMESTAMP,
MODIFIED_BY VARCHAR2(50),
MODIFIED TIMESTAMP,
CONSTRAINT STANDORT_PK PRIMARY KEY (ID)
);
-- 4. ABTEILUNG
CREATE TABLE ABTEILUNG (
ID INTEGER NOT NULL,
ABTEILUNG VARCHAR2(50) NOT NULL,
BESCHREIBUNG VARCHAR2(255),
MANAGER_ID INTEGER,
STANDORT_ID INTEGER,
CREATED_BY VARCHAR2(50),
CREATED TIMESTAMP,
MODIFIED_BY VARCHAR2(50),
MODIFIED TIMESTAMP,
CONSTRAINT ABTEILUNG_PK PRIMARY KEY (ID)
);
-- 5. JOB
CREATE TABLE JOB (
ID INTEGER NOT NULL,
TITEL VARCHAR2(50) NOT NULL,
MIDESTGEHALT NUMBER(15,2) NOT NULL,
MAXIMALGEHALT NUMBER(15,2) NOT NULL,
CREATED_BY VARCHAR2(50),
CREATED TIMESTAMP,
MODIFIED_BY VARCHAR2(50),
MODIFIED TIMESTAMP,
CONSTRAINT JOB_PK PRIMARY KEY (ID),
CONSTRAINT JOB_GEHALT_CK CHECK (MIDESTGEHALT <= MAXIMALGEHALT)
);
-- 6. MITARBEITER
CREATE TABLE MITARBEITER (
ID INTEGER NOT NULL,
NAME VARCHAR2(50) NOT NULL,
VORNAME VARCHAR2(50) NOT NULL,
EMAIL VARCHAR2(255),
FON VARCHAR2(50),
MOBIL VARCHAR2(50),
BESCHAFTIGT_SEIT DATE NOT NULL,
MANAGER_ID INTEGER,
JOB_ID INTEGER,
ABTEILUNG_ID INTEGER,
GEHALT NUMBER(15,2),
CREATED_BY VARCHAR2(50),
CREATED TIMESTAMP,
MODIFIED_BY VARCHAR2(50),
MODIFIED TIMESTAMP,
CONSTRAINT MITARBEITER_PK PRIMARY KEY (ID)
);
-- 7. JOB_HISTORY
CREATE TABLE JOB_HISTORY (
ID INTEGER NOT NULL,
MITARBEITER_ID INTEGER,
JOB_ID INTEGER,
ABTEILUNG_ID INTEGER,
BEGINN_DATUM DATE NOT NULL,
ENDE_DATUM DATE,
CREATED_BY VARCHAR2(50),
CREATED TIMESTAMP,
MODIFIED_BY VARCHAR2(50),
MODIFIED TIMESTAMP,
CONSTRAINT JOB_HISTORY_PK PRIMARY KEY (ID),
CONSTRAINT JOB_HISTORY_DATE_CK CHECK (ENDE_DATUM IS NULL OR ENDE_DATUM >= BEGINN_DATUM)
);
-- ========== PHASE 2: UNIQUE + INDEX + FK ========
-- Unique Constraints + Indexe (NN U)
CREATE UNIQUE INDEX REGION_UKX ON REGION (REGION);
ALTER TABLE REGION ADD CONSTRAINT REGION_UK UNIQUE (REGION);
CREATE UNIQUE INDEX LAND_UKX ON LAND (LAND);
ALTER TABLE LAND ADD CONSTRAINT LAND_UK UNIQUE (LAND);
CREATE UNIQUE INDEX ABTEILUNG_UKX ON ABTEILUNG (ABTEILUNG);
ALTER TABLE ABTEILUNG ADD CONSTRAINT ABTEILUNG_UK UNIQUE (ABTEILUNG);
CREATE UNIQUE INDEX JOB_UKX ON JOB (TITEL);
ALTER TABLE JOB ADD CONSTRAINT JOB_UK UNIQUE (TITEL);
-- FK-Indexe (Detailtabelle)
CREATE INDEX LAND_REGION_FKX ON LAND (REGION_ID);
CREATE INDEX STANDORT_LAND_FKX ON STANDORT (LAND_ID);
CREATE INDEX ABTEILUNG_MANAGER_FKX ON ABTEILUNG (MANAGER_ID);
CREATE INDEX ABTEILUNG_STANDORT_FKX ON ABTEILUNG (STANDORT_ID);
CREATE INDEX MITARBEITER_MANAGER_FKX ON MITARBEITER (MANAGER_ID);
CREATE INDEX MITARBEITER_JOB_FKX ON MITARBEITER (JOB_ID);
CREATE INDEX MITARBEITER_ABTEILUNG_FKX ON MITARBEITER (ABTEILUNG_ID);
CREATE INDEX JOB_HISTORY_MITARBEITER_FKX ON JOB_HISTORY (MITARBEITER_ID);
CREATE INDEX JOB_HISTORY_JOB_FKX ON JOB_HISTORY (JOB_ID);
CREATE INDEX JOB_HISTORY_ABTEILUNG_FKX ON JOB_HISTORY (ABTEILUNG_ID);
-- Fremdschlüssel (in korrekter Reihenfolge)
ALTER TABLE LAND ADD CONSTRAINT LAND_REGION_FK
FOREIGN KEY (REGION_ID) REFERENCES REGION(ID);
ALTER TABLE STANDORT ADD CONSTRAINT STANDORT_LAND_FK
FOREIGN KEY (LAND_ID) REFERENCES LAND(ID);
ALTER TABLE ABTEILUNG ADD CONSTRAINT ABTEILUNG_STANDORT_FK
FOREIGN KEY (STANDORT_ID) REFERENCES STANDORT(ID);
ALTER TABLE ABTEILUNG ADD CONSTRAINT ABTEILUNG_MANAGER_FK
FOREIGN KEY (MANAGER_ID) REFERENCES MITARBEITER(ID);
ALTER TABLE MITARBEITER ADD CONSTRAINT MITARBEITER_JOB_FK
FOREIGN KEY (JOB_ID) REFERENCES JOB(ID);
ALTER TABLE MITARBEITER ADD CONSTRAINT MITARBEITER_ABTEILUNG_FK
FOREIGN KEY (ABTEILUNG_ID) REFERENCES ABTEILUNG(ID);
ALTER TABLE MITARBEITER ADD CONSTRAINT MITARBEITER_MANAGER_FK
FOREIGN KEY (MANAGER_ID) REFERENCES MITARBEITER(ID);
ALTER TABLE JOB_HISTORY ADD CONSTRAINT JOB_HISTORY_MITARBEITER_FK
FOREIGN KEY (MITARBEITER_ID) REFERENCES MITARBEITER(ID);
ALTER TABLE JOB_HISTORY ADD CONSTRAINT JOB_HISTORY_JOB_FK
FOREIGN KEY (JOB_ID) REFERENCES JOB(ID);
ALTER TABLE JOB_HISTORY ADD CONSTRAINT JOB_HISTORY_ABTEILUNG_FK
FOREIGN KEY (ABTEILUNG_ID) REFERENCES ABTEILUNG(ID);
-- ========== TRIGGERS ========
-- REGION
CREATE OR REPLACE TRIGGER REGION_INS_TRG
BEFORE INSERT ON REGION
FOR EACH ROW
BEGIN
IF :NEW.ID IS NULL THEN
SELECT REGION_SEQ.NEXTVAL INTO :NEW.ID FROM DUAL;
END IF;
:NEW.CREATED_BY := NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER);
:NEW.CREATED := SYSTIMESTAMP;
END;
/
CREATE OR REPLACE TRIGGER REGION_UPD_TRG
BEFORE UPDATE ON REGION
FOR EACH ROW
BEGIN
:NEW.MODIFIED_BY := NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER);
:NEW.MODIFIED := SYSTIMESTAMP;
END;
/
-- LAND
CREATE OR REPLACE TRIGGER LAND_INS_TRG
BEFORE INSERT ON LAND
FOR EACH ROW
BEGIN
IF :NEW.ID IS NULL THEN
SELECT LAND_SEQ.NEXTVAL INTO :NEW.ID FROM DUAL;
END IF;
:NEW.CREATED_BY := NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER);
:NEW.CREATED := SYSTIMESTAMP;
END;
/
CREATE OR REPLACE TRIGGER LAND_UPD_TRG
BEFORE UPDATE ON LAND
FOR EACH ROW
BEGIN
:NEW.MODIFIED_BY := NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER);
:NEW.MODIFIED := SYSTIMESTAMP;
END;
/
-- STANDORT
CREATE OR REPLACE TRIGGER STANDORT_INS_TRG
BEFORE INSERT ON STANDORT
FOR EACH ROW
BEGIN
IF :NEW.ID IS NULL THEN
SELECT STANDORT_SEQ.NEXTVAL INTO :NEW.ID FROM DUAL;
END IF;
:NEW.CREATED_BY := NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER);
:NEW.CREATED := SYSTIMESTAMP;
END;
/
CREATE OR REPLACE TRIGGER STANDORT_UPD_TRG
BEFORE UPDATE ON STANDORT
FOR EACH ROW
BEGIN
:NEW.MODIFIED_BY := NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER);
:NEW.MODIFIED := SYSTIMESTAMP;
END;
/
-- ABTEILUNG
CREATE OR REPLACE TRIGGER ABTEILUNG_INS_TRG
BEFORE INSERT ON ABTEILUNG
FOR EACH ROW
BEGIN
IF :NEW.ID IS NULL THEN
SELECT ABTEILUNG_SEQ.NEXTVAL INTO :NEW.ID FROM DUAL;
END IF;
:NEW.CREATED_BY := NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER);
:NEW.CREATED := SYSTIMESTAMP;
END;
/
CREATE OR REPLACE TRIGGER ABTEILUNG_UPD_TRG
BEFORE UPDATE ON ABTEILUNG
FOR EACH ROW
BEGIN
:NEW.MODIFIED_BY := NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER);
:NEW.MODIFIED := SYSTIMESTAMP;
END;
/
-- JOB
CREATE OR REPLACE TRIGGER JOB_INS_TRG
BEFORE INSERT ON JOB
FOR EACH ROW
BEGIN
IF :NEW.ID IS NULL THEN
SELECT JOB_SEQ.NEXTVAL INTO :NEW.ID FROM DUAL;
END IF;
:NEW.CREATED_BY := NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER);
:NEW.CREATED := SYSTIMESTAMP;
END;
/
CREATE OR REPLACE TRIGGER JOB_UPD_TRG
BEFORE UPDATE ON JOB
FOR EACH ROW
BEGIN
:NEW.MODIFIED_BY := NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER);
:NEW.MODIFIED := SYSTIMESTAMP;
END;
/
-- MITARBEITER
CREATE OR REPLACE TRIGGER MITARBEITER_INS_TRG
BEFORE INSERT ON MITARBEITER
FOR EACH ROW
BEGIN
IF :NEW.ID IS NULL THEN
SELECT MITARBEITER_SEQ.NEXTVAL INTO :NEW.ID FROM DUAL;
END IF;
:NEW.CREATED_BY := NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER);
:NEW.CREATED := SYSTIMESTAMP;
END;
/
CREATE OR REPLACE TRIGGER MITARBEITER_UPD_TRG
BEFORE UPDATE ON MITARBEITER
FOR EACH ROW
BEGIN
:NEW.MODIFIED_BY := NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER);
:NEW.MODIFIED := SYSTIMESTAMP;
END;
/
-- JOB_HISTORY
CREATE OR REPLACE TRIGGER JOB_HISTORY_INS_TRG
BEFORE INSERT ON JOB_HISTORY
FOR EACH ROW
BEGIN
IF :NEW.ID IS NULL THEN
SELECT JOB_HISTORY_SEQ.NEXTVAL INTO :NEW.ID FROM DUAL;
END IF;
:NEW.CREATED_BY := NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER);
:NEW.CREATED := SYSTIMESTAMP;
END;
/
CREATE OR REPLACE TRIGGER JOB_HISTORY_UPD_TRG
BEFORE UPDATE ON JOB_HISTORY
FOR EACH ROW
BEGIN
:NEW.MODIFIED_BY := NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER);
:NEW.MODIFIED := SYSTIMESTAMP;
END;
/
-- ========== ACCESS PACKAGES ========
-- 1. REGION_ACCESS
CREATE OR REPLACE PACKAGE REGION_ACCESS AS
FUNCTION INS(p_ROW IN REGION%ROWTYPE, p_ERRCODE OUT NUMBER) RETURN INTEGER;
PROCEDURE UPD(p_ROW IN REGION%ROWTYPE, p_ERRCODE OUT NUMBER);
PROCEDURE DEL(p_ID IN INTEGER, p_ERRCODE OUT NUMBER);
END REGION_ACCESS;
/
CREATE OR REPLACE PACKAGE BODY REGION_ACCESS AS
FUNCTION INS(p_ROW IN REGION%ROWTYPE, p_ERRCODE OUT NUMBER) RETURN INTEGER IS
v_ID INTEGER;
BEGIN
INSERT INTO REGION (ID, REGION, BESCHREIBUNG, CREATED_BY, CREATED, MODIFIED_BY, MODIFIED)
VALUES (
REGION_SEQ.NEXTVAL,
p_ROW.REGION, p_ROW.BESCHREIBUNG,
NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER), SYSTIMESTAMP,
NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER), SYSTIMESTAMP
) RETURNING ID INTO v_ID;
p_ERRCODE := 0; RETURN v_ID;
EXCEPTION WHEN OTHERS THEN p_ERRCODE := SQLCODE; RETURN -1;
END;
PROCEDURE UPD(p_ROW IN REGION%ROWTYPE, p_ERRCODE OUT NUMBER) IS
BEGIN
UPDATE REGION SET REGION = p_ROW.REGION, BESCHREIBUNG = p_ROW.BESCHREIBUNG,
MODIFIED_BY = NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER),
MODIFIED = SYSTIMESTAMP WHERE ID = p_ROW.ID;
IF SQL%NOTFOUND THEN p_ERRCODE := -20001; ELSE p_ERRCODE := 0; END IF;
EXCEPTION WHEN OTHERS THEN p_ERRCODE := SQLCODE;
END;
PROCEDURE DEL(p_ID IN INTEGER, p_ERRCODE OUT NUMBER) IS
BEGIN
DELETE FROM REGION WHERE ID = p_ID;
IF SQL%NOTFOUND THEN p_ERRCODE := -20001; ELSE p_ERRCODE := 0; END IF;
EXCEPTION WHEN OTHERS THEN p_ERRCODE := SQLCODE;
END;
END REGION_ACCESS;
/
-- 2. LAND_ACCESS
CREATE OR REPLACE PACKAGE LAND_ACCESS AS
FUNCTION INS(p_ROW IN LAND%ROWTYPE, p_ERRCODE OUT NUMBER) RETURN INTEGER;
PROCEDURE UPD(p_ROW IN LAND%ROWTYPE, p_ERRCODE OUT NUMBER);
PROCEDURE DEL(p_ID IN INTEGER, p_ERRCODE OUT NUMBER);
END LAND_ACCESS;
/
CREATE OR REPLACE PACKAGE BODY LAND_ACCESS AS
FUNCTION INS(p_ROW IN LAND%ROWTYPE, p_ERRCODE OUT NUMBER) RETURN INTEGER IS
v_ID INTEGER;
BEGIN
INSERT INTO LAND (ID, LAND, REGION_ID, CREATED_BY, CREATED, MODIFIED_BY, MODIFIED)
VALUES (
LAND_SEQ.NEXTVAL, p_ROW.LAND, p_ROW.REGION_ID,
NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER), SYSTIMESTAMP,
NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER), SYSTIMESTAMP
) RETURNING ID INTO v_ID;
p_ERRCODE := 0; RETURN v_ID;
EXCEPTION WHEN OTHERS THEN p_ERRCODE := SQLCODE; RETURN -1;
END;
PROCEDURE UPD(p_ROW IN LAND%ROWTYPE, p_ERRCODE OUT NUMBER) IS
BEGIN
UPDATE LAND SET LAND = p_ROW.LAND, REGION_ID = p_ROW.REGION_ID,
MODIFIED_BY = NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER),
MODIFIED = SYSTIMESTAMP WHERE ID = p_ROW.ID;
IF SQL%NOTFOUND THEN p_ERRCODE := -20001; ELSE p_ERRCODE := 0; END IF;
EXCEPTION WHEN OTHERS THEN p_ERRCODE := SQLCODE;
END;
PROCEDURE DEL(p_ID IN INTEGER, p_ERRCODE OUT NUMBER) IS
BEGIN
DELETE FROM LAND WHERE ID = p_ID;
IF SQL%NOTFOUND THEN p_ERRCODE := -20001; ELSE p_ERRCODE := 0; END IF;
EXCEPTION WHEN OTHERS THEN p_ERRCODE := SQLCODE;
END;
END LAND_ACCESS;
/
-- 3. STANDORT_ACCESS
CREATE OR REPLACE PACKAGE STANDORT_ACCESS AS
FUNCTION INS(p_ROW IN STANDORT%ROWTYPE, p_ERRCODE OUT NUMBER) RETURN INTEGER;
PROCEDURE UPD(p_ROW IN STANDORT%ROWTYPE, p_ERRCODE OUT NUMBER);
PROCEDURE DEL(p_ID IN INTEGER, p_ERRCODE OUT NUMBER);
END STANDORT_ACCESS;
/
CREATE OR REPLACE PACKAGE BODY STANDORT_ACCESS AS
FUNCTION INS(p_ROW IN STANDORT%ROWTYPE, p_ERRCODE OUT NUMBER) RETURN INTEGER IS
v_ID INTEGER;
BEGIN
INSERT INTO STANDORT (
ID, STANDORT, STRASSE, PLZ, ORT, LAND_ID, CREATED_BY, CREATED, MODIFIED_BY, MODIFIED
) VALUES (
STANDORT_SEQ.NEXTVAL,
p_ROW.STANDORT, p_ROW.STRASSE, p_ROW.PLZ, p_ROW.ORT, p_ROW.LAND_ID,
NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER), SYSTIMESTAMP,
NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER), SYSTIMESTAMP
) RETURNING ID INTO v_ID;
p_ERRCODE := 0; RETURN v_ID;
EXCEPTION WHEN OTHERS THEN p_ERRCODE := SQLCODE; RETURN -1;
END;
PROCEDURE UPD(p_ROW IN STANDORT%ROWTYPE, p_ERRCODE OUT NUMBER) IS
BEGIN
UPDATE STANDORT SET
STANDORT = p_ROW.STANDORT, STRASSE = p_ROW.STRASSE, PLZ = p_ROW.PLZ,
ORT = p_ROW.ORT, LAND_ID = p_ROW.LAND_ID,
MODIFIED_BY = NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER),
MODIFIED = SYSTIMESTAMP WHERE ID = p_ROW.ID;
IF SQL%NOTFOUND THEN p_ERRCODE := -20001; ELSE p_ERRCODE := 0; END IF;
EXCEPTION WHEN OTHERS THEN p_ERRCODE := SQLCODE;
END;
PROCEDURE DEL(p_ID IN INTEGER, p_ERRCODE OUT NUMBER) IS
BEGIN
DELETE FROM STANDORT WHERE ID = p_ID;
IF SQL%NOTFOUND THEN p_ERRCODE := -20001; ELSE p_ERRCODE := 0; END IF;
EXCEPTION WHEN OTHERS THEN p_ERRCODE := SQLCODE;
END;
END STANDORT_ACCESS;
/
-- 4. ABTEILUNG_ACCESS
CREATE OR REPLACE PACKAGE ABTEILUNG_ACCESS AS
FUNCTION INS(p_ROW IN ABTEILUNG%ROWTYPE, p_ERRCODE OUT NUMBER) RETURN INTEGER;
PROCEDURE UPD(p_ROW IN ABTEILUNG%ROWTYPE, p_ERRCODE OUT NUMBER);
PROCEDURE DEL(p_ID IN INTEGER, p_ERRCODE OUT NUMBER);
END ABTEILUNG_ACCESS;
/
CREATE OR REPLACE PACKAGE BODY ABTEILUNG_ACCESS AS
FUNCTION INS(p_ROW IN ABTEILUNG%ROWTYPE, p_ERRCODE OUT NUMBER) RETURN INTEGER IS
v_ID INTEGER;
BEGIN
INSERT INTO ABTEILUNG (
ID, ABTEILUNG, BESCHREIBUNG, MANAGER_ID, STANDORT_ID, CREATED_BY, CREATED, MODIFIED_BY, MODIFIED
) VALUES (
ABTEILUNG_SEQ.NEXTVAL,
p_ROW.ABTEILUNG, p_ROW.BESCHREIBUNG, p_ROW.MANAGER_ID, p_ROW.STANDORT_ID,
NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER), SYSTIMESTAMP,
NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER), SYSTIMESTAMP
) RETURNING ID INTO v_ID;
p_ERRCODE := 0; RETURN v_ID;
EXCEPTION WHEN OTHERS THEN p_ERRCODE := SQLCODE; RETURN -1;
END;
PROCEDURE UPD(p_ROW IN ABTEILUNG%ROWTYPE, p_ERRCODE OUT NUMBER) IS
BEGIN
UPDATE ABTEILUNG SET
ABTEILUNG = p_ROW.ABTEILUNG, BESCHREIBUNG = p_ROW.BESCHREIBUNG,
MANAGER_ID = p_ROW.MANAGER_ID, STANDORT_ID = p_ROW.STANDORT_ID,
MODIFIED_BY = NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER),
MODIFIED = SYSTIMESTAMP WHERE ID = p_ROW.ID;
IF SQL%NOTFOUND THEN p_ERRCODE := -20001; ELSE p_ERRCODE := 0; END IF;
EXCEPTION WHEN OTHERS THEN p_ERRCODE := SQLCODE;
END;
PROCEDURE DEL(p_ID IN INTEGER, p_ERRCODE OUT NUMBER) IS
BEGIN
DELETE FROM ABTEILUNG WHERE ID = p_ID;
IF SQL%NOTFOUND THEN p_ERRCODE := -20001; ELSE p_ERRCODE := 0; END IF;
EXCEPTION WHEN OTHERS THEN p_ERRCODE := SQLCODE;
END;
END ABTEILUNG_ACCESS;
/
-- 5. JOB_ACCESS
CREATE OR REPLACE PACKAGE JOB_ACCESS AS
FUNCTION INS(p_ROW IN JOB%ROWTYPE, p_ERRCODE OUT NUMBER) RETURN INTEGER;
PROCEDURE UPD(p_ROW IN JOB%ROWTYPE, p_ERRCODE OUT NUMBER);
PROCEDURE DEL(p_ID IN INTEGER, p_ERRCODE OUT NUMBER);
END JOB_ACCESS;
/
CREATE OR REPLACE PACKAGE BODY JOB_ACCESS AS
FUNCTION INS(p_ROW IN JOB%ROWTYPE, p_ERRCODE OUT NUMBER) RETURN INTEGER IS
v_ID INTEGER;
BEGIN
INSERT INTO JOB (
ID, TITEL, MIDESTGEHALT, MAXIMALGEHALT, CREATED_BY, CREATED, MODIFIED_BY, MODIFIED
) VALUES (
JOB_SEQ.NEXTVAL,
p_ROW.TITEL, p_ROW.MIDESTGEHALT, p_ROW.MAXIMALGEHALT,
NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER), SYSTIMESTAMP,
NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER), SYSTIMESTAMP
) RETURNING ID INTO v_ID;
p_ERRCODE := 0; RETURN v_ID;
EXCEPTION WHEN OTHERS THEN p_ERRCODE := SQLCODE; RETURN -1;
END;
PROCEDURE UPD(p_ROW IN JOB%ROWTYPE, p_ERRCODE OUT NUMBER) IS
BEGIN
UPDATE JOB SET TITEL = p_ROW.TITEL, MIDESTGEHALT = p_ROW.MIDESTGEHALT, MAXIMALGEHALT = p_ROW.MAXIMALGEHALT,
MODIFIED_BY = NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER),
MODIFIED = SYSTIMESTAMP WHERE ID = p_ROW.ID;
IF SQL%NOTFOUND THEN p_ERRCODE := -20001; ELSE p_ERRCODE := 0; END IF;
EXCEPTION WHEN OTHERS THEN p_ERRCODE := SQLCODE;
END;
PROCEDURE DEL(p_ID IN INTEGER, p_ERRCODE OUT NUMBER) IS
BEGIN
DELETE FROM JOB WHERE ID = p_ID;
IF SQL%NOTFOUND THEN p_ERRCODE := -20001; ELSE p_ERRCODE := 0; END IF;
EXCEPTION WHEN OTHERS THEN p_ERRCODE := SQLCODE;
END;
END JOB_ACCESS;
/
-- 6. MITARBEITER_ACCESS
CREATE OR REPLACE PACKAGE MITARBEITER_ACCESS AS
FUNCTION INS(p_ROW IN MITARBEITER%ROWTYPE, p_ERRCODE OUT NUMBER) RETURN INTEGER;
PROCEDURE UPD(p_ROW IN MITARBEITER%ROWTYPE, p_ERRCODE OUT NUMBER);
PROCEDURE DEL(p_ID IN INTEGER, p_ERRCODE OUT NUMBER);
END MITARBEITER_ACCESS;
/
CREATE OR REPLACE PACKAGE BODY MITARBEITER_ACCESS AS
FUNCTION INS(p_ROW IN MITARBEITER%ROWTYPE, p_ERRCODE OUT NUMBER) RETURN INTEGER IS
v_ID INTEGER;
BEGIN
INSERT INTO MITARBEITER (
ID, NAME, VORNAME, EMAIL, FON, MOBIL, BESCHAFTIGT_SEIT,
MANAGER_ID, JOB_ID, ABTEILUNG_ID, GEHALT, CREATED_BY, CREATED, MODIFIED_BY, MODIFIED
) VALUES (
MITARBEITER_SEQ.NEXTVAL,
p_ROW.NAME, p_ROW.VORNAME, p_ROW.EMAIL, p_ROW.FON, p_ROW.MOBIL, p_ROW.BESCHAFTIGT_SEIT,
p_ROW.MANAGER_ID, p_ROW.JOB_ID, p_ROW.ABTEILUNG_ID, p_ROW.GEHALT,
NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER), SYSTIMESTAMP,
NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER), SYSTIMESTAMP
) RETURNING ID INTO v_ID;
p_ERRCODE := 0; RETURN v_ID;
EXCEPTION WHEN OTHERS THEN p_ERRCODE := SQLCODE; RETURN -1;
END;
PROCEDURE UPD(p_ROW IN MITARBEITER%ROWTYPE, p_ERRCODE OUT NUMBER) IS
BEGIN
UPDATE MITARBEITER SET
NAME = p_ROW.NAME, VORNAME = p_ROW.VORNAME, EMAIL = p_ROW.EMAIL,
FON = p_ROW.FON, MOBIL = p_ROW.MOBIL, BESCHAFTIGT_SEIT = p_ROW.BESCHAFTIGT_SEIT,
MANAGER_ID = p_ROW.MANAGER_ID, JOB_ID = p_ROW.JOB_ID,
ABTEILUNG_ID = p_ROW.ABTEILUNG_ID, GEHALT = p_ROW.GEHALT,
MODIFIED_BY = NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER),
MODIFIED = SYSTIMESTAMP WHERE ID = p_ROW.ID;
IF SQL%NOTFOUND THEN p_ERRCODE := -20001; ELSE p_ERRCODE := 0; END IF;
EXCEPTION WHEN OTHERS THEN p_ERRCODE := SQLCODE;
END;
PROCEDURE DEL(p_ID IN INTEGER, p_ERRCODE OUT NUMBER) IS
BEGIN
DELETE FROM MITARBEITER WHERE ID = p_ID;
IF SQL%NOTFOUND THEN p_ERRCODE := -20001; ELSE p_ERRCODE := 0; END IF;
EXCEPTION WHEN OTHERS THEN p_ERRCODE := SQLCODE;
END;
END MITARBEITER_ACCESS;
/
-- 7. JOB_HISTORY_ACCESS
CREATE OR REPLACE PACKAGE JOB_HISTORY_ACCESS AS
FUNCTION INS(p_ROW IN JOB_HISTORY%ROWTYPE, p_ERRCODE OUT NUMBER) RETURN INTEGER;
PROCEDURE UPD(p_ROW IN JOB_HISTORY%ROWTYPE, p_ERRCODE OUT NUMBER);
PROCEDURE DEL(p_ID IN INTEGER, p_ERRCODE OUT NUMBER);
END JOB_HISTORY_ACCESS;
/
CREATE OR REPLACE PACKAGE BODY JOB_HISTORY_ACCESS AS
FUNCTION INS(p_ROW IN JOB_HISTORY%ROWTYPE, p_ERRCODE OUT NUMBER) RETURN INTEGER IS
v_ID INTEGER;
BEGIN
INSERT INTO JOB_HISTORY (
ID, MITARBEITER_ID, JOB_ID, ABTEILUNG_ID, BEGINN_DATUM, ENDE_DATUM,
CREATED_BY, CREATED, MODIFIED_BY, MODIFIED
) VALUES (
JOB_HISTORY_SEQ.NEXTVAL,
p_ROW.MITARBEITER_ID, p_ROW.JOB_ID, p_ROW.ABTEILUNG_ID,
p_ROW.BEGINN_DATUM, p_ROW.ENDE_DATUM,
NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER), SYSTIMESTAMP,
NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER), SYSTIMESTAMP
) RETURNING ID INTO v_ID;
p_ERRCODE := 0; RETURN v_ID;
EXCEPTION WHEN OTHERS THEN p_ERRCODE := SQLCODE; RETURN -1;
END;
PROCEDURE UPD(p_ROW IN JOB_HISTORY%ROWTYPE, p_ERRCODE OUT NUMBER) IS
BEGIN
UPDATE JOB_HISTORY SET
MITARBEITER_ID = p_ROW.MITARBEITER_ID, JOB_ID = p_ROW.JOB_ID,
ABTEILUNG_ID = p_ROW.ABTEILUNG_ID, BEGINN_DATUM = p_ROW.BEGINN_DATUM,
ENDE_DATUM = p_ROW.ENDE_DATUM,
MODIFIED_BY = NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER),
MODIFIED = SYSTIMESTAMP WHERE ID = p_ROW.ID;
IF SQL%NOTFOUND THEN p_ERRCODE := -20001; ELSE p_ERRCODE := 0; END IF;
EXCEPTION WHEN OTHERS THEN p_ERRCODE := SQLCODE;
END;
PROCEDURE DEL(p_ID IN INTEGER, p_ERRCODE OUT NUMBER) IS
BEGIN
DELETE FROM JOB_HISTORY WHERE ID = p_ID;
IF SQL%NOTFOUND THEN p_ERRCODE := -20001; ELSE p_ERRCODE := 0; END IF;
EXCEPTION WHEN OTHERS THEN p_ERRCODE := SQLCODE;
END;
END JOB_HISTORY_ACCESS;
/
---
-- ========== TESTDATEN ========
-- Vorausgesetzt: Tabellen, Sequenzen, Packages existieren
-- 1–3 Standorte pro Land → ~50 Standorte total
-- 1. Regionen (15): DACH + Erdteile + Sonstige
INSERT ALL
INTO REGION (ID, REGION, BESCHREIBUNG) VALUES (1, 'SCHWEIZ', 'Schweiz')
INTO REGION (ID, REGION, BESCHREIBUNG) VALUES (2, 'DEUTSCHLAND', 'Deutschland')
INTO REGION (ID, REGION, BESCHREIBUNG) VALUES (3, 'ÖSTERREICH', 'Österreich')
INTO REGION (ID, REGION, BESCHREIBUNG) VALUES (4, 'EUROPA', 'Europa')
INTO REGION (ID, REGION, BESCHREIBUNG) VALUES (5, 'AMERIKA', 'Nord- & Südamerika')
INTO REGION (ID, REGION, BESCHREIBUNG) VALUES (6, 'ASIEN', 'Asien')
INTO REGION (ID, REGION, BESCHREIBUNG) VALUES (7, 'AFRIKA', 'Afrika')
INTO REGION (ID, REGION, BESCHREIBUNG) VALUES (8, 'AUSTRALIEN', 'Australien & Ozeanien')
INTO REGION (ID, REGION, BESCHREIBUNG) VALUES (9, 'ANTARKTIS', 'Antarktis')
INTO REGION (ID, REGION, BESCHREIBUNG) VALUES (10, 'DACH', 'DACH-Region')
INTO REGION (ID, REGION, BESCHREIBUNG) VALUES (11, 'NORDAMERIKA', 'Nordamerika')
INTO REGION (ID, REGION, BESCHREIBUNG) VALUES (12, 'SÜDAMERIKA', 'Südamerika')
INTO REGION (ID, REGION, BESCHREIBUNG) VALUES (13, 'OSTEUROPA', 'Osteuropa')
INTO REGION (ID, REGION, BESCHREIBUNG) VALUES (14, 'WESTEUROPA', 'Westeuropa')
INTO REGION (ID, REGION, BESCHREIBUNG) VALUES (15, 'MIDDLE EAST', 'Naher Osten')
SELECT 1 FROM DUAL;
-- 2. Länder (20) mit Zuordnung zu Regionen
INSERT ALL
INTO LAND (ID, LAND, REGION_ID) VALUES (1, 'Schweiz', 10)
INTO LAND (ID, LAND, REGION_ID) VALUES (2, 'Deutschland', 10)
INTO LAND (ID, LAND, REGION_ID) VALUES (3, 'Österreich', 10)
INTO LAND (ID, LAND, REGION_ID) VALUES (4, 'Frankreich', 14)
INTO LAND (ID, LAND, REGION_ID) VALUES (5, 'Italien', 14)
INTO LAND (ID, LAND, REGION_ID) VALUES (6, 'Spanien', 14)
INTO LAND (ID, LAND, REGION_ID) VALUES (7, 'Niederlande', 14)
INTO LAND (ID, LAND, REGION_ID) VALUES (8, 'Belgien', 14)
INTO LAND (ID, LAND, REGION_ID) VALUES (9, 'Luxemburg', 14)
INTO LAND (ID, LAND, REGION_ID) VALUES (10, 'Polen', 13)
INTO LAND (ID, LAND, REGION_ID) VALUES (11, 'Tschechien', 13)
INTO LAND (ID, LAND, REGION_ID) VALUES (12, 'Ungarn', 13)
INTO LAND (ID, LAND, REGION_ID) VALUES (13, 'Rumänien', 13)
INTO LAND (ID, LAND, REGION_ID) VALUES (14, 'Bulgarien', 13)
INTO LAND (ID, LAND, REGION_ID) VALUES (15, 'USA', 11)
INTO LAND (ID, LAND, REGION_ID) VALUES (16, 'Kanada', 11)
INTO LAND (ID, LAND, REGION_ID) VALUES (17, 'Mexiko', 11)
INTO LAND (ID, LAND, REGION_ID) VALUES (18, 'Brasilien', 12)
INTO LAND (ID, LAND, REGION_ID) VALUES (19, 'Argentinien', 12)
INTO LAND (ID, LAND, REGION_ID) VALUES (20, 'China', 6)
SELECT 1 FROM DUAL;
-- 3. Standorte (1–3 pro Land, insgesamt ~50)
-- Alle auf Zürich (PLZ=8000, Ort=Zürich), Strasse=Beispielstrasse
INSERT INTO STANDORT (
ID, STANDORT, STRASSE, PLZ, ORT, LAND_ID, CREATED_BY, CREATED, MODIFIED_BY, MODIFIED
)
SELECT
STANDORT_SEQ.NEXTVAL,
'Standort ' || l.LAND || ' - ' || TO_CHAR(LEVEL, 'FM00'),
'Beispielstrasse ' || TO_CHAR(LEVEL, 'FM00'),
'80' || TO_CHAR(LEVEL, 'FM00'),
'Zürich',
l.ID,
NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER),
SYSTIMESTAMP,
NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER),
SYSTIMESTAMP
FROM LAND l
CONNECT BY LEVEL <= CASE
WHEN l.LAND IN ('Schweiz','Deutschland','Österreich') THEN 3
ELSE 1 + MOD(LEVEL, 2)
END
START WITH 1 = 1;
-- 4. Abteilungen (100)
-- Hauptabteilungen + 90 Regionalabteilungen (auf Zürich Standort-ID=1)
INSERT INTO ABTEILUNG (
ID, ABTEILUNG, BESCHREIBUNG, STANDORT_ID, CREATED_BY, CREATED, MODIFIED_BY, MODIFIED
)
SELECT
ABTEILUNG_SEQ.NEXTVAL,
CASE
WHEN LEVEL <= 10 THEN
CASE LEVEL
WHEN 1 THEN 'Geschäftsführung'
WHEN 2 THEN 'HR'
WHEN 3 THEN 'Finanzen'
WHEN 4 THEN 'IT'
WHEN 5 THEN 'Marketing'
WHEN 6 THEN 'Vertrieb'
WHEN 7 THEN 'Produktion'
WHEN 8 THEN 'Logistik'
WHEN 9 THEN 'Forschung & Entwicklung'
WHEN 10 THEN 'Qualitätsmanagement'
END
ELSE 'Regionalstelle ' || TO_CHAR(LEVEL - 10, 'FM00')
END,
CASE
WHEN LEVEL <= 10 THEN
CASE LEVEL
WHEN 1 THEN 'Executive Management'
WHEN 2 THEN 'Personalabteilung'
WHEN 3 THEN 'Buchhaltung & Controlling'
WHEN 4 THEN 'Informationstechnologie'
WHEN 5 THEN 'Marketing & Kommunikation'
WHEN 6 THEN 'Außendienst'
WHEN 7 THEN 'Fertigung'
WHEN 8 THEN 'Einkauf & Versand'
WHEN 9 THEN 'R&D'
WHEN 10 THEN 'QM'
END
ELSE 'Regionale Niederlassung ' || TO_CHAR(LEVEL - 10, 'FM00')
END,
1, -- Standort Zürich für alle
NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER),
SYSTIMESTAMP,
NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER),
SYSTIMESTAMP
FROM DUAL
CONNECT BY LEVEL <= 100;
-- 5. Jobs (20)
INSERT ALL
INTO JOB (ID, TITEL, MIDESTGEHALT, MAXIMALGEHALT) VALUES (1, 'CEO', 250000, 500000)
INTO JOB (ID, TITEL, MIDESTGEHALT, MAXIMALGEHALT) VALUES (2, 'CFO', 200000, 400000)
INTO JOB (ID, TITEL, MIDESTGEHALT, MAXIMALGEHALT) VALUES (3, 'CTO', 180000, 350000)
INTO JOB (ID, TITEL, MIDESTGEHALT, MAXIMALGEHALT) VALUES (4, 'Senior Manager', 120000, 200000)
INTO JOB (ID, TITEL, MIDESTGEHALT, MAXIMALGEHALT) VALUES (5, 'Manager', 90000, 150000)
INTO JOB (ID, TITEL, MIDESTGEHALT, MAXIMALGEHALT) VALUES (6, 'Teamlead', 70000, 110000)
INTO JOB (ID, TITEL, MIDESTGEHALT, MAXIMALGEHALT) VALUES (7, 'Senior Engineer', 60000, 95000)
INTO JOB (ID, TITEL, MIDESTGEHALT, MAXIMALGEHALT) VALUES (8, 'Engineer', 50000, 80000)
INTO JOB (ID, TITEL, MIDESTGEHALT, MAXIMALGEHALT) VALUES (9, 'Junior Engineer', 40000, 60000)
INTO JOB (ID, TITEL, MIDESTGEHALT, MAXIMALGEHALT) VALUES (10, 'Trainee', 35000, 45000)
INTO JOB (ID, TITEL, MIDESTGEHALT, MAXIMALGEHALT) VALUES (11, 'Senior Analyst', 65000, 90000)
INTO JOB (ID, TITEL, MIDESTGEHALT, MAXIMALGEHALT) VALUES (12, 'Analyst', 50000, 75000)
INTO JOB (ID, TITEL, MIDESTGEHALT, MAXIMALGEHALT) VALUES (13, 'Senior Consultant', 70000, 100000)
INTO JOB (ID, TITEL, MIDESTGEHALT, MAXIMALGEHALT) VALUES (14, 'Consultant', 55000, 80000)
INTO JOB (ID, TITEL, MIDESTGEHALT, MAXIMALGEHALT) VALUES (15, 'Senior Developer', 65000, 95000)
INTO JOB (ID, TITEL, MIDESTGEHALT, MAXIMALGEHALT) VALUES (16, 'Developer', 50000, 75000)
INTO JOB (ID, TITEL, MIDESTGEHALT, MAXIMALGEHALT) VALUES (17, 'Junior Developer', 40000, 60000)
INTO JOB (ID, TITEL, MIDESTGEHALT, MAXIMALGEHALT) VALUES (18, 'QA Engineer', 50000, 70000)
INTO JOB (ID, TITEL, MIDESTGEHALT, MAXIMALGEHALT) VALUES (19, 'Support Specialist', 45000, 65000)
INTO JOB (ID, TITEL, MIDESTGEHALT, MAXIMALGEHALT) VALUES (20, 'Administrator', 48000, 68000)
SELECT 1 FROM DUAL;
-- 6. Mitarbeiter (1000) mit rekursiver Manager-Hierarchie
INSERT INTO MITARBEITER (
ID, NAME, VORNAME, EMAIL, FON, MOBIL, BESCHAFTIGT_SEIT,
MANAGER_ID, JOB_ID, ABTEILUNG_ID, GEHALT,
CREATED_BY, CREATED, MODIFIED_BY, MODIFIED
)
WITH base AS (
SELECT 'Mustermann' AS name, 'Hans' AS vorname, 'hans.mustermann@firma.com' AS email,
'+41 44 123 45 67' AS fon, '+41 79 123 45 67' AS mobil,
DATE '2020-01-01' AS eingestellt,
NULL AS manager_id, 1 AS job_id, 1 AS abteilung_id, 350000 AS gehalt
FROM DUAL
UNION ALL
SELECT 'Musterfrau', 'Anna', 'anna.musterfrau@firma.com', '+41 44 123 45 68', '+41 79 123 45 68',
DATE '2020-01-01', NULL, 2, 3, 280000
FROM DUAL
UNION ALL
SELECT 'Schmidt', 'Peter', 'peter.schmidt@firma.com', '+41 44 123 45 69', '+41 79 123 45 69',
DATE '2020-01-02', NULL, 3, 4, 220000
FROM DUAL
UNION ALL
SELECT 'Müller', 'Sabine', 'sabine.muller@firma.com', '+41 44 123 45 70', '+41 79 123 45 70',
DATE '2020-02-15', 1, 4, 5, 120000
FROM DUAL
UNION ALL
SELECT 'Wegmann', 'Dirk', 'dirk.wegmann@firma.com', '+41 44 123 45 71', '+41 79 123 45 71',
DATE '2020-03-01', 4, 5, 5, 90000
FROM DUAL
UNION ALL
SELECT 'Fischer', 'Elisabeth', 'elisabeth.fischer@firma.com', '+41 44 123 45 72', '+41 79 123 45 72',
DATE '2020-04-10', 4, 7, 5, 75000
FROM DUAL
UNION ALL
SELECT 'Krause', 'Michael', 'michael.krause@firma.com', '+41 44 123 45 73', '+41 79 123 45 73',
DATE '2020-05-15', 5, 8, 6, 60000
FROM DUAL
UNION ALL
SELECT 'Schulze', 'Jutta', 'jutta.schulze@firma.com', '+41 44 123 45 74', '+41 79 123 45 74',
DATE '2020-06-01', 5, 9, 6, 50000
FROM DUAL
UNION ALL
SELECT 'Meier', 'Thomas', 'thomas.meier@firma.com', '+41 44 123 45 75', '+41 79 123 45 75',
DATE '2020-07-10', 7, 10, 6, 45000
FROM DUAL
UNION ALL
SELECT 'Bauer', 'Julia', 'julia.bauer@firma.com', '+41 44 123 45 76', '+41 79 123 45 76',
DATE '2020-08-20', 6, 10, 7, 45000
FROM DUAL
)
SELECT
MITARBEITER_SEQ.NEXTVAL,
name, vorname, email, fon, mobil,
eingestellt + MOD(LEVEL - 1, 365),
CASE
WHEN LEVEL <= 10 THEN NULL
ELSE (SELECT ID FROM MITARBEITER WHERE LEVEL_ID = MOD(LEVEL - 2, 10) + 1)
END,
1 + MOD(LEVEL, 20),
1 + MOD(LEVEL, 100),
40000 + MOD(LEVEL, 20) * 1000 + TRUNC(DBMS_RANDOM.VALUE(0, 10000)),
NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER),
SYSTIMESTAMP,
NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER),
SYSTIMESTAMP
FROM base
CONNECT BY LEVEL <= 1000
START WITH 1 = 1;
-- 7. Job_History (1–3 Einträge pro Mitarbeiter, total ~2500)
INSERT INTO JOB_HISTORY (
ID, MITARBEITER_ID, JOB_ID, ABTEILUNG_ID, BEGINN_DATUM, ENDE_DATUM,
CREATED_BY, CREATED, MODIFIED_BY, MODIFIED
)
WITH job_hist AS (
SELECT
MITARBEITER_ID,
1 + MOD(LEVEL, 20) AS job_id,
1 + MOD(LEVEL, 100) AS abteilung_id,
BESCHAFTIGT_SEIT + (LEVEL - 1) * 365 AS beginn,
CASE WHEN LEVEL < 3 THEN BESCHAFTIGT_SEIT + LEVEL * 365 ELSE NULL END AS ende
FROM MITARBEITER
CONNECT BY LEVEL <= 3
START WITH 1 = 1
)
SELECT
JOB_HISTORY_SEQ.NEXTVAL,
MITARBEITER_ID, job_id, abteilung_id, beginn, ende,
NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER),
SYSTIMESTAMP,
NVL(SYS_CONTEXT('APEX$SESSION', 'APP_USER'), USER),
SYSTIMESTAMP
FROM job_hist;
COMMIT;
-- Verify
SELECT 'Regionen' AS tabelle, COUNT(*) AS count FROM REGION
UNION ALL
SELECT 'Länder', COUNT(*) FROM LAND
UNION ALL
SELECT 'Standorte', COUNT(*) FROM STANDORT
UNION ALL
SELECT 'Abteilungen', COUNT(*) FROM ABTEILUNG
UNION ALL
SELECT 'Mitarbeiter', COUNT(*) FROM MITARBEITER
UNION ALL
SELECT 'Job_History', COUNT(*) FROM JOB_HISTORY;