Skip to Main Content
 
Titelbild Muniqsoft Training

Search Results

Oracle Tipp Text

Suche nach Bildern und Links in einer APEX Seite

Bereich:APEX, Version: ab APEX 23.2, Letzte Überarbeitung: 19.08.2024

Keywords:

Wollten Sie immer schon mal wissen, welche BIlder in Ihrer Seite vorkommen? Wenn man das über die APEX Tabellen lösen möchte wird es schwierig, denn davon gibt es fast 600.

Wir verwenden deswegen einen anderen Ansatz: Wir rendern die Seite und suchen dann nach Links oder Bildern.

Geben Sie dazu im Kopf des SQL Statements die Werte für die Application ID (hier 100

Bilder in einer Seite finden:

WITH my_app as  (select 100 as app_id, 1 as page_id FROM dual)
SELECT my_app.app_id,my_app.page_id,replace(column_value,'/','/') img FROM TABLE (
SELECT APEX_STRING.GREP(page, '<img src[^>]*','i')as bilder FROM (
select apex_web_service.make_rest_request(
p_url=>'https://www.muniqsoft-training.de/ords/oracle/r/muso_training/schulung/'||page_alias,
p_http_method=>'GET',
p_wallet_path=>'file:///opt/oracle/admin/FREE/https_wallet'
) as page 
from  apex_application_pages app,my_app
where app.application_id=my_app.app_id
and app.page_id =my_app.page_id)),my_app
/

Links in einer Seite finden:

WITH my_app as  (select 100 as app_id, 1 as page_id FROM dual)
SELECT my_app.app_id,my_app.page_id,replace(column_value,'/','/') link FROM TABLE (
SELECT APEX_STRING.GREP(page, '<a href=*[^>]*">[[:alnum:] ]{1,}<\/a>' ,'i')as bilder FROM (
select apex_web_service.make_rest_request(
p_url=>'https://www.muniqsoft-training.de/ords/oracle/r/muso_training/schulung/'||page_alias,
p_http_method=>'GET',
p_wallet_path=>'file:///opt/oracle/admin/FREE/https_wallet'
) as page 
from  apex_application_pages app,my_app
where app.application_id=my_app.app_id
and app.page_id =my_app.page_id)),my_app;

Weitere Tipps erfahren Sie in unserem APEX Kurs



Weitere Interessente Artikel zum Thema:


Empfohlene Schulungen zum Thema:


Oracle Constraints nachträglich anlegen

Bereich:SQL, Version: ab RDBMS 12.x:RDBMS 18.3:RDBMS 19.3:RDBMS 21.1, Letzte Überarbeitung: 08.08.2022

Keywords:Primary Key, Foreign Key, Not Null

Wie oft habe ich schon nach der Syntax gesucht um nachträglich einen Constraint auf eine Tabelle zu legen. Google ist ja da immer eine gute Hilfe, aber in deutsch gab es bisher wenig.
Das ändert sich nun, wir beschätigen uns mit dem Thema Constraints hier nun.

Nachträglich einen Not Null Constraints auf eine Tabelle legen:

ALTER TABLE emp MODIFY (ename VARCHAR2(10) CONSTRAINT emp_ename_nn NOT NULL);


Constraint wieder löschen:

ALTER TABLE emp DROP CONSTRAINT emp_ename_nn;


Nachträglich einen Primärschlüssel anlegen (Achtung es darf keiner bisher existieren):

ALTER TABLE emp ADD CONSTRAINT pk_emp PRIMARY KEY(empno);


Constraint wieder löschen:

ALTER TABLE emp DROP CONSTRAINT pk_emp;


Nachträglich einen Unique Index anlegen (davon dürfen Sie mehrere auf der gleichen Tabelle besitzen)
Hier erlauben wir pro Tag nur einen Mitarbeiter mit gleichem Nachname einzustellen.

ALTER TABLE emp ADD CONSTRAINT emp_hiredate_ename_uk UNIQUE KEY(hiredate,ename);


Constraint wieder löschen:

ALTER TABLE emp DROP CONSTRAINT emp_hiredate_ename_uk;



Foreign Key Constraint anlegen. (Dieser hat einen Bezug zu einer lokalen Spalte der Tabelle und eine Verbindung zu einer Spalte einer anderen Tabelle)

ALTER TABLE emp ADD CONSTRAINT fk_deptno FOREIGN KEY (deptno)
REFERENCES dept(deptno);


Der Foreign Key Constraint hat aber noch eine weitere nette Option: On Delete Cascade
Wenn hier ein Hauptdatensatz (bei uns z.B. die Abteilung) gelöscht wird, werden die Kinddatensätze (hier die Mitarbeiter der Abteilung) auch rekursiv gelöscht

ALTER TABLE emp ADD CONSTRAINT fk_deptno FOREIGN KEY (deptno)
REFERENCES dept(deptno) ON DELETE CASCADE;


Alternativ kann auch nur die abhängige Spalte auf NULL gesetzt werden

ALTER TABLE emp ADD CONSTRAINT fk_deptno FOREIGN KEY (deptno)
REFERENCES dept(deptno) ON DELETE SET NULL;


Constraint wieder löschen:

ALTER TABLE emp DROP CONSTRAINT fk_deptno;

 
Sonderfälle
Constraints können auch verzögert geprüft werden. Das ist dann sinvoll, wenn im Datenmodell mit Constraints Änderungen durchgeführt werden sollen, die gegen die Regeln des Constraint verstossen würden.
Durch die verzögerte Prüfung erfolgt dies erst beim Commit und nicht schon während der Transaktion.

ALTER TABLE emp ADD CONSTRAINT fk_deptno FOREIGN KEY (deptno)
REFERENCES dept(deptno) DEFERRABLE INITIALLY IMMEDIATE;

Bei der Option DEFERRABLE ENABLE NOVALIDATE  wird bei einem Unique Index ein Non-Unique Index angelegt.
Sollte ein Index auf den benötigten Spalten bereits vorhanden sein, wird dieser verwendet.

Danach kann die verzögerte Prüfung in der Session aktiviert werden:
ALTER SESSION SET CONSTRAINTS = DEFERRED;
oder wieder zurück auf Default:
ALTER SESSION SET CONSTRAINTS = IMMEDIATE;

 

Mit einem ALTER TABLE kann der Zustand der verzögerten Constraints verändert werden:

  • ENABLE VALIDATE   entspricht dem ENABLE. Der Constraint wird für alle Zeilen geprüft
  • ENABLE NOVALIDATE  hier werden nur neue Zeilen geprüft, bereits existierende Zeilen können gegen den Constraint verstossen:
    Oracle muss hier einen Non-Unique Index anlegen, weil ja evtl Doubletten bereits vorhanden sind.
  • DISABLE NOVALIDATE  entspricht dem Zustand DISABLE. Der Constraint wird nicht geprüft und damit kann es Daten geben, die gegen die Constraint Regel verstossen.
    Bei Unique Constraints wird der dazugehörige Index gelöscht
  • DISABLE VALIDATE bedeutet, dass der Constraint nicht geprüft wird und verbietet Änderungen an den betroffenen Spalten. Sinnvoll bei einem Exchange Parition Vorgng im DWH.
    Bei Unique Constraints wird der dazugehörige Index gelöscht
 

 

 



Weitere Interessente Artikel zum Thema:


Empfohlene Schulungen zum Thema:


Oracle ORDS 22.x-25.x Installation und Fehlerbehebung / ORDS Troubleshooting

Bereich:APEX:ORDS, Version: ab ORDS 23.1:ORDS 23.2:ORDS 24.2:ORDS 22.2, Letzte Überarbeitung: 04.03.2025

Keywords:Oracle, ORDS 22.x/23.x/24.x/25.x , Installation

Seit einiger Zeit  ist die neue Oracle ORDS Version 23.x zum Download verfügbar. 
Wie heisst ein berühmtes Zitat: Mann muss in einem System jeden Fehler gemacht haben um das System verstanden zu haben.         
Na dann sind wir kurz davor alles zu wissen :-) …

  1. Installation
  2. Prüfung im OS
  3. Silent Installation (Unix)
  4. Weitere Datenbank in ORDS eintragen (Unix)
  5. Silent Installation (Win)
  6. Zusätzliche Datenbank in ORDS eintragen (win)
  7. Prüfungen Im Betriebssystem
  8. Prüfungen in der Datenbank
  9. Gängige Fehler des ORDS
  10. Bonustrack: Apex Fehler

1. Installation

Laden Sie die Software von Oracle herunter und packen Sie das ZIP File in einem eigenen Ordner aus.      

 

2. Prüfung im OS

   
Prüfen Sie nun Ihre installierte Java Version:

Unix / Linux

java --version
    java 11.0.16.1 2022-08-18 LTS
    Java(TM) SE Runtime Environment 18.9 (build 11.0.16.1+1-LTS-1)
    Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.16.1+1-LTS-1, mixed mode)
                                    

Das sieht gut aus … OpenJDK wird offiziell von Oracle nicht unterstützt und kann Probleme verursachen! Verwenden Sie Java 11 oder Java 17

Windows:

java.exe --version
    openjdk 11 2018-09-25
    OpenJDK Runtime Environment 18.9 (build 11+28)
    OpenJDK 64-Bit Server VM 18.9 (build 11+28, mixed mode)

Das ist schlecht, weil Oracle unterstützt wie gesagt keine Java OpenJDK Version. KEINE !         
Es kann funktionieren, bei uns trat meist ein Webserver Fehler 500 auf (ohne große Begründung)         
Unterstützt werden nur die Oracle Java Versionen 11 und 17.

Nach erfolgreicher Installation der Oracle Java Version gehen Sie in den ausgepackten Ordner (z.B. cd c:\temp\ords)

Dort sollte ein “bin” Ordner liegen und da drin eine ords.exe und eine ords Datei

Tragen Sie Java in Ihren Pfad ein:

Windows:

set PATH=%PATH%;"C:\Program Files\Java\jdk-11\bin"

UNIX:

export PATH=$PATH:/usr/bin/

Wenn Java in Ihrem Pfad eingetragen wurde, starten Sie die Installation mit:

Windows:
    bin/ords.exe --config c:\oracle\ords install

Tipp: Sie sollten auch nach der Installation des Ords das “bin” Verzeichnis des ORDS und die Datei ords.war nicht wegwerfen. Für nachtägliche Wartungsarbeiten benötigt man die Dateien eventuell nochmal. 
Sie können die Dateien z.B in den Hauptordner des Config-Verzeichnisses kopieren, also z.B. C:\oracle\ords für Windows, oder /opt/oracle für Linux.

Ab ORDS Version 22.2.x wird der Pfad für den Config Ordner beim Tomcat unter UNIX im Service File eingetragen:

cat /etc/systemd/system/tomcat.service:

…
    Environment="JAVA_HOME=/usr/lib/jvm/jre"
    Environment="JAVA_OPTS=-Djava.security.egd=file:///dev/urandom -Dconfig.url=/opt/oracle/ords"
…

 

Windows (wird im Tomcat (tomcat9w.exe) Eigenschaftsfenster Reiter “Java” im Bereich “Java Options” (nicht “Java 9 Options” !!!) eingetragen)

-Dconfig.url=c:\oracle\ords

Unix/Linux:

 bin/ords --config /opt/oracle/ords install

Interaktion mit dem Skript:

# bin/ords --config /opt/oracle/ords install
    ORDS: Release 23.4 Produktion am Mi. Dez 15 06:49:08 2023
    Copyright (c) 2010, 2023, Oracle.
    Konfiguration:/opt/oracle/ords/
    Der Konfigurationsordner /opt/oracle/ords enthält keine Konfigurationsdateien.
    Oracle REST Data Services - Interaktive Installation
    Geben Sie eine Zahl zur Auswahl des Installationstyps ein
    [1] ORDS nur in der Datenbank installieren oder upgraden
    [2] Datenbankpool erstellen oder aktualisieren und ORDS in der Datenbank installieren/upgraden
    [3] Nur Datenbankpool erstellen oder aktualisieren
    Choose [2]:
    Geben Sie eine Zahl zur Auswahl des zu verwendenden Datenbankverbindungstyps ein
    [1] Basis (Hostname, Port, Servicename)
    [2] TNS (TNS-Alias, TNS-Verzeichnis)
    [3] Benutzerdefinierte Datenbank-URL
    Choose [1]:
    Geben Sie den Hostnamen der Datenbank ein [localhost]: 172.30.30.141
    Listener-Port der Datenbank eingeben [1521]:
    Datenbankservicename eingeben [orcl]: apex222
    Geben Sie einen Datenbankbenutzernamen mit Administratorberechtigungen an.
    Geben Sie den Administratorbenutzernamen ein: sys
    Datenbankkennwort für SYS AS SYSDBA eingeben:
    Verbindung zu Datenbankbenutzer: SYS AS SYSDBA URL: jdbc:oracle:thin:@//172.30.30.141:1521/apex222 wird hergestellt
    Informationen werden abgerufen.
    Geben Sie den Default Tablespace für ORDS_METADATA und ORDS_PUBLIC_USER ein [SYSAUX]:
    Geben Sie den Temporary Tablespace für ORDS_METADATA und ORDS_PUBLIC_USER ein [TEMP]:
    Geben Sie eine Zahl ein, um zusätzliche Features zum Aktivieren auszuwählen:
     [1] Datenbankaktionen (aktiviert alle Features)
     [2] REST-fähige SQL und Datenbank-API
     [3] REST-fähige SQL
     [4] Datenbank-API
     [5] Kein Wert
    Choose [1]:
     Geben Sie eine Zahl zum Konfigurieren und Starten von ORDS im Standalone-Modus ein
     [1] ORDS im Standalone-Modus konfigurieren und starten
     [2] Überspringen
     Choose [1]: 2
     …
     INFO: 08:19:07 Validating objects for Oracle REST Data Services.
     VALIDATION: 08:19:07 Starting validation for schema: ORDS_METADATA
     VALIDATION: 08:19:07 Validating objects
     VALIDATION: 08:19:08 Validating roles granted to ORDS_METADATA and
     ORDS_PUBLIC_USER
     VALIDATION: 08:19:08 Validating ORDS Public Synonyms
     VALIDATION: 08:19:08 Total objects: 306, invalid objects: 0, missing objects: 0
     VALIDATION: 08:19:08     94  INDEX
     VALIDATION: 08:19:08      3  LOB
     VALIDATION: 08:19:08     15  PACKAGE
     VALIDATION: 08:19:08     14  PACKAGE BODY
     VALIDATION: 08:19:08      1  PROCEDURE
     VALIDATION: 08:19:08     52  PUBLIC SYNONYM
     VALIDATION: 08:19:08      1  SEQUENCE
     VALIDATION: 08:19:08     32  TABLE
     VALIDATION: 08:19:08     32  TRIGGER
     VALIDATION: 08:19:08     20  TYPE
     VALIDATION: 08:19:08      6  TYPE BODY
     VALIDATION: 08:19:08     36  VIEW
     VALIDATION: 08:19:08 Validation completed.
     INFO: 08:19:08 Completed validation for Oracle REST Data Services.
     PL/SQL-Prozedur erfolgreich abgeschlossen.
     Commit abgeschlossen.
     ------------------------------------------------------------
     Containername: APEX222
     Skripts für scheduler werden ausgeführt
     ------------------------------------------------------------
     …
     PL/SQL-Prozedur erfolgreich abgeschlossen.
     Commit abgeschlossen.
     2023-03-15T07:19:15.030Z INFO        Installation für Oracle REST Data Services Version 22.4.4.r0411526 wurde abgeschlossen. Verstrichene Zeit: 00:00:27.496
     [*** Informationen: Installation für Oracle REST Data Services Version 22.4.4.r0411526 wurde abgeschlossen. Verstrichene Zeit: 00:00:27.496
     ]
     ------------------------------------------------------------
     Containername: APEX222
     ------------------------------------------------------------
     [*** script: ords_configure_gateway.sql]
     Configured PL/SQL Gateway user APEX_PUBLIC_USER to be proxiable from
     ORDS_PUBLIC_USER
     PL/SQL-Prozedur erfolgreich abgeschlossen.
     2023-03-15T07:19:15.130Z INFO        Konfiguration von PL/SQL-Gatewaybenutzer für Oracle REST Data Services Version 22.4.4.r0411526 wurde abgeschlossen. Verstrichene Zeit: 00:00:00.95
     [*** Informationen: Konfiguration von PL/SQL-Gatewaybenutzer für Oracle REST Data Services Version 22.4.4.r0411526 wurde abgeschlossen. Verstrichene Zeit: 00:00:00.95
     ]

3. Silent Installation (Unix)

export DB_PORT=1521
    export DB_SERVICE=apex231
    export SYSDBA_USER=SYS
    export SYSDBA_PASSWORD=sys
    export ORDS_PASSWORD=ords
    export ORDS_HOME=/opt/oracle/ords
    export ORDS_CONFIG=/opt/oracle/ords
    export ORDS_LOGS=$ORDS_CONFIG/logs
        export PATH=$PATH:$ORDS_HOME/bin
ords --config $ORDS_CONFIG install \
    --log-folder $ORDS_LOGS \
    --admin-user $SYSDBA_USER \
    --db-hostname 127.0.0.1 \
    --db-port $DB_PORT \
    --feature-db-api true \ 
    --feature-rest-enabled-sql true \
    --feature-sdw true \
    --gateway-mode proxied \
    --gateway-user APEX_PUBLIC_USER \
    --proxy-user 

Zugriff dann im Browser via: http:server:8080/ords

4. oder neue Datenbank bzw. Alias zusätzlich in ORDS (Unix) eintragen:

ords --config $ORDS_CONFIG install \
    --db-pool $DB_SERVICE \
    --admin-user $SYSDBA_USER \
    --db-hostname 127.0.0.1 \
    --db-port $DB_PORT \
    --db-servicename $DB_SERVICE \
    --feature-db-api true \ 
    --feature-rest-enabled-sql true \
    --feature-sdw true \
    --gateway-mode proxied \
    --gateway-user APEX_PUBLIC_USER \
    --proxy-user 
     Zugriff dann via: http:server:8080/ords/$DB_SERVICE

5. Silent Installation unter Windows:

set DB_PORT=1521
    set DB_SERVICE=apex231
    set SYSDBA_USER=SYS
    set SYSDBA_PASSWORD=sys
    set ORDS_PASSWORD=ords
    set ORDS_HOME=c:\oracle\ords
    set ORDS_CONFIG=c:\oracle\ords
    set ORDS_LOGS=%ORDS_CONFIG%\logs
        set PATH=%PATH%;%ORDS_HOME%\bin
ords --config %ORDS_CONFIG% install ^ 
    --log-folder %ORDS_LOGS% ^
    --admin-user %SYSDBA_USER% ^
    --db-hostname 127.0.0.1 ^
    --db-port %DB_PORT% ^
    --db-servicename %DB_SERVICE% ^
    --feature-db-api true ^
    --feature-rest-enabled-sql true ^
    --feature-sdw true ^
    --gateway-mode proxied ^
    --gateway-user APEX_PUBLIC_USER ^
    --proxy-user 
    

6. Zusätzliche Datenbank oder Alias in ORDS eintragen (Windows)

ords --config %ORDS_CONFIG% install ^ 
    --log-folder %ORDS_LOGS% ^
    --db-pool %DB_SERVICE% ^
    --admin-user %SYSDBA_USER% ^
    --db-hostname 127.0.0.1 ^
    --db-port %DB_PORT% ^
    --db-servicename %DB_SERVICE% ^
    --feature-db-api true ^
    --feature-rest-enabled-sql true ^
    --feature-sdw true ^
    --gateway-mode proxied ^
    --gateway-user APEX_PUBLIC_USER ^
        --proxy-user 
    Zugriff dann im Browser via: http:server:80/ords/%DB_SERVICE%
    Hinweis: der Tomcat Dienst benötigt Leserechte im ORDS Config Verzeichnis !!!
    icacls %ORDS_CONFIG% /T /grant Benutzer:R

7. Prüfung im OS

Wir oben beschrieben muss die richtige Java Version installiert worden sein (Oracle Java 11 oder 17)         
Kann der config Ordner von Oracle/Tomcat Benutzer gelesen werden? Wenn nicht bitte Lese/Schreibrechte an die Dateien vergeben.

 

8. Prüfung in der Datenbank

Sind alle notwendigen Accounts NICHT gesperrt und stimmt das Passwort ?

select username,account_status,lock_date,expiry_date
    from dba_users
    where username like '%PUBLIC%';
    USERNAME                 ACCOUNT_STATUS     LOCK_DATE           EXPIRY_DATE        
    ------------------------ ------------------ ------------------- -------------------
    APEX_PUBLIC_USER         LOCKED             15.03.2023 07:57:59                    
    APEX_REST_PUBLIC_USER    OPEN                                   11.09.2024 08:08:32

Das schaut hier nicht gut aus, der Benutzer APEX_PUBLIC_USER ist gesperrt!.

Entsperren mittels:

ALTER USER apex_public_user ACCOUNT UNLOCK IDENTIFIED BY <mein_geheimes_passwort>;

Danach starten wir dem TomCat nochmal durch.

Windows:

net stop TomCat9
net start TomCat9

Für den Fall, dass Sie das Passwort vom ORDS_PUBLIC_USER in der DB geändert haben und das im ORDS nachtragen möchten:

Für den Default Pool: (wenn der Config Ordner unter /opt/oracle/ords liegt)

ords --config /opt/oracle/ords config --db-pool default secret db.password

Für einen anderen Pool (hier apex231)

ords --config /opt/oracle/ords config --db-pool apex231 secret db.password

 

Fehlermeldung: Benutzer oder Kennwort für den Verbindungspool namens |default|lo| ist ungültig oder abgelaufen, oder der Account wurde gesperrt 

Die naheliegenden Lösungen wären natürlich:

  • Passwort falsch
  • Account abgelaufen

aber in unserem Fall war die Datenbank (pluggable Database Container) im restricted Modus:

Die Lösung war:

alter pluggable database <containername> close immediate;
alter pluggable database <containername> open;

 

Fehlermeldung: Service Unavailable

ORDS_Fehler_503.jpeg

HTTP Status Code: 503

Request ID: bI7hBYBLvXJ1aZXhH61iMA

Request Timestamp: 2023-10-21T08:58:05.233483445Z

Das Datenbankkennwort-Secret fehlt in dem mit dem Pool |default|lo| verknüpften Wallet


Hier fehlt das Passwort für den ORDS_PUBLIC_USER, oder es ist veraltet oder abgelaufen. 
Die Lösung unter LINUX ist, das Wallet mit dem Passwort neu anzulegen mittels:

cd /opt/oracle/ords
REM dort liegt der Ordner bin aus der Installtion des ORDS!, wenn nicht, gehen Sie bitte in den entsprechenden Ordner
sudo bin/ords --config /opt/oracle/ords config secret db.password
REM zweimal das Passwort eingeben
REM wenn Sie noch weitere Pools besitzen, muss auch hier das Passwort geöndert werden (Pool-Name apex231)
sudo bin/ords --config /opt/oracle/ords config --db-pool apex231 secret db.password
REM TomCat durchstarten
sudo systemctl restart tomcat

Für Windows würde die Lösung so aussehen:

cd c:\oracle\ords
    bin\ords --config c:\oracle\ords config --db-pool default secret db.password
    bin\ords --config c:\oracle\ords config --db-pool apex241 secret db.password

Unsere Ordnerstruckur für den Ords sieht wie folgt aus:

ORDS_Ordner.png

Fehler 404 DispatcherNotFoundException

Hier war ein falscher Eintrag in der pool.xml Datei (unter Windows z.B. unter c:\oracle\ords\databases) schuld.

Der Parameter plsql.gateway.mode muss auf proxied stehen!

Für Windows:

set ORDS_HOME=c:\oracle\ords
set ORDS_CONFIG=c:\oracle\ords
%ORDS_HOME%\bin\ords --config %ORDS_CONFIG% config set plsql.gateway.mode proxied

Für Unix

export ORDS_HOME=/opt/oracle/ords
export ORDS_CONFIG=/opt/oracle/ords
$ORDS_HOME/bin/ords --config $ORDS_CONFIG config set plsql.gateway.mode proxied

Fehler 404 not found

Im Logfile des Apache TomCat steht nur:

"GET /ords/wwv_flow.js_messages?p_app_id=4550&p_lang=en&p_version=24.1.5-3518415 HTTP/1.1" 404 451412

Problem war eine falsche Einstellung in der Datei pool.xml (z.B. unter c:\oracle\ords\databases\default):

dort musste 

<entry key="plsql.gateway.mode">disabled</entry>

durch

<entry key="plsql.gateway.mode">proxied</entry>

ersetzt werden.

10. Bonustrack: Oracle Apex Fehler

Wir hatten kürzlich das Problem, das keine Applikationen mehr importiert werden konnten. Beim Auswählen der Datei kam im nächsten Schritt die Fehlermeldung, dass keine Datei ausgewählt wurde.

Das Problem war hier, dass beim Benutzer FLOWS_FILES einige Indizes defekt waren. Nach einem Rebuild war ein Import wieder möglich.

Der folgende Select schreibt Ihnen ein Skript, um defekte Indizes zu finden und reparieren.

select 'ALTER INDEX '||owner||'.'||index_name||' REBUILD;' as sql_stmt
from dba_indexes 
where (owner='FLOWS_FILES' 
or owner=(select schema from dba_registry where comp_id='APEX'))
and status='UNUSABLE';

Bei der Migration auf einen anderen Server hatten wir plätzlich beim start des Workspace den Fehler:

Die Anforderung kann nicht verarbeitet werden, da diese Ressource keine Cross Origin Sharing-Anforderungen 
unterstützt oder da der Anforderungsursprung nicht autorisiert ist, 
auf diese Ressource zuzugreifen. Wenn ords als Reverse-Proxy verwendet wird, stellen Sie sicher, 
dass der Frontend-Server den Hostnamen propagiert. 
Stellen Sie für mod_proxy sicher, dass "ProxyPreserveHost" eingeschaltet ist.

Die Lösung bestand darin in die die Datei /etc/httpd/conf/httpd.conf (bzw. eine Unterdatei z.B. in conf.d) um folgenden Eintrag zu ergänzen:

RequestHeader unset Origin

 

Mehr Tipps & Tricks erfahren Sie in unserem Oracle ORDS und im APEX II Kurs. Wir freuen uns auf Sie! 
 



Weitere Interessente Artikel zum Thema:


Empfohlene Schulungen zum Thema:


Funktion Return Boolean in SQL Problem lösen

Bereich:PL/SQL, Version: ab RDBMS 10.x, Letzte Überarbeitung: 26.07.2018

Keywords:SQL, Boolean, WITH Function

Haben Sie sich auch schon darüber geärgert, dass gerade Oracle keine Boolean Datentypen in SQL erlaubt?
Wenn Sie also eine PL/SQL Funktion besitzen, die nur TRUE oder FALSE zurückgibt, muss man sich einen Wrapper in PL/SQL schreiben.

Aber in unserem Tipp des Monats, bekommen wir das mit einem Trick auch nur in SQL hin.

Sagen wir mal, wir hätten eine Funktion wie diese hier:

CREATE OR REPLACE FUNCTION my_bool
RETURN BOOLEAN IS
BEGIN
IF EXTRACT(HOUR FROM cast (systimestamp as timestamp))<=12 RETURN true;
ELSE RETURN false;
END IF;
END;

Dann könnten wir mit einer einfachen WITH Klausel das auch in reinem SQL hinbekommen.

WITH
  FUNCTION return_boolean RETURN VARCHAR2 IS
  BEGIN
   IF my_bool THEN
    RETURN 'True';
   ELSE
    RETURN 'False';
  END IF;
  END;
SELECT return_boolean FROM dual;

 

Viele weitere Tipps & Tricks bekommen Sie in einem unserer bewährten PL/SQL Kurse (PL/SQL, PL/SQL II, PL/SQL Packages, ...)



Weitere Interessente Artikel zum Thema:



Empfohlene Schulungen zum Thema:


DBMS_OUTPUT umgeleitet

Bereich:PL/SQL, Version: ab RDBMS 10.x, Letzte Überarbeitung: 23.07.2018

Keywords:dbms_output, dbms_pipe, htp.p

Ich liebe das Package dbms_output, wenn es nur nicht ...

  • so einen langen Namen
  • so eine eingeschänkte Bedienung

hätte.

Aber den letzen Punkt können wir ändern. In diesem Tipp holen wir die Daten, die wir in den Puffer von dbms_output geschrieben haben und legen Sie woanders hin.

##################################################
Umleitung von dbms_output zu pipe
##################################################

CREATE OR REPLACE PROCEDURE test_ausgabe
-- Rechte: grant execute on dbms_pipe to scott;
IS
    PROCEDURE dop2pipe (pipe_name IN VARCHAR2 DEFAULT 'DBMS_OUTPUT_PIPE') IS
       lines dbms_output.chararr;
       num_lines number:=1000000;
    BEGIN
       dbms_output.get_lines(lines, num_lines);
       FOR i IN 1..num_lines LOOP
            dbms_pipe.pack_message (lines(i));
       END LOOP;
        IF (dbms_pipe.send_message (pipe_name)) <> 0 THEN
            raise_application_error(-20500,'Fehler beim Senden in Pipe '||pipe_name||' aufgetreten !');
        END IF;
    END;
BEGIN
   dbms_output.enable(null);  
   FOR i IN 1 .. 10 LOOP
     dbms_output.put_line('Zeile='||i);
   END LOOP;
dop2pipe;   -- <=######## Umwandelung von dbms_output in Pipe
END;
/

CREATE OR REPLACE PROCEDURE get_dout_from_pipe (pipe_name IN VARCHAR2 DEFAULT 'DBMS_OUTPUT_PIPE')

IS

     v_message VARCHAR2(32767);

     v_timeout NUMBER:=120;

BEGIN

     IF (DBMS_PIPE.receive_message(pipe_name,v_timeout)) <> 0 THEN

          RAISE_APPLICATION_ERROR(-20501,'Fehler beim Lesen aus Pipe '||pipe_name||' aufgetreten !');

     END IF;

     LOOP

        EXIT WHEN DBMS_PIPE.NEXT_ITEM_TYPE = 0;

         DBMS_PIPE.unpack_message(v_message);

         DBMS_OUTPUT.PUT_LINE(v_message);

     END LOOP;

END;

/

##################################################
Umleitung von dbms_output zu htp
##################################################


CREATE OR REPLACE PROCEDURE test_ausgabe
IS
    PROCEDURE dop2htp IS
       lines dbms_output.chararr;
       num_lines number:=1000000;
    BEGIN
       dbms_output.get_lines(lines, num_lines);
       FOR i IN 1..num_lines LOOP
          htp.p(lines(i)||'<BR>');
       END LOOP;
    END;
BEGIN
   dbms_output.enable(null);  
   FOR i IN 1 .. 10 LOOP
     dbms_output.put_line('Zeile='||i);
   END LOOP;
dop2htp;   -- <=######## Umwandelung von dbms_output in htp.p Ausgabe
END;

 

Ich hoffe dies konnte Ihnen weiterhelfen, bei sonstigen Fragen melden sie sich gerne bei uns und oder besuchen sie einen der Kurse. :-)



Weitere Interessente Artikel zum Thema:



Empfohlene Schulungen zum Thema:


Best Practices für das Datenbank-Audit in Oracle 11g und 12c

Bereich:DBA, Version: ab RDBMS 11.x, Letzte Überarbeitung: 19.05.2022

Keywords:Vorträge, DBA

Firmen, die mit personenbezogenen Daten arbeiten, sind gesetzlich zum Audit verpflichtet. In der Praxis sieht das allerdings häufig so aus, dass man im Vertrauen darauf, dass die Default-Audit Einstellungen von Oracle schon alles Wesentliche abdecken werden, die Inhalte der Audit-Trails eine bestimmte Zeit aufhebt und dann löscht oder irgendwohin exportiert, ohne jemals einen Blick auf den Inhalt geworfen zu haben.

Und wenn ein entnervter DBA unter Oracle 11g kurz nachsehen will, welcher xxxxx an den Datenbankparametern gedreht hat und es wieder mal keiner gewesen sein will, wird er im Audit-Trail der Datenbank vergeblich danach suchen, selbst wenn er den Parameter AUDIT_SYS_OPERATIONS auf TRUE gesetzt hat. Unter Linux müsste er dazu unzählige aud-Files im Betriebssystem und unter Windows das Eventlog durchforsten.

Um wirklich das auditiert zu bekommen, was man gerne hätte und die Auswertung nicht allzu zeitraubend zu gestalten, muss man die Audit-Einstellungen und das Housekeeping der Daten manuell einrichten und testen. Auch die Übertragung eines fertigen 11g-Audit-Konzepts auf Oracle 12c ist eine Menge Arbeit. Dieser Vortrag beschäftigt sich anhand eines fiktiven, aber nicht untypischen Szenarios mit den Schwachstellen der Default-Audit-Konfiguration in Version 11g und 12c und stellt Lösungsansätze für das Audit vor, mit denen man die Erfassung der Daten und die Auswertung vereinfachen kann.

AUSGANGSLAGE FÜR DAS AUDIT-SZENARIO
Der neue, motivierte und experimentierfreudige DBA einer mittelständischen Firma soll ein neues Audit-Konzept umsetzen. Im Einsatz sind Oracle-Datenbanken der Version 11.2.0.4 (Standard Edition One) auf Linux und auf Windows. Alle Angestellten der Firma loggen sich unter dem selben Account , aber von unterschiedlichen Rechnern aus ein und können entweder direkt (über SQL Developer) oder über ein APEX-Interface auf die Tabellen zugreifen. Der DBA-Kollege nutzt manchmal auch den Enterprise Manager. Die DBAs arbeiten als SYSDBA auf der Instanz, alle anderen haben die Rollen CONNECT und RESOURCE.

Auditiert werden sollen:

  • Änderungen (DML) an der Tabelle Kundendaten im Schema KUNDEN
  • DROP oder TRUNCATE-Vorgänge an Tabellen im Schema KUNDEN
  • Änderungen am PL/SQL-Code im Schema KUNDEN
  • fehlgeschlagene Login-Versuche
  • Für einen begrenzten Zeitraum alle Aktionen des neuen Werkstudenten, der eine neue Applikationin einem eigenen Schema (mit vollem Zugriff auf das Kundenschema) entwickeln soll.
  • Alle Aktionen des externen Consultants, der sich ebenfalls als SYSDBA auf der Instanz einloggt.
  • Stop, Starts, Änderungen an Parametern und Datenbankdateien
  • Rechte-Vergabe bzw. Entzug 

Weitere Forderungen

  • Bis jetzt wurde die Tabelle aud$ einmal pro Woche über einen DBMS_JOB-Aufruf geleert. In Zukunft sollen die Daten 120 Tage aufbewahrt und "zeitnah" ausgewertet werden.
  • Alle Audit-Einträge sollen über eine zentrale View selektierbar sein, um die Auswertung zu erleichtern.
  • Das Audit soll mit möglichst wenig Aufwand (bzw. Kosten) einzurichten sein.
  • Es soll sowohl für Windows- als auch für Linux-Datenbanken umsetzbar sein und möglichst keine zusätzlichen Shell- oder Batch-Skripte für das Housekeeping etc. erfordern.

PROBLEME BEI EINRICHTUNG, AUSWERTUNG UND HOUSEKEEPING DES AUDITS UNTER 11G

Gewöhnungsbedürftige Syntax für das Einrichten der Audit-Optionen
Von den oben geforderten Audits wird nur ein kleiner Teil über die Default-Einstellungen abgedeckt. Es ist also viel Handarbeit nötig, wobei die Syntax der AUDIT-Anweisung einige Fallstricke enthält.
So kann z.B. ein unvorsichtiges SELECT TABLE BY <username> den Audit-Trail exponentiell aufblasen, wenn man ein Tool wie den SQL Developer benutzt.

Überflüssige Informationen im Audit-Trail
Wenn man den Enterprise Manager konfiguriert hat, der sich im Sekundentakt bei der Datenbank anmeldet, führen die Default-Einstellungen dazu, dass der Audit-Trail größtenteils mit den Logon- und Logoff-Einträgen der User SYSMAN und DBNSMP gefüllt wird. Beim SYS-Auditing werden unter 11g alle Statements aufgezeichnet. Hier muss man den Overhead der Data-Dictionary-Zugriffe (recursive SQL) beim Auswerten herausfiltern. Auch der RMAN erzeugt eine Flut von Einträgen, wenn er sich als SYSDBA anmeldet.


Unterschiedliche Speicherorte der verschiedenen Audit-Informationen
Connects als SYSDBA bzw. SYSOPER, Stops und Starts der Datenbank werden per default auditiert (mandatory auditing). Alle weiteren Aktionen des Users SYS können durch Umstellung des Parameters AUDIT_SYS_OPERATIONS auf TRUE auditiert werden. Unter Unix werden die Informationen jedoch in das durch den Parameter audit_file_dest angegebene Verzeichnis geschrieben, unter Windows ins Eventlog. Um dieses Audit auswerten zu können, müsste man die Inhalte der *.aud-Files unter Linux bzw. die Oracle-spezifischen Inhalte des Eventlogs unter Windows parsen und in die Datenbank laden. Beides ist möglich, aber aufwendig.


Unzulänglichkeiten des Packages DBMS_AUDIT_MGMT
Das Package DBMS_AUDIT_MGMT ist umständlich zu bedienen, vor allem, wenn man sowohl in der Datenbank als auch im Filesystem aufräumen will. Um die Einträge im Windows Event Log muss man sich selber kümmern.


VORSCHLAG FÜR EIN "UNIFIED AUDITING" ab 11G
Wenn man den Parameter AUDIT_TRAIL auf XML setzt, werden alle zu auditierenden Statements des Standard, Mandatory, SYS- und Fine Grained Auditing (letzteres natürlich nur in der Enterprise Edition) im AUDIT-FILE-DEST-Verzeichnis als XML-Files gespeichert und können über die View V$XML_AUDIT_TRAIL gemeinsam ausgewertet werden, ähnlich wie die XML-Files des Alert.logs über die View V$DIAG_ALERT_EXT. Die seit der Oracle-Version 10.2 verfügbare View DBA_COMMON_AUDIT_TRAIL vereint diese Einträge mit denen aus DBA_AUDIT_TRAIL und DBA_FGA_AUDIT_TRAIL, so dass auch die Inhalte der Tabellen AUD$ und FGA_LOG$ noch sichtbar bleiben. Diese Lösung kann man durchaus als abgespecktes "Unified Auditing" für 11g bezeichnen. Die Auswertung und das Housekeeping werden dadurch wesentlich erleichtert. Auch die Performance des Audits leidet nicht darunter, dass man die Audit-Files im Betriebssystem speichert , eher im Gegenteil. Wenn man den XML-Audit-Trail vor Zugriffen durch übel gesinnte DBAs schützen will, muss man dafür sorgen, dass die Files in kurzen Abständen auf einen anderen Server außer Reichweite gesichert werden.

Im Vortrag wird gezeigt, wie man die oben genannten Anforderungen an das Audit mit dieser Methode, ein paar Views für die bequeme Auswertung und einem datenbankgesteuerten Housekeeping umsetzen kann.

UND WIE SIEHT'S UNTER 12C AUS ?
Unsere mittelständische Firma wächst und gedeiht und denkt an einen Upgrade auf die Version 12c.

Das unter 12c neu eingeführte Unified Auditing soll schneller, leichter einzurichten und zu verwalten und für alle Editions verfügbar sein.
Im Prinzip ja, aber ...

Die Standard Edition One (SEO) gibt es nur noch in der Version 12.1.0.1. Hier ist das Feature wegen des Bugs 17466854 (CANNOT SET UNIFIED AUDITING IN STANDARD EDITION) nicht zu aktivieren. Dafür gibt es einen Patch 17466854, den man aber nicht einspielen kann, wenn man seine Datenbank mit dem aktuellen Patchset versorgt hat. Der Bugfix wurde für den Patchset 12.1.0.1.5 (vom 14.10.2014) geschrieben und funktioniert nicht für höhere Levels. Für SEO-Datenbanken unter Windows soll der Bugfix im Patchset 12.1.0.1.18 eingeschlossen sein.

Da die SEO allerdings sowieso ein Auslaufmodell ist, steigt unsere Firma doch lieber auf die Standard Edition 2 (SE2) um. Bei der Version 12.1.0.2 funktioniert der Umstieg auf Unified Auditing problemlos. Es macht sich jedoch deutlich bemerkbar, dass bei jedem Patch diverse Bugs gefixt werden. Man sollte also unbedingt die neuesten Patches einspielen !

Nach der Migration auf die Version 12c ist zunächst der sogenannte Mixed Mode aktiv, unter dem nur die Policies ORA_SECURECONFIG und zusätzlich ORA_LOGON_FAILURES (erst ab 12.1.0.2) aktiviert sind. Diesen beizubehalten, ist keine wirklich gute Option, vor allem hinsichtlich des Housekeepings und der Sicherheit der Audit-Daten gegenüber Manipulation durch den DBA. Zudem gibt es einige wirklich nützliche zusätzliche Features beim neuen Unified Auditing, die man sich nicht entgehen lassen sollte. Dazu zählt vor allem die größere Sicherheit der Audit-Daten.

WAS IST BEIM UMSTIEG AUF UNIFIED AUDITING UNTER ANDEREM ZU BEACHTEN ?


Das sys-Audit ist nicht mehr das, was es einmal war
Änderungen am System, an den Audit-Einstellungen etc. werden im Unified Auditing immer auditiert, egal, ob man sich als SYSTEM oder SYS AS SYSDBA, SYSOPER, SYSDG, SYSBACKUP bzw. SYSKM anmeldet.
Ansonsten wird der User SYS aber genauso behandelt wie Hinz und Kunz !!! Auch seine Selects erscheinen nicht mehr im Audit-Trail. Wenn man z.B. mitbekommen will, ob er sich per DML an irgendwelchen User-Tabellen vergreift, muss man dieses Audit explizit in einer Policy einrichten! Der Parameter AUDIT_SYS_OPERATIONS hat keinen Einfluss mehr.


Per Default werden nur failed logins auditiert
Dass nicht mehr jeder LOGON und LOGOFF registriert wird, macht den Audit-Trail zwar übersichtlicher, aber für Auswertungen des Audit-Trails hinsichtlich der Dauer der Sessions oder der Anzahl nicht korrekt beendeter Session (LOGOFF BY CLEANUP) muss man sich nun eigene Policies schreiben. 


Die alten Audit-Optionen sind nicht 1:1 in den neuen Policies abzubilden
Ein AUDIT PROCEDURE BY consultant;
unter 11g wird unter 12c zu
CREATE AUDIT POLICY consultant_plsql_pol
ACTIONS CREATE FUNCTION,
 DROP FUNCTION,
 CREATE LIBRARY,
 DROP LIBRARY,
 CREATE PACKAGE,
 DROP PACKAGE,
 CREATE PACKAGE BODY,
 CREATE PROCEDURE,
 DROP PROCEDURE;
AUDIT POLICY consultant_plsql_pol BY consultant;


Das ist eigentlich ein Vorteil, weil diese Syntax besser verständlich ist, aber die Policies können schnell unübersichtlich werden. Die Anforderungen an das Audit von Seite 1 lassen sich über die beiden Default-Policies und 3 zusätzliche maßgeschneiderte Policies verwirklichen. Zuviel Policies wirken sich eher schädlich auf die Performance aus.

Das neue Audit ist per Default "extended"
Während man unter der Version 11g den Parameter audit_trail auf DB, extended oder XML, extended setzen musste, um auch die SQL-Statement und Bindvariablen zu erfassen, ist das in Version 12c per Default der Fall. Das lässt den Audit Trail entsprechend schnell anwachsen, wenn die Statements etwas komplexer werden. Auch das obligatorische RMAN-Audit braucht einiges an Platz. Deshalb ist ein durchdachtes Housekeeping fast noch wichtiger als unter 11g.


Wie der (immer noch motivierte) DBA den Umstieg von der selbstgestrickten Unified Auditing Lösung unter 11g auf die Audit-Konfiguration unter 12c meistert und was ein vernünftig konfiguriertes Audit alles an Verbrechen gegen die Datenbank im allgemeinen und die guten Sitten im besonderen zu Tage fördern kann, erfahren Sie im Vortrag.



Weitere Interessente Artikel zum Thema:


Empfohlene Schulungen zum Thema:


Das PL/SQL-Berechtigungskonzept in 12c

Bereich:PL/SQL, Version: ab RDBMS 12.x, Letzte Überarbeitung: 11.01.2021

Keywords:Oracle Neuerungen, PL/SQL, 12C Release 1

Oracle hat das Berechtigungskonzept zu PL/SQL in Hinblick auf zwei gegensätzliche Szenarien in 12c ausgebaut:

  • Szenario 1: Der Ausführende hat mehr Rechte als der Programmierer.
    Die Änderung zu diesem Szenario (Privileg "INHERIT [ANY] PRIVILEGES") betrifft ausschließlich Prozeduren, die mit Invoker Rights arbeiten. 
  • Szenario 2: Der Ausführende hat weniger Rechte als der Programmierer.

Die Änderung zu diesem Szenario (Vergabe von Rollen) betrifft Prozeduren, die mit Invoker Rights arbeiten oder mit dynamischem SQL.

Beiden Neuerungen gemeinsam ist, dass es um die Überprüfung von Rechten zur Laufzeit geht. Auch weiterhin benötigt ein Entwickler alle entsprechenden Berechtigungen selber, um eine Prozedur erfolgreich kompilieren zu können. Dynamisches SQL allerdings wird zur Kompilierzeit nicht ausgewertet.

Hinweis: Hier und im weiteren ist "Prozedur" als Sammelbegriff zu verstehen, der auch Funktionen und Packages mit einschließt. 

INHERIT [ANY] PRIVILEGES 

Hier kommt Szenario 1 ins Spiel:

Angenommen, der User ADMIN hat weitreichende Rechte.

Weiter angenommen, der Programmierer SCOTT hat vergleichsweise wenig Rechte.

Nun schreibt SCOTT eine Prozedur mit Invoker Rights (bei Definer Rights spielt das neue Privileg keine Rolle), die er ADMIN zur Verfügung stellt. Solange es sich um statisches SQL handelt, kann nicht viel passieren, da ja SCOTT's Berechtigungen zur Compile-Zeit überprüft werden.

Nun könnte aber SCOTT bösartigerweise Befehle in dynamisches SQL verpacken, die weit über seine eigenen Berechtigungen hinausgehen (z. B. an sich selbst Admin-Rechte vergeben), nicht jedoch über diejenigen von ADMIN. SCOTT kann so eine Prozedur problemlos erstellen - aber nicht ausführen. ADMIN dagegen kann sie ausführen.

Um einen solchen Missbrauch ggf. unterbinden zu können, wurde mit Version 12c die Berechtigung INHERIT [ANY] PRIVILEGES eingeführt. Das Konzept dabei sieht folgendermaßen aus:

Der Eigentümer der Prozedur (in obigem Szenario SCOTT) braucht, sofern er nicht über das Systemprivileg INHERIT ANY PRIVILEGES  verfügt, von dem Benutzer, der die Prozedur später ausführen soll - also hier ADMIN - , das Objektprivileg INHERIT PRIVILEGES. Hat er das nicht, so kann ADMIN später die Prozedur nicht ausführen.

Damit sich das Verhalten zwischen 11g und 12c nicht ändert, erhält PUBLIC automatisch für jeden neu angelegten User dieses Recht. Das können Sie leicht nachprüfen über dba_tab_privs:

SELECT *
  FROM dba_tab_privs
 WHERE grantee = 'PUBLIC' 
   AND privilege = 'INHERIT PRIVILEGES';


Oracle empfiehlt jedoch PUBLIC diese Rechte zu entziehen.
 

Für das oben beschriebene Szenario bedeutet dies:

Hat PUBLIC das Recht INHERIT PRIVILEGES ON USER ADMIN, wie das dem Default entspricht, so wird ADMIN die Prozedur von SCOTT ausführen können, wie auch schon in 11g. Entzieht ADMIN dieses Recht mit

REVOKE INHERIT PRIVILEGES ON USER ADMIN FROM PUBLIC;

und vergibt das Recht auch nicht explizit an SCOTT, dann kann er keine Prozedur von SCOTT mehr ausführen, die mit INVOKER RIGHTS angelegt wurde. Beim Versuch erhält er die Fehlermeldung

ORA-06598: Nicht ausreichende INHERIT PRIVILEGES-Berechtigung

ROLLEN AN PL/SQL-OBJEKTE VERGEBEN

Szenario 2 sieht so aus:

User SCOTT stellt wiederum Invoker Rights-Prozeduren zur Verfügung, in diesem Fall für den User LEHRLING. Eine der Prozeduren greift auf ein Tabelle im Schema SCOTT zu. SCOTT will aber nicht, dass LEHRLING direkten Zugriff auf diese Tabelle bekommt. Den bräuchte LEHRLING aber bis einschließlich Version 11g, um diese Prozedur ausführen zu können.

SCOTT erstellt z. B. folgende Funktion:

CREATE OR REPLACE FUNCTION deptno_exists (p_deptno IN SCOTT.DEPT.deptno%TYPE)
   RETURN BOOLEAN
   AUTHID CURRENT_USER
IS
   v_count   NUMBER;
   v_ret     BOOLEAN;
BEGIN
   SELECT COUNT (*)
     INTO v_count
     FROM scott.dept
    WHERE deptno = p_deptno;
   IF v_count > 0
   THEN
      v_ret  := TRUE;
   ELSE
      v_ret  := FALSE;
   END IF;
   RETURN v_ret;
END deptno_exists;
/
GRANT EXECUTE ON deptno_exists TO LEHRLING
/

LEHRLING wird diese Funktion bis jetzt nicht erfolgreich ausführen können:

ORA-00942: Tabelle oder View nicht vorhanden
ORA-06512: in "SCOTT.DEPTNO_EXISTS", Zeile 8

In Version 12c stellt nun ein DB-Administrator SCOTT eine Rolle zur Verfügung:

CREATE ROLE select_dept;
GRANT select_dept TO SCOTT WITH ADMIN OPTION;

SCOTT vergibt das benötigte Select-Recht and die Rolle:

GRANT SELECT ON dept TO select_dept;

Und die Rolle an die Funktion(!):

GRANT select_dept TO FUNCTION deptno_exists;

Nun kann LEHRLING die Funktion erfolgreich ausführen ohne direkten Zugriff auf SCOTT.dept zu erhalten.

An dem Grundprinzip, dass Rollen in PL/SQL unwirksam sind, ändert sich nichts: Eine Prozedur kann auch weiterhin nur dann erfolgreich kompiliert werden, wenn ihr Eigentümer alle dafür erforderlichen Rechte DIREKT gegrantet bekommen hat.

Ein Beispiel zur Anwendung dieses Rollenkonzepts bei Definer Rights und dynamischem SQL hat Tom Kyte in seinem Blog beschrieben.



Weitere Interessente Artikel zum Thema:


Empfohlene Schulungen zum Thema:


Die wahre Größe einer Tabelle mit LOB Spalten

Bereich:DBA:Monitoring, Version: ab RDBMS 10.x, Letzte Überarbeitung: 06.07.2018

Keywords:Lobs, Tabellengröße, DBA_SEGMENTS

Letztens bin ich über ein vermeintlich trivales Problem gestolpert: Ich wollte die Größe einer Tabelle berechnen. Nur war die vermeintlich nur sehr klein. Wo lag das Problem?

Wenn man in USER_SEGMENTS oder DBA_SEGMENTS nachschaut, steht dort beim Tabellen (Segment) Namen nur die Größe der Basistabelle (ohne Lob Spalten)

Als Eigentümer des Objekts kann man folgenden Befehl absetzen:
Hinweis: Ändern Sie bitte in der ersten Zeile den Namen der Tabelle ab.

WITH t as (select 'TEST_TAB' as table_name FROM dual)
select 'Tab: '||t.table_name as info,to_char(round(bytes/1024/1024,2),'999,999,9990.99')||' MB' as SIZE_MB
from user_segments s, t
where s.segment_name=t.table_name
UNION ALL
select 'LOB: '||column_name,to_char(round(u.bytes/1024/1024,2),'999,999,9990.99')||' MB'
from user_lobs l, user_segments u, t
where l.segment_name=u.segment_name
AND l.table_name=t.table_name
UNION ALL
select 'Zusammen: ',to_char(round(sum(u.bytes/1024/1024),2),'999,999,9990.99')||' MB'
from user_segments u, t
where u.segment_name IN (select segment_name FROM t,user_lobs ul WHERE ul.table_name=t.table_name)
OR u.segment_name=t.table_name
group by 'Zusammen: ';

Das Ergebnis könnte dann z.B so aussehen:

INFOSIZE_MB
-------------------------------------------------
Tab: EMP_LOB0.06 MB
LOB: BILD4.19 MB
Zusammen:4.25 MB

Wenn man als DBA das Ganze ansehen möchte:

WITH t as (select 'SCOTT' as owner, 'EMP_LOB' as table_name FROM dual)
select 'Tab: '||t.table_name as info,to_char(round(bytes/1024/1024,2),'999,999,9990.99')||' MB' as SIZE_MB
from dba_segments s, t
where s.segment_name=t.table_name AND s.owner=t.owner
UNION ALL
select 'LOB: '||column_name,to_char(round(u.bytes/1024/1024,2),'999,999,9990.99')||' MB'
from dba_lobs l, dba_segments u, t
where l.segment_name=u.segment_name
AND l.table_name=t.table_name AND l.owner=t.owner
UNION ALL
select 'Zusammen: ',to_char(round(sum(u.bytes/1024/1024),2),'999,999,9990.99')||' MB'
from dba_segments u, t
where u.segment_name IN (select segment_name FROM t,dba_lobs ul WHERE ul.table_name=t.table_name AND ul.owner=t.owner)
OR u.segment_name=t.table_name AND u.owner=t.owner
group by 'Zusammen: ';


Weitere Interessente Artikel zum Thema:


Empfohlene Schulungen zum Thema:


Informationssystem für APEX-Applikationen

Bereich:APEX, Version: ab APEX 5.x, Letzte Überarbeitung: 10.06.2024

Keywords:APEX

Hatten Sie auch schon einmal den Wunsch, den Anwendern einer Applikation etwas mitzuteilen? Und wäre es nicht manchmal gut, wenn jeder Anwender nachweislich die Information gelesen hat? Die Informationen sollen sofort, also auch in einer laufenden Session, in einem modalen Popup- Fenster angezeigt werden. Dieses Problem stellte sich vor einiger Zeit in einem APEX-Projekt und aus diesem Grund haben wir ein Informationssystem für APEX-Anwendungen entwickelt.

Um die Anforderungen abzudecken brauchen wir im ersten Schritt zwei Tabellen. In der ersten werden die "Nachrichten" gespeichert, in der zweiten die Information, wer wann das Lesen bestätigt hat.

CREATE TABLE INFORMATIONEN
(
  ID          NUMBER,        -- Eindeutige ID, Trigger und Sequence
  APP_ID      NUMBER,        -- Applikations ID der Apexanwendung
  STATUS      CHAR(1),       -- A für aktiv oder I für inaktiv
  INFORMATION VARCHAR2(500), -- Freitext für die Informationsn
  CONSTRAINT PK_INFORMATIONEN PRIMARY KEY (ID)
);
CREATE TABLE INFORMATION_READ
(
  INFO_ID     NUMBER,        -- ID der Information die gelesen wurde  
  APP_USER    VARCHAR2(30),  -- Welcher User hat gelesen
  READ_DATE   DATE           -- Wann wurde gelesen, Trigger mit SYSDATE
);


Der nächste Schritt ist, eine Seite in der Applikation zu erstellen auf der die Informationen angezeigt werden sollen. Als Template wählen wir Popup. Im Bereich "Page HTML Body Attribute" kommt noch folgende Anweisung dazu:


Jetzt sollte man sich auch überlegen wie viele Informationen gleichzeitig angezeigt werden müssen. Entsprechend viele Items müssen erzeugt werden.
Wir benötigen je Info ein "Display only" Item (PX_INFOTEXT_X). Dabei sollte beachtet werden, dass die Eigenschaft "Escape special characters" auf NO steht, dadurch können HTML Formatierungen in den Text integriert werden. Zu jedem Infotext kommt noch eine Checkbox (return 1 when checked), damit können die Nachrichten einzeln bestätigt werden. Infotext und Checkbox werden nur angezeigt, wenn Infotext "NOT NULL" ist. Schließlich fehlt noch je ein Hidden Item zum Speichern der ID und ein Button zum Bestätigen der Seite.

Jetzt fehlen noch zwei Prozesse, der erste "On Load - Before Header" mit dem die Inhalte gefüllt werden:

declare
 v_cnt number := 1;
 v_trenner varchar2(30) := '<p><hr><p>';
begin
 for rec in (select id, information from informationen
               where app_id = :APP_ID and status = 'A'
               and id not in (select info_id
                from information_read where app_user = :APP_USER)
                order by id) loop
  if v_cnt = 1 then
   :PX_ID1 := rec.id;
   :PX_INFOTEXT_1 := rec.information;
  end if;
  if v_cnt = 2 then
   :PX_ID2 := rec.id;
   :PX_INFOTEXT_2 := rec.information;
  end if;
  v_cnt := v_cnt + 1;
 end loop;
end;


Dann kommt der Prozess zum Speichern der Informationen, wenn der Button gedrückt wurde:

begin
 if :PX_CHECK1 = 1 then
  insert into information_read values(:PX_ID1, :APP_USER, SYSDATE);
 end if;
 if :PX_CHECK2 = 1 then
  insert into information_read values(:PX_ID2, :APP_USER, SYSDATE);
 end if;
 :FXXX_HIDE := 'TRUE';
 :FXXX_INFO := '0';
end;


Hier tauchen erstmals die Applikations Items FXXX_HIDE und FXXX_INFO auf. Diese werden für die Steuerung des Infofensters benötigt. Bei Betätigung des Buttons wird auf jeden Fall das Item HIDE auf TRUE gesetzt, damit wird eine weitere Anzeige innerhalb der gleichen Session verhindert. Die Nachrichten können separat bestätigt werden, das heißt der Anwender kann entscheiden, ob sie in der nächsten Session wieder erscheinen sollen.

Nachdem es sich um ein modales Popup-Fenster handelt brauchen wir noch einen Branch, um es nach der Verarbeitung zu schließen. Der Branch Typ ist "Branch to PL/SQL Procedure".

begin
 htp.p('<body>');
 htp.p('<script type="text/java-script">');
 htp.p('parent.$("#modalDialog").dialog("close");');
 htp.p('</script>');
 htp.p('</body>');
end;


Kommen wir nun zur Steuerung. Bedingung war ja, dass es auch in einer laufenden Session erscheinen soll. Dafür müssen wir bei jedem Neuladen einer Seite nachsehen, ob es eine Nachricht gibt die der jeweilige User noch nicht gelesen hat. Weiterhin soll es die Möglichkeit geben, das modale Fenster zu schließen ohne die Nachrichten zu bestätigen. Dafür werden die Applikationsitems FXXX_HIDE und FXXX_INFO angelegt.
Dazu gehört auch ein Applikations Prozess mit dem Zündpunkt "On Load: Before Header".

begin
 select count(*)into :FXXX_INFO
             from informationen
             where app_id = :APP_ID
             and status = 'A'
             and id not in (select info_id from information_read where app_user = :APP_USER);
end;


Jetzt der zentrale Punkt: wann und wie zeige ich das modale Fenster an!

Dazu wird auf der Seite 0 eine "Dynamic Action" mit folgenden Einstellungen angelegt.

When:      Event = Page Load
           Condition = No Condition
Advanced:  Event Scope = once
Condition: Type = PL/SQL Expression
           Expression1 = NVL(:FXXX_HIDE,'F') != 'TRUE' and :FXXX_INFO > 0 and :APP_PAGE_ID not in (101,X)


Es müssen Loginseite und die Seite die als Popup erscheint ausgeschlossen werden.

TRUE ACTION: Execute Java-ScriptCode (In diesem Beispiel wird in der Applikation 100 die Seite 7 als modales Popup aufgerufen)
var ev = this.browserEvent;
ev.preventDefault;
var horizontalPadding = 20;
var verticalPadding = 20;
$('<iframe id="modalDialog" src="f?p=100:7:&APP_SESSION.::NO::" />').dialog(
       {title: "APEX System Info",
        autoOpen: true,
        width: 700,
        hight: 350,
        modal: true,
        close: function(event, ui) { $(this).remove();},
        overlay: {opacity: 0.5, background: "black"}
        }
  ).width(700 - horizontalPadding).height(350 - verticalPadding);
return false;

Der Anwender bekommt "neue Informationen" auch während einer laufenden Session sofort angezeigt und er muss aktiv werden und die Meldungen bestätigen, um sie dauerhaft auszublenden.

Achtung! Ab der APEX Version 4.1 sind aus Sicherheitsgründen Frames per default nicht erlaubt. Damit dieses Beispiel funktioniert muss in den Security Attributen "Embed in Frames" auf "Allow from same origin" umgestellt werden.

 

Weitere Informationen und Tipps zur Entwicklung mit APEX bekommen Sie auch in unseren Schulungen Entwicklung mit Application Express.



Weitere Interessente Artikel zum Thema:


Empfohlene Schulungen zum Thema:


Unsichtbare Spalten

Bereich:SQL, Version: ab RDBMS 12.2, Letzte Überarbeitung: 04.07.2018

Keywords:Oracle Neuerungen, SQL, 12C Release 1

Ab Version 12c können Spalten ausgeblendet werden, indem man sie als "invisible" deklariert. Das ist sowohl beim Anlegen einer Tabelle oder Spalte möglich als auch nachträglich.
Das bedeutet aber nicht, dass auf eine solche Spalte nicht zugegriffen werden kann, sondern nur, dass sie ohne Angabe einer expliziten Spaltenliste nicht berücksichtigt wird:

CREATE TABLE UNSICHTBAR
(
  A        NUMBER,
  B        NUMBER INVISIBLE,
  C        NUMBER
);

 

INSERT INTO UNSICHTBAR   
VALUES(1, 1);

 

INSERT INTO UNSICHTBAR   
(A, B, C )
VALUES(2, 2, 2);

 

SELECT * FROM UNSICHTBAR;
         A          C
---------- ----------
         1          1
         2          2

 

SELECT A, B, C FROM UNSICHTBAR;
         A          B          C
---------- ---------- ----------
         1                     1
         2          2          2

Solange man die Spalte explizit anspricht, kann man also ganz normal mit ihr arbeiten.

Interessant ist, dass TOAD die Spalte beim DESCRIBE mit angibt - und an welcher Position:

DESC UNSICHTBAR   -- in SQL*Plus
 Name                         Null?    Typ
 ---------------------------- -------- -----------
 A                                     NUMBER
 C                                     NUMBER
 
DESC UNSICHTBAR   -- im TOAD
 Name                         Null?    Type                       
 ---------------------------- -------- ------------
 A                                     NUMBER     
 C                                     NUMBER     
 B                                     NUMBER                    

Hier lohnt sich ein Blick ins Data Dictionary:

  SELECT column_name, column_id
    FROM user_tab_columns
   WHERE table_name = 'UNSICHTBAR'
ORDER BY column_id;
COL  COLUMN_ID
--- ----------
A            1
C            2
B            

Die column_id ist also bei unsichtbaren Spalten nicht gefüllt. Interessanter noch ist die etwas weniger bekannte user_tab_cols:

  SELECT column_name, column_id, internal_column_id,
         hidden_column, virtual_column
    FROM user_tab_cols
   WHERE table_name = 'UNSICHTBAR'
ORDER BY internal_column_id;
COL  COLUMN_ID INTERNAL_COLUMN_ID HIDDEN_COLUMN VIRTUAL_COLUMN
--- ---------- ------------------ ------------- --------------
A            1                  1 NO            NO           
B                               2 YES           NO           
C            2                  3 NO            NO           

Wenn eine unsichtbare Spalte wieder sichtbar gemacht wird, bekommt sie grundsätzlich die höchste column_id und rückt dementsprechend ans Ende der Spaltenliste:

ALTER TABLE UNSICHTBAR MODIFY (b VISIBLE);

 

SELECT * FROM UNSICHTBAR;
         A          C          B
---------- ---------- ----------
         1          1           
         2          2          2

 

  SELECT column_name, column_id,  internal_column_id,
         hidden_column, virtual_column
    FROM user_tab_cols
   WHERE table_name = 'UNSICHTBAR'
ORDER BY internal_column_id;
COL  COLUMN_ID INTERNAL_COLUMN_ID HIDDEN_COLUMN VIRTUAL_COLUMN
--- ---------- ------------------ ------------- --------------
A            1                  1 NO            NO           
B            3                  2 NO            NO           
C            2                  3 NO            NO                  

Auf diese Art könnte man beispielsweise auch eine neu eingefügte Spalte, die ja bis 11g grundsätzlich als letzte Spalte angezeigt wird, in der Spaltenliste weiter nach vorne wandern lassen:

ALTER TABLE UNSICHTBAR ADD (e NUMBER);
ALTER TABLE UNSICHTBAR MODIFY (b INVISIBLE, c INVISIBLE);
ALTER TABLE UNSICHTBAR MODIFY (b VISIBLE, c VISIBLE);

 

SELECT *  FROM UNSICHTBAR;
         A          E          B          C
---------- ---------- ---------- ----------
         1                                1
         2                     2          2

So ganz neu ist das Konzept der unsichtbaren Spalten nicht. Haben Sie sich schon einmal angeschaut, wie eine Spalte vom Typ XMLTYPE intern dargestellt wird?

ALTER TABLE UNSICHTBAR ADD (xml XMLTYPE);

 

  SELECT column_name, column_id
    FROM user_tab_columns
   WHERE table_name = 'UNSICHTBAR'
ORDER BY column_id;
COLUMN_NAME    COLUMN_ID
------------- ----------
A                      1
E                      2
B                      3
C                      4
XML                    5

 

  SELECT column_name, column_id, internal_column_id,
         hidden_column, virtual_column
    FROM user_tab_cols
   WHERE table_name = 'UNSICHTBAR'
ORDER BY internal_column_id;        
COLUMN_NAME    COLUMN_ID INTERNAL_COLUMN_ID HIDDEN_COLUMN VIRTUAL_COLUMN
------------- ---------- ------------------ ------------- --------------
A                      1                  1 NO            NO            
B                      3                  2 NO            NO            
C                      4                  3 NO            NO            
E                      2                  4 NO            NO            
XML                    5                  5 NO            YES           
SYS_NC00006$           5                  6 YES           NO                 

 

Hier kann die unsichtbare Spalte SYS_NC00006$ prinzipiell auch direkt angesprochen werden. Da es sich aber bei Speicherung des XMLTYPE als binary xml (dem Default in 12c) um einen BLOB handelt, ist das allerdings nicht sehr sinnvoll. Und bei Speicherung als CLOB (deprecated in 12c) bietet es zumindest keine Vorteile.

Das Prinzip ist also ähnlich, auch wenn es offensichtlich Unterschiede im Detail gibt. Interessierte sollten sich die Spalten column_id (hier gefüllt!), segment_column_id, virtual_column und - in Version 12c neu eingeführt - user_generated in user_tab_cols genauer anschauen.

Sie würden gerne wissen, was ist alles neu in 12c? - dann schauen Sie doch in unserer Oracle Schulung Neuerungen 12c vorbei.



Weitere Interessente Artikel zum Thema:



Empfohlene Schulungen zum Thema:


Tipps zur Statistikerstellung in der Datenbank

Bereich:DBA, Version: ab RDBMS 10.x:RDBMS 11.1:RDBMS 11.2:RDBMS 12.x:RDBMS 18.1:RDBMS 18.3:RDBMS 19.1:RDBMS 19.3:RDBMS 21.1, Letzte Überarbeitung: 12.01.2021

Keywords:DBA, Oracle Tuning, Statistiken

Die Erstellung von Statistiken über Datenbank-Objekte wie Tabellen oder Indizes ist seit der Einführung des kostenbasierenden Optimizers ein absolutes Muss. Die Qualität der Statistiken entscheidet darüber, ob ein SQL-Statement performant in der Datenbank verarbeitet werden kann oder nicht. Mit Hilfe der Statistiken bestimmt die Datenbank, welche Kosten im Sinne von Ressourcen-Nutzung entstehen und welcher Zugriffs-Pfad zu den geringsten Kosten führt.

Der vorliegende Monatstipp hat nicht das Ziel einer detaillierten Beschreibung des kostenbasierenden Optimizers. Dazu wird auf unsere Muniqsoft-Training-Schulungen und auf die einschlägige Literatur verwiesen.

Vielmehr werden folgende Fragestellungen näher betrachtet:

  • Wie sollen Statistiken erstellt werden?
  • Wann sollten Statistiken erstellt werden?
  • Welche Arten von Statistiken sollten erstellt werden?


WIE SOLLEN  STATISTIKEN ERSTELLT WERDEN ?
AUTOMATISCH

Seit Einführung der RDBMS-Version 10g existiert ein automatischer Statistik-Erstell-Job, der für alle Tabellen, die entweder keine oder veraltete (Stale) Statistiken aufweisen, Statistiken erstellt. Die verwendete Prozedur heißt „DBMS_STATS.GATHER_DATABASE_STATS_JOB_PROC“ und verwendet die gleichen Default-Parameter wie die „DBMS_STATS.GATHER_*_STATS“-Prozedur. Per Default werden die Statistiken aller Tabellen, deren Datensätze sich zu 10% geändert haben, als veraltet (stale) betrachtet und somit neu erstellt. Dieser Wert ist bis Oracle Version 10.2 fest und kann nicht geändert werden. Wenn die Notwendigkeit besteht, Statistiken für eine Tabelle öfters erstellen zu lassen, gibt es ab Version 11.1 die Prozedur „DBMS_STATS.SET_TABLE_PREFS“.

Z.B. kann die Tabelle „SCHULUNG“ des Users „MQS“ bereits bei 5% Änderungen folgendermaßen als veraltet deklariert werden und somit die Erstellung von Statistiken erzwungen werden:

SQL>
BEGIN
  DBMS_STATS.SET_TABLE_PREFS('MQS','SCHULUNG','STALE_PERCENT',5);
END;
/


MANUELL

Wenn das automatische Erstellen von Statistiken nicht gewünscht wird, weil z.B. eine andere Prozedur dies bereits erledigt, kann der Automatismus folgendermaßen nur auf Erstellung der Data Dictionary-Tabellen eingeschränkt werden:

SQL>
BEGIN
  DBMS_STATS.SET_GLOBAL_PREFS('AUTOSTATS_TARGET','ORACLE');
END;
/


Für die manuelle Erstellung von Statistiken ist grundsätzlich das PL/SQL-Package DBMS_STATS und nicht mehr das ANALYZE-Kommando zu verwenden.

Bei der DBMS_STATS-Prozedur sollen die beiden Parameter ESTIMATE_PERCENT und METHOD_OPT näher betrachtet werden:

ESTIMATE_PERCENT gibt den prozentualen Wert an, wieviele Datensätze einer Tabelle als Basis für die Statistikerstellung herangezogen werden. Je höher der Wert, desto genauer die Statistik, aber desto höher auch die Ausführungszeit der Statistikerstellung. Ab 10g hat sich der Default-Wert für estimate_percent von 100% auf AUTO_SAMPLE_SIZE geändert. Hierbei entscheidet die Datenbank selbst über das Verhältnis von untersuchten Datensätzen zur Erzielung brauchbarer Statistiken und der möglichst geringen Systembelastung bedingt durch deren Erstellung. Leider hat sich bei 10g gezeigt, dass die Anzahl der untersuchten Datensätze zu gering war und deswegen ein absoluter Wert für ESTIMATE_PERCENT sinnvoller war.

Mit Version 11g hat Oracle den Algorithmus für AUTO_SAMPLE_SIZE geändert. Der Effekt ist, dass 100% der Datensätze untersucht werden, aber nur eine Systembelastung wie bei einem Sample von ca. 10% zu verzeichnen ist.

Die Empfehlung ist daher bei der Statistik-Erstellung von Tabellen einer 11g-Datenbank für ESTIMATE_PERCENT den  AUTO_SAMPLE_SIZE Parameter zu verwenden.

SQL>
BEGIN
 DBMS_STATS.GATHER_TABLE_STATS('MQS','SCHULUNG',
   ESTIMATE_PERCENT => DBMS_STATS.AUTO_SAMPLE_SIZE);
END;
/


METHOD_OPT

Mit dem Parameter METHOD_OPT kann definiert werden, ob sogenannte Histogramme für Tabellenspalten erzeugt werden. Histogramme teilen dem Oracle Optimizer mit, wie die Werte in den Spalten verteilt sind – z. B. wie oft der Wert „Müller“ in der Spalte „KD_NAME“ enthalten ist. Je öfter der Wert vorhanden ist, desto eher kann sich der Oracle Optimizer dazu entschließen, anstatt einem Direkt-Zugriff über Index alle Datensätze der Tabelle mit einem Full Table Scan zu lesen.

Bei Datenbanken der Version 10g trat bei Verwendung von Histogrammen der sogenannte „Bind-Peeking“-Effekt ein, bei dem Oracle auf Grund des ersten Wertes einer Spalte in der „where“-Klausel einen Ausführungsplan gewählt hat (z.B. ein Full Table Scan, weil der Wert sehr häufig vorkam) und bei der nächsten Ausführung trotz eines Wertes mit geringerer Selektivität (Wert kommt seltener vor) den Ausführungsplan nicht geändert hat.

Um diesen Effekt bei Version 10g zu verhindern gibt es zwei Möglichkeiten:

  • Löschen der Histogramme und Erstellen der Statistiken ohne Histogramme
  • Ausschalten von Bind Peeking

Wenn in der Applikation aussschließlich Bind-Variablen verwendet werden, sollten die Histogramme gelöscht werden:

BEGIN
  DBMS_STATS.DELETE_TABLE_STATS('MQS','SCHULUNG');
END;
/


Nach Ausführen der folgenden Prozedur erstellen sowohl der automatische Statistik-Erstell-Job als auch die DBMS_STATS.GATHER_*_STATS-Prozeduren keine Histogramme mehr.

BEGIN
  DBMS_STATS.SET_PARAM(PNAME => 'METHOD_OPT',
    PVAL  => 'FOR ALL COLUMNS SIZE 1');
END;
/


Wenn die Applikation auch Literale verwendet, ist das Ausschalten des Bind-Peekings die bessere Methode.

SQL> alter system set "_OPTIM_PEEK_USER_BINDS"=false;


Bitte vor Setzen des sogenanten Underscore-Parameters in MyOracleSupport über mögliche Seiteneffekte informieren oder beim Oracle Support nachfragen.

Mit Version 11g ist das sogenannte „Adaptive Cursor Sharing“ zur Verhinderung des Bind-Peekings eingeführt worden. Vereinfacht gesprochen können bei diesem Konzept mehrere Ausführungspläne für ein SQL-Statement bei Vorlage unterschiedlicher Verteilungen in den Bind-Variablen erstellt werden.

Die Empfehlung bei 11g für den Parameter METHOD_OPT ist der Default-Wert mit Erstellung von Histogrammen.


PENDING STATISTICS  (Ab Oracle 11.1)

Eine weitere Option, die beim Erstellen von Statistiken beachtet werden kann, sind „Pending Statistics“. Wenn man vom Default abweichende Angaben bei der Erstellung von Statistiken gemacht hat, können diese innerhalb der eigenen Session ohne Beeinflussung des Gesamtsystems als „nicht zu veröffentlichen“ deklariert werden.

BEGIN
  DBMS_STATS.SET_TABLE_PREFS ('MQS','SCHULUNG','PUBLISH','FALSE');
END;
/


Mit Hilfe des Session-Parameters “OPTIMIZER_USE_PENDING_STATISTICS“ = TRUE werden die erstellten Statistiken innerhalb der Session verwendet. Wenn die Statistiken akzeptiert werden sollen, können sie „veröffentlicht“ werden und gelten damit für das Gesamtsystem.

BEGIN
  DBMS_STATS.PUBLISH_PENDING_STATS('MQS','CONSULTING');
END;
/


WANN SOLLEN STATISTIKEN ERSTELLT WERDEN ?
AUTOMATISCHE ERSTELLUNG

Die Datenbank erstellt seit Version 10g automatisch im Rahmen eines Maintenance-Windows (werktäglich um 22 Uhr, Samstag und Sonntag  um 6 Uhr bei 11g ) Statistiken für alle Tabellen, die keine oder veraltete Statistiken haben.

Wenn der Zeitpunkt nicht erwünscht ist, kann die Statistikerstellung folgendermaßen deaktiviert

BEGIN
  DBMS_AUTO_TASK_ADMIN.DISABLE('auto optimizer stats collection',null,null);
END;
/


oder verschoben werden  (z. B. am Montag auf 6 Uhr morgens):

BEGIN
  DBMS_SCHEDULER.SET_ATTRIBUTE('MONDAY_WINDOW',
    'repeat_interval',
    'freq=daily;byday=MON;byhour=06;byminute=0; bysecond=0');
END;
/


MANUELLE ERSTELLUNG

Eine manuelle Erstellung bietet sich dann an, wenn zum Zeitpunkt der automatischen Statistik-Erstellung die Tabelle keine repräsentativen Inhalte für Statistiken besitzt.

Ein Batch-Programm hat um 20 Uhr die Tabelle geleert, der Statistik-Erstell-Job um 22 Uhr ermittelt als Anzahl 0 Datensätze, um 2 Uhr morgens befüllt wiederum ein Batch-Job die Tabelle und um 10 Uhr erfolgt eine Query im Rahmen eines DataWareHouse-Systems. Der Oracle Optimizer ist hierbei nicht in der Lage, einen adäquaten Ausführungsplan zu erzeugen.

In einem solchen Fall ist es besser, den Statistik-Erstell-Job als Bestandteil des Ladeprozesses zu definieren. Der Aufwand für die Erstellung der Statistiken während des Ladeprozesses wird durch eine spürbare Laufzeitverbesserung des SQL-Statements (im DWH-Umfeld kann es sich um Stunden handeln) mehr als ausgeglichen.

Eine Alternative wäre auch, die Statistiken nach Beladung zu sperren oder zu löschen. Aufgrund der Möglichkeit des Optimizers, ein dynamisches Sampling bei Tabellen ohne Statistiken während der Laufzeit durchzuführen (init.ora-Parameter "optimizer_dynamic_sampling" Default=2) kann ein immer noch besserer Ausführungsplan als mit falschen Statistiken erzielt werden.


WELCHE ZUSÄTZLICHEN STATISTIKEN SOLLTEN ERSTELLT WERDEN?
FIXED OBJECT STATISTIKEN

Für X$-Tabellen verwendet der Optimizer voreingestellte Statistikwerte, die nicht unbedingt ein Abbild der tatsächlichen Beladung darstellen. Da Dynamic Sampling nicht automatisch für diese Tabellen verwendet wird, sollten Statistiken folgendermaßen erzeugt werden:

BEGIN
 DBMS_STATS.GATHER_FIXED_OBJECTS_STATS;
END;
/


Die Statistik-Erstellung für Fixed Objects sollte nach Datenbank-Upgrades oder nach Änderungen in der Datenbank-Konfiguration wiederholt werden.


SYSTEM-STATISTIKEN

Neben den Objekt-Statistiken benötigt der Oracle Optimizer Informationen über die Kapazität des Systems, auf dem die Datenbank läuft.

Bei der Ausführung der Query  „select * from mqs.consulting order by mitarbeiter“ kommen im wesentlichen zwei Ausführungspfade in Betracht:

  • Full-Table-Scan auf mqs.consulting und Sortierung der Ergebnismenge
  • Ermitteln der (bereits sortierten) Zeilen über den Primary Index

Wenn das System über schnelle CPUs und langsame Platten verfügt, ist der erste Ausführungspfad günstiger. Bei schnellen Platten und langsameren CPUs ist möglicherweise der zweite Pfad günstiger.

Damit sich die Datenbank einen Überblick über das System verschaffen kann, ist es empfehlenswert, mit folgender Prozedur Systemstatistiken zur Hauptverarbeitungszeit zu erstellen:

BEGIN
  DBMS_STATS.GATHER_SYSTEM_STATS(
    gathering_mode => 'INTERVAL',
    Interval => 60);
END;
/


Die von Oracle im Intervall von 60 Minuten ermittelten Werte können anschließend der VIEW SYS.AUX_STATS$ entnommen werden.

Fazit

Das Vorhandensein von passenden Objekt-Statistiken ist die unabdingbare Voraussetzung für ein performantes Datenbanksystem und eine hohe Akzeptanz auf Anwenderseite.

Statistiken auf Fixed Tables und auf Systemressourcen verbessern den Durchsatz in der Datenbank, haben aber bei weitem nicht den Effekt wie die Objekt-Statistiken.

Detaillierte Informationen zu Statistiken erhalten Sie in den DB Tuning , SQL Tuning und Optimizer Schulungsangeboten der Muniqsoft GmbH.



Weitere Interessente Artikel zum Thema:


Empfohlene Schulungen zum Thema:


Zeilenbegrenzung in 12c - Pagination leicht gemacht

Bereich:PL/SQL, Version: ab RDBMS 12.x, Letzte Überarbeitung: 05.08.2023

Keywords:Oracle Neuerungen, SQL, PL/SQL

Wer je Daten für eine Web-Applikation bereitstellen musste, kennt das Problem der Pagination. In der Regel holt das Frontend ja nur die Daten, die auf eine Seite passen, und erst wenn mehr angefordert werden, werden auch mehr geholt. Das stellt den Programmierer vor die Schwierigkeit, die Daten entprechend "mundgerecht" zu liefern. Entscheidend dabei ist in jedem Fall eine absolut eindeutige Sortier-Reihenfolge.

Ein klassischer von Tom Kyte u. a. hier Öffnet externen Link in neuem Fenster hier beschriebener Ansatz über ROWNUM sieht dann so aus:

select *
  from ( select /*+ FIRST_ROWS(n) */
  a.*, ROWNUM rnum
      from ( your_query_goes_here,
      with order by ) a
      where ROWNUM <=
      :MAX_ROW_TO_FETCH )
where rnum  >= :MIN_ROW_TO_FETCH;

Anmerkung: Für die folgenden Beispiele wurde eine Tabelle OBJECT_TAB als Kopie von ALL_OBJECTS erstellt.

Angewendet auf diese Tabelle sähe ein Select, der die Zeilen 11 bis 20 holt, z. B. so aus:

SELECT object_id, object_name
  FROM (SELECT a.*, ROWNUM rnum
          FROM (  SELECT *
                    FROM object_tab
                ORDER BY object_id) a
         WHERE ROWNUM <= 20)
 WHERE rnum >= 11;

Der Ausführungsplan zeigt, dass der Optimizer hier mit Stopkeys arbeitet:

Execution Plan
----------------------------------------------------------
0      SELECT STATEMENT Optimizer Mode=ALL_ROWS (Cost=1872 Card=20 Bytes=1 K)
1   0    VIEW (Cost=1872 Card=20 Bytes=1 K)
2   1      COUNT STOPKEY
3   2        VIEW (Cost=1872 Card=65 K Bytes=4 M)
4   3          SORT ORDER BY STOPKEY (Cost=1872 Card=65 K Bytes=6 M)
5   4            TABLE ACCESS FULL OBJECT_TAB (Cost=290 Card=65 K Bytes=6 M)

Anmerkung: Um Zeilenumbrüche in der Darstellung zu verhindern, wurden überflüssige Leerzeichen und Schema-Angaben aus dem Ausführungsplan entfernt.

Im PL/SQL-Umfeld würde eine entsprechende Prozedur - stark vereinfacht - z. B. so aussehen:

CREATE PROCEDURE test_1 (p_from     IN     NUMBER,
                         p_until    IN     NUMBER,
                         p_result      OUT SYS_REFCURSOR)
AS
BEGIN
   OPEN p_result FOR
      SELECT object_id, object_name
        FROM (SELECT a.*, ROWNUM rnum
                FROM (  SELECT object_id, object_name
                          FROM object_tab
                      ORDER BY object_id) a
               WHERE ROWNUM <= p_until)
       WHERE rnum >= p_from;
END test_1;
/

Da man jedoch gerade bei Suchmasken in der Regel mit dynamischen SQL arbeiten muss, ist das wohl etwas näher dran:

CREATE PROCEDURE test_2 (p_from     IN     NUMBER,
                         p_until    IN     NUMBER,
                         p_result      OUT SYS_REFCURSOR)
AS
   v_sql   VARCHAR2 (4000 CHAR);
BEGIN
   v_sql  := 'SELECT object_id, object_name
              FROM (SELECT a.*, ROWNUM rnum
                      FROM (  SELECT object_id, object_name
                                FROM object_tab
                                -- WHERE .....
                            ORDER BY object_id) a
                     WHERE ROWNUM <= :1)
             WHERE rnum >= :2';
   OPEN p_result FOR v_sql USING p_until, p_from;
END test_2;
/

Das funktioniert zwar, ist aber nicht direkt intuitiv.

Mit Version 12c hat nun Oracle eine neue Klausel zur Begrenzung der Zeilen eingeführt, die die Syntax in solchen Fällen deutlich vereinfacht.

Angegeben werden können dabei:

  • OFFSET: Ein Startpunkt, ab wo geliefert werden soll; wird diese Angabe weggelassen, gilt OFFSET = 0, also Beginn beim ersten Datensatz der Ergebnismenge

 

  • FETCH: Anzahl oder Prozentsatz an Zeilen, die geholt werden sollen; wird diese Angabe weggelassen, so werden alle Zeilen ab <OFFEST + 1> geholt


Das obige Beispiel würde in 12c so aussehen:

  SELECT object_id, object_name
    FROM object_tab
ORDER BY object_id
 OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY;

Glaubt man dem Ausführungsplan, so ist diese Abfrage nicht nur wesentlich lesbarer, sondern auch noch performanter:

Execution Plan
----------------------------------------------------------
0      SELECT STATEMENT Optimizer Mode=ALL_ROWS (Cost=860 Card=65 K Bytes=6 M)
1   0    VIEW (Cost=860 Card=65 K Bytes=6 M)
2   1      WINDOW SORT PUSHED RANK (Cost=860 Card=65 K Bytes=1 M)
3   2        TABLE ACCESS FULL OBJECT_TAB (Cost=289 Card=65 K Bytes=1 M)

Anmerkung: Auch hier wurden überflüssige Leerzeichen und Schema-Angaben aus dem Ausführungsplan entfernt.

Eine Prozedur analog zu oben würde dann so aussehen:

CREATE OR REPLACE PROCEDURE test (p_offset   IN     NUMBER,
                                  p_lines    IN     NUMBER,
                                  p_result      OUT SYS_REFCURSOR)
AS
   v_sql   VARCHAR2 (4000 CHAR);
BEGIN
   v_sql  := '  SELECT object_id,object_name
                 FROM object_tab
                 -- WHERE .....
                ORDER BY object_id
                OFFSET :1 ROWS FETCH NEXT :2 ROWS ONLY';
   OPEN p_result FOR v_sql USING p_offset, p_lines;
END test;
/

Wesentlich lesbarer, oder?

Welche Möglichkeiten bietet die neue Klausel noch? Neben dem oben angegebenen <n> ROWS kann auch ein Prozentsatz mitgegeben werden mit <n> PERCENT ROWS. Das würde dann so aussehen:

  SELECT object_id, object_name
    FROM object_tab
ORDER BY object_id
 OFFSET 10 ROWS FETCH NEXT 10 PERCENT ROWS ONLY;

Sollte die Sortierung nicht eindeutig sein (und auch nicht sein müssen), so kann man angeben, dass alle Datenätze mit ausgegeben werden sollen, die den gleichen Wert haben wie der zuletzt geholte. Dazu gibt man statt ONLY an WITH TIES. Der Effekt sei hier gezeigt an der allseits bekannten Tabelle SCOTT.EMP:

  SELECT ename, sal
    FROM scott.emp
ORDER BY sal DESC
FETCH NEXT 2 ROWS ONLY;
ENAME             SAL
---------- ----------
KING             5000
SCOTT            3000

 


  SELECT ename, sal
    FROM scott.emp
ORDER BY sal DESC
FETCH NEXT 2 ROWS WITH TIES;
ENAME             SAL
---------- ----------
KING             5000
SCOTT            3000
FORD             3000

Diese neue Klausel ist prinzipell nicht abhängig von der ORDER BY-Klausel. Ihre vollen Möglichkeiten entfaltet sie aber nur hier.

Mehr zu diesem Thema erfahren Sie in unserer Schulung Oracle Neuerungen 12c, schauen Sie doch einfach vorbei :-)



Weitere Interessente Artikel zum Thema:


Empfohlene Schulungen zum Thema:


Netzwerk Verschlüsselung

Bereich:DBA, Version: ab RDBMS 10.x, Letzte Überarbeitung: 02.04.2022

Keywords:DBA, Security

Seit dem Erscheinen der Oracle Datenbank 12c wurde eine Reihe von Security Features, die bisher Bestandteil der Advanced-Security-Option (ASO) waren, als Feature für die Datenbank verfügbar gemacht. Dies gilt für die Enterprise Edition und auch für die Standard Edition. Siehe auch Oracle icensing Guide.

SQL*NET MIT GERINGEM AUFWAND VERSCHLÜSSELN


Auf der Serverseite unter UNIX $ORACLE_HOME/network/admin, oder unter Windows %ORACLE_HOME%\network\admin folgende Einträge in der sqlnet.ora vornehmen:

### SQL*Net encryption ##################################
SQLNET.CRYPTO_CHECKSUM_TYPES_SERVER = (SHA1)
SQLNET.CRYPTO_CHECKSUM_SERVER = REQUIRED
SQLNET.CRYPTO_SEED="9iY3kcmwXdiZwXZypQ8g#_:*'!10aRtopRC#"
SQLNET.ENCRYPTION_TYPES_SERVER = (AES256)
SQLNET.ENCRYPTION_SERVER = REQUIRED
### SQL*Net encryption end ##############################

Die sqlnet.ora des/der Client/s wird nicht modifiziert.

Die Bedeutung der Werte im Einzelnen:

SQLNET.CRYPTO_CHECKSUM_TYPES_SERVER

mögliche Werte

  • MD5 für den RSA Algorythmus
  • SHA1 für den Secure Hash Algorythmus


SQLNET.CRYPTO_CHECKSUM_SERVER

  • ACCEPTED - (Default) und bedeutet, dass die Verschlüsselung akzeptiert wird, wenn der Kommunikationspartner dies möchte.
  • REJECTED - Die Verschlüsselung wird grundsätzlich abgelehnt.
  • REQUESTED - Die Verschlüsselung wird gewünscht, aber nicht verlangt.
  • REQUIRED - Die Verschlüsselung ist notwendig.

SQLNET.CRYPTO_SEED

Der Wert sollte aus 10 bis 70 Buchstaben, Zahlen und Sonderzeichen bestehen. SQLNET.CRYPTO_SEED ist notwendig für die Checksummenprüfung und die Verschlüsselung. Als default wird der Wert "qwertyuiopasdfghjkl;zxcvbnm,.s1" verwendet.

SQLNET.ENCRYPTION_TYPES_SERVER

Die zur Verfügung stehenden Verschlüsselungs Algorythmen sind:

  • 3des112 for triple DES with a two-key (112-bit) option
  • 3des168 for triple DES with a three-key (168-bit) option
  • des for standard 56-bit key size
  • des40 for 40-bit key size
  • rc4_40 for 40-bit key size
  • rc4_56 for 56-bit key size
  • rc4_128 for 128-bit key size
  • rc4_256 for 256-bit key size


SQLNET.ENCRYPTION_SERVER

  • ACCEPTED - Die Verschlüsselung wird akzeptiert, wenn der Kommunikationspartner dies möchte.
  • REJECTED - Die Verschlüsselung wird grundsätzlich abgelehnt.
  • REQUESTED - Die Verschlüsselung wird gewünscht, aber nicht verlangt.
  • REQUIRED - Die Verschlüsselung ist obligatorisch


Der Test:

Das Funktionieren der SQL*Net Verschlüsselung lässt sich ganz einfach überprüfen. Dazu wird in der sqlnet.ora das Tracing aktiviert.

Serverseitig:

DIAG_ADR_ENABLED=off
trace_level_server=16
trace_directory_server = /tmp/ora_trace
trace_file_server = srv

Clientseitig:

DIAG_ADR_ENABLED=off
trace_level_client=16
trace_directory_client = C:\tmp\ora_trace
trace_file_client = cli

Die Tracefiles unverschlüsselt
vom Server:

[03-MRZ-2014 13:41:20:734] nsprecv: packet dump
[03-MRZ-2014 13:41:20:734] nsprecv: 2F 73 65 6C 65 63 74 20  |/select.|
[03-MRZ-2014 13:41:20:734] nsprecv: 65 6E 61 6D 65 2C 20 73  |ename,.s|
[03-MRZ-2014 13:41:20:734] nsprecv: 61 6C 20 66 72 6F 6D 20  |al.from.|
[03-MRZ-2014 13:41:20:734] nsprecv: 65 6D 70 20 77 68 65 72  |emp.wher|
[03-MRZ-2014 13:41:20:734] nsprecv: 65 20 65 6E 61 6D 65 20  |e.ename.|
[03-MRZ-2014 13:41:20:734] nsprecv: 3D 20 27 4B 49 4E 47 27  |=.'KING'|
[03-MRZ-2014 13:41:20:734] nsprecv: 00 00 00 00              |....    |
[03-MRZ-2014 13:41:20:734] nsprecv: normal exit

vom Client:

[03-MRZ-2014 13:41:20:911] nsbasic_bsd: packet dump
[03-MRZ-2014 13:41:20:911] nsbasic_bsd: 2F 73 65 6C 65 63 74 20  |/select.|
[03-MRZ-2014 13:41:20:911] nsbasic_bsd: 65 6E 61 6D 65 2C 20 73  |ename,.s|
[03-MRZ-2014 13:41:20:911] nsbasic_bsd: 61 6C 20 66 72 6F 6D 20  |al.from.|
[03-MRZ-2014 13:41:20:911] nsbasic_bsd: 65 6D 70 20 77 68 65 72  |emp.wher|
[03-MRZ-2014 13:41:20:911] nsbasic_bsd: 65 20 65 6E 61 6D 65 20  |e.ename.|
[03-MRZ-2014 13:41:20:911] nsbasic_bsd: 3D 20 27 4B 49 4E 47 27  |=.'KING'|
[03-MRZ-2014 13:41:20:912] nsbasic_bsd: 00 00 00 00              |....    |
[03-MRZ-2014 13:41:20:912] nsbasic_bsd: exit (0)

Die Tracefiles verschlüsselt
vom Server:

[03-MRZ-2014 13:47:46:719] nspsend: packet dump
[03-MRZ-2014 13:47:46:719] nspsend: 2A 86 48 86 F7 12 01 02  |*.H.....|
[03-MRZ-2014 13:47:46:719] nspsend: 02 02 00 6F 81 88 30 81  |...o..0.|
[03-MRZ-2014 13:47:46:719] nspsend: 85 A0 03 02 01 05 A1 03  |........|
[03-MRZ-2014 13:47:46:719] nspsend: 02 01 0F A2 79 30 77 A0  |....y0w.|
[03-MRZ-2014 13:47:46:719] nspsend: 03 02 01 12 A2 70 04 6E  |.....p.n|
[03-MRZ-2014 13:47:46:719] nspsend: 5E 9B EB EC 6B 37 CD FB  |^...k7..|
[03-MRZ-2014 13:47:46:719] nspsend: A2 6F 08 21 6B 02 F8 33  |.o.!k..3|
[03-MRZ-2014 13:47:46:719] nspsend: 0A 2E E2 71 A3 33 FC BE  |...q.3..|
[03-MRZ-2014 13:47:46:719] nspsend: 97 E8 70 C3 71 D6 98 F6  |..p.q...|
[03-MRZ-2014 13:47:46:719] nspsend: F5 F2 A2 48 47 67 E9 34  |...HGg.4|
[03-MRZ-2014 13:47:46:719] nspsend: 28 09 ED F1 10 93 3B 72  |(.....;r|
[03-MRZ-2014 13:47:46:719] nspsend: 8B A0 59 B5 F3 DA FE 7E  |..Y....~|
[03-MRZ-2014 13:47:46:719] nspsend: 0A F8 EB AF D8 64 4C D9  |.....dL.|
[03-MRZ-2014 13:47:46:719] nspsend: 25 A8 19 41 E1 39 DD 9A  |%..A.9..|
[03-MRZ-2014 13:47:46:719] nspsend: 4F CD 43 81 3A F2 57 4A  |O.C.:.WJ|
[03-MRZ-2014 13:47:46:719] nspsend: 1E 1B 5D 68 03 0A 6B 6D  |..]h..km|
[03-MRZ-2014 13:47:46:719] nspsend: 96 27 BC 43 05 E7 49 51  |.'.C..IQ|
[03-MRZ-2014 13:47:46:719] nspsend: AB D8 41 E9 6C AE BB 47  |..A.l..G|
[03-MRZ-2014 13:47:46:719] nspsend: FB C0 73 0E 72 37        |..s.r7  |

vom Client:

[03-MRZ-2014 13:47:46:998] nsprecv: packet dump
[03-MRZ-2014 13:47:46:998] nsprecv: 88 1B 3E A9 2A 2E D7 7E  |..>.*..~|
[03-MRZ-2014 13:47:46:998] nsprecv: DB 19 32 AD BE E1 04 8D  |..2.....|
[03-MRZ-2014 13:47:46:998] nsprecv: CF 5B 1D 4B 69 F2 75 4A  |.[.Ki.uJ|
[03-MRZ-2014 13:47:46:998] nsprecv: 4F 9F 2E CE 5E 49 DC 13  |O...^I..|
[03-MRZ-2014 13:47:46:998] nsprecv: 9D 18 D9 16 9A 8E BE BB  |........|
[03-MRZ-2014 13:47:46:998] nsprecv: 4C 79 78 F4 23 82 95 3A  |Lyx.#..:|
[03-MRZ-2014 13:47:46:998] nsprecv: 53 6C DA B0 6E 47 92 68  |Sl..nG.h|
[03-MRZ-2014 13:47:46:998] nsprecv: 5D 1A 27 69 34 7E 96 A4  |].'i4~..|
[03-MRZ-2014 13:47:46:998] nsprecv: 84 A1 36 26 83 AC F2 8A  |..6&....|
[03-MRZ-2014 13:47:46:998] nsprecv: 2D 70 59 03 DB 56 C2 76  |-pY..V.v|
[03-MRZ-2014 13:47:46:998] nsprecv: 9E DA 5E DB 4D E6 D9 7D  |..^.M..}|
[03-MRZ-2014 13:47:46:998] nsprecv: 4D 2D 92 E1 89 54 57 3C  |M-...TW<|
[03-MRZ-2014 13:47:46:998] nsprecv: B6 FA B1 09 68 4E 05 A0  |....hN..|
[03-MRZ-2014 13:47:46:998] nsprecv: 88 0B C1 87 BE 69 FD 9C  |.....i..|
[03-MRZ-2014 13:47:46:998] nsprecv: 22 DE FD D0 DA 5E FF 03  |"....^..|
[03-MRZ-2014 13:47:46:998] nsprecv: B7 7F 53 F5 8B 88 35 6F  |..S...5o|
[03-MRZ-2014 13:47:46:998] nsprecv: 7D FE 64 CF 71 82 C7 83  |}.d.q...|
[03-MRZ-2014 13:47:46:998] nsprecv: 0A AE B4 4B 98 C8 9E AC  |...K....|
[03-MRZ-2014 13:47:46:998] nsprecv: 07 AE A0 55 5D 3A 76 BD  |...U]:v.|
[03-MRZ-2014 13:47:46:998] nsprecv: 1A 3A A6 DF 2A EB AC 9F  |.:..*...|
[03-MRZ-2014 13:47:46:998] nsprecv: D7 AB 13 6F 84 48 FC 81  |...o.H..|
[03-MRZ-2014 13:47:46:998] nsprecv: 54 76 E2 D0 D0 C2 69 7E  |Tv....i~|
[03-MRZ-2014 13:47:46:998] nsprecv: DF D4 73 0D CB B6 88 C7  |..s.....|
[03-MRZ-2014 13:47:46:998] nsprecv: 38 3D 1B 24 9D FD BF 77  |8=.$...w|
[03-MRZ-2014 13:47:46:998] nsprecv: E2 3B F9 E8 19 5B B4 3D  |.;...[.=|
[03-MRZ-2014 13:47:46:998] nsprecv: 19 7B FA C4 3C 27 7E D0  |.{..<'~.|
[03-MRZ-2014 13:47:46:998] nsprecv: 3C 5F B3 FB 79 36 FA 07  |<_..y6..|
[03-MRZ-2014 13:47:46:998] nsprecv: 99 BB EF 2E 7A D4 65 1B  |....z.e.|
[03-MRZ-2014 13:47:46:998] nsprecv: B6 05 12 80 4C 70 6E C3  |....Lpn.|
[03-MRZ-2014 13:47:46:998] nsprecv: 3F C3 5C 76 74 4C 75 B0  |?.\vtLu.|
[03-MRZ-2014 13:47:46:998] nsprecv: 0D F8 F4 AD 6E 76 68 1D  |....nvh.|
[03-MRZ-2014 13:47:46:998] nsprecv: 33 DF 3F E9 45 BF 5E D4  |3.?.E.^.|
[03-MRZ-2014 13:47:46:998] nsprecv: 87 4C 3A 03 0F C9 99 8E  |.L:.....|
[03-MRZ-2014 13:47:46:998] nsprecv: 39 84 A1 5C B7 DD AD F7  |9..\....|
[03-MRZ-2014 13:47:46:998] nsprecv: A6 6D F2 C3 0B 74 18 04  |.m...t..|
[03-MRZ-2014 13:47:46:998] nsprecv: 4E D1 0A 01              |N...    |

 

Fazit:

Die SQL*Net Verschlüsselung ist einfach einzurichten und es entstehen keine zusätzliche Kosten. Performanceunterschiede zwischen verschlüsselter und unverschlüsselter Datenübertragung sind nicht bekannt.

Sollten Sie Interesse an weiteren Sicherheitsfunktionen zu Oracle Datenbanken haben, besuchen Sie doch unseren Kurs Datenbank Security.



Weitere Interessente Artikel zum Thema:



Empfohlene Schulungen zum Thema:


Instanzstart Mittels RMAN ohne Parameterdatei

Bereich:DBA, Version: ab APEX 5.x, Letzte Überarbeitung: 12.01.2021

Keywords:DBA, Oracle Backup & Recovery

Haben Sie schon einmal versucht Ihre Oracle Instanz zu starten und dabei feststellen müssen, dass Oracle keine Parameterdatei mehr findet? Gleichgültig, ob Sie eine binäre Datei, also ein SPFILE, oder eine ASCII-Datei verwenden, benötigt Oracle für den Startvorgang eine Datei, aus der die Basis-Parameter (Kontrolldateien, SGA-Parameter, UNDO-Verwaltung etc.) ausgelesen werden. Wird beim Start über SQL*Plus keine Parameterdatei gefunden und auch kein PFILE explizit angegeben, kommt es zu folgender Fehlermeldung:

SQL> startup nomount
ORA-01078: failure in processing system parameters
LRM-00109: could not open parameter file '/u01/oracle/product/21.0.0/dbhome_1/dbs/inito21c.ora'

Ein Starten ohne Parameterdatei ist offensichtlich nicht möglich. Jedoch gilt hier die Devise: nicht gleich aufgeben!

Hinweis

Bei den folgenden Szenarien wird grundsätzlich davon ausgegangen, dass das Backup des SPFILEs mittels RMAN erzeugt worden ist. Im Idealfall ist der RMAN-Konfigurationsparameter CONTROLFILE AUTOBACKUP auf ON gesetzt, wodurch automatische Backups vom Controlfile und SPFILE verfügbar sind.

Öffnen Sie nun den RMAN, melden Sie sich über diese Oberfläche an und probieren Sie den Startvorgang erneut.

$> rman target /
Recovery Manager: Release 21.0.0.3.0 - Production on Tue Jan 7 12:16:31 2021
Copyright (c) 1982, 2021, Oracle and/or its affiliates.  All rights reserved.
connected to target database (not started)
RMAN> startup nomount

Auch hier kommt es zur obigen Fehlermeldung, allerdings ist der RMAN in der Lage die Instanz mittels temporärer Dummy-Parameterdatei dennoch zu starten.

startup failed: ORA-01078: failure in processing system parameters
LRM-00109: could not open parameter file '/u01/oracle/product/11.2.0/dbhome_1/dbs/inito11g.ora'
starting Oracle instance without parameter file for retrieval of spfile
Oracle instance started
Total System Global Area     158662656 bytes
Fixed Size                     2226456 bytes
Variable Size                 92276456 bytes
Database Buffers              58720256 bytes
Redo Buffers                   5439488 bytes

Oracle generiert dazu auch eine neue Alertdatei (unter ORACLE_BASE/diag/rdbms/dummy) in der folgende Initialisierungsparameter aufgelistet werden:

...
Using parameter settings in client-side pfile /tmp/ora_tfilkB8krT on ...
System parameters with non-default values:
  sga_target               = 152M
  compatible               = "21.0.0.3.0"
  _dummy_instance          = TRUE
  remote_login_passwordfile= "EXCLUSIVE"
  db_name                  = "DUMMY"

Wenn Sie erst einmal soweit gekommen sind, dann ist der Rest - das Neuerzeugen des SPFILEs aus dem RMAN Backup und ein erneutes Durchstarten - auch nicht mehr schwer. Dazu kommen wir allerdings etwas später.

MÖGLICHE PROBLEME BEIM STARTEN ÜBER DEN RMAN


Zunächst widmen wir uns aber dem eigentlichen Zweck dieses Tipps, nämlich den möglichen Problemen, die beim Startvorgang über den RMAN evtl. auftreten können.

PROBLEM 1 - FEHLENDE BERECHTIGUNGEN


Unter Windows 7 bzw. 2008 Server kann es notwendig sein, die Kommandozeile aus der heraus Sie den RMAN starten, mit dem Zusatz "Als Administrator ausführen" zu öffnen. Ansonsten erhalten Sie den Fehler:

RMAN> startup nomount
Start nicht erfolgreich: ORA-01078: failure in processing system parameters
LRM-00109: Parameterdatei 'C:\ORACLE\PRODUCT\21.0.0\DBHOME_1\DATABASE\INITO21C.ORA' konnte nicht geöffnet werden
Oracle-Instanz ohne Parameterdatei gestartet, um spfile abzurufen
RMAN-00571: ===========================================================
RMAN-00569: =============== ERROR MESSAGE STACK FOLLOWS ===============
RMAN-00571: ===========================================================
RMAN-03002: Fehler bei startup Befehl auf 01/01/2021 11:45:17
RMAN-04025: Temporäre Datei kann nicht generiert werden

 

PROBLEM 2 - ABSTURZ DER INSTANZ MIT ORA-03113 UND ORA-04031


Das zweite Problem stellt sich wesentlich dramatischer dar:

RMAN> startup nomount
Start nicht erfolgreich: ORA-01078: failure in processing system parameters
LRM-00109: Parameterdatei 'C:\ORACLE\PRODUCT\21.0.0\DBHOME_1\DATABASE\INITO21C.ORA' konnte nicht geöffnet werden
Oracle-Instanz ohne Parameterdatei gestartet, um spfile abzurufen
RMAN-00571: ===========================================================
RMAN-00569: =============== ERROR MESSAGE STACK FOLLOWS ===============
RMAN-00571: ===========================================================
RMAN-03002: Fehler bei startup Befehl auf 01/01/2021 11:47:12
RMAN-04014: Start nicht erfolgreich: ORA-03113: Unerwartetes Übertragungsende in Kommunikation

Wagt man nun einen Blick in die gerade neu erzeugte Alertdatei unter %ORACLE_BASE%\diag\rdbms\dummy, dann erkennt man eine Reihe von Speicherfehlern und abschließend den Absturz der Instanz. Hier ein Auszug aus der Alertdatei:

Starting up:
...
Tue Jan 01 11:47:01 2021
Errors in file C:\ORACLE\diag\rdbms\dummy\o11g\trace\o21c_pmon_3056.trc  (incident=4161):
ORA-04031: unable to allocate 3977048 bytes of shared memory ("shared pool","unknown object","KSFD SGA I/O b","ksfd I/O buffer pool")
Incident details in: C:\ORACLE\diag\rdbms\dummy\o21c\incident\incdir_4161\o21c_pmon_3056_i4161.trc
Use ADRCI or Support Workbench to package the incident.
See Note 411.1 at My Oracle Support for error and packaging details.
WARNING:Shared I/O Pool created with size=0 set size=4194304
PMON started with pid=2, OS id=3056
...
Errors in file C:\oracle\diag\rdbms\dummy\o21c\trace\o21c_ora_3428.trc  (incident=4162):
ORA-04031: unable to allocate 973600 bytes of shared memory ("large pool","unknown object","large pool","PX msg pool")
Incident details in: C:\oracle\diag\rdbms\dummy\o21c\incident\incdir_4162\o21c_ora_3428_i4162.trc
...
Sweep [inc][4164]: completed
...
USER (ospid: 3428): terminating the instance due to error 12853
Tue Jan 07 11:47:10 2014
Instance terminated by USER, pid = 3428

Läuft Ihre Datenbank unter Windows, muss an dieser Stelle sogar der Datenbankdienst OracleService <sid> neu gestartet werden, ansonsten ist ein weiteres Arbeiten nicht mehr möglich.

Tritt dieses Verhalten auf, dann sind Sie in einen dokumentierten Bug (# 9680987) für die Version 11.2.x hineingelaufen. Dabei sind die Dummy-Werte, die der RMAN zum Starten der Instanz verwenden möchte nicht ausreichend dimensioniert.

Als Workaround für dieses Problem setzen Sie vor dem RMAN-Aufruf einfach die Systemvariable ORA_RMAN_SGA_TARGET und starten dann erst über den RMAN Ihre Instanz.

-- unter Windows
C:\> set ORA_RMAN_SGA_TARGET=350M
-- unter Linux
$> export ORA_RMAN_SGA_TARGET=350M
rman target /
RMAN> startup nomount
startup failed: ORA-01078: failure in processing system parameters
LRM-00109: could not open parameter file '/u01/oracle/product/11.2.0/dbhome_1/dbs/inito11g.ora'
starting Oracle instance without parameter file for retrieval of spfile
Oracle instance started
Total System Global Area     367439872 bytes
Fixed Size                     2228464 bytes
Variable Size                121638672 bytes
Database Buffers             239075328 bytes
Redo Buffers                   4497408 bytes

 

NEUERZEUGUNG DES SPFILES


Haben Sie es - trotz aller Widrigkeiten - geschafft Ihre Instanz ohne Parameterdatei in die NOMOUNT-Phase zu versetzen, dann fehlt jetzt nur noch das Zurückspielen des SPFILEs und das erneute Durchstarten, damit das gerade erzeugte SPFILE herangezogen werden kann. Bis zur Version 10.2 mussten Sie vor dem Restore des SPFILEs erst noch die DBID Ihrer Datenbank setzen, ab Version 11g ist dies optional.

-- RMAN> set dbid <id>
RMAN> restore spfile from autobackup
       db_recovery_file_dest='/u01/oracle/fast_recovery_area'
       db_name='o11g';
Starting restore at 07.01.2014 14:44:02
using target database control file instead of recovery catalog
allocated channel: ORA_DISK_1
channel ORA_DISK_1: SID=19 device type=DISK
recovery area destination: /u01/oracle/fast_recovery_area
database name (or database unique name) used for search: O11G
channel ORA_DISK_1: AUTOBACKUP /u01/oracle/fast_recovery_area/O11G/autobackup/2014_01_07/o1_mf_s_836219695_9dqnx0b4_.bkp found in the recovery area
AUTOBACKUP search with format "%F" not attempted because DBID was not set
channel ORA_DISK_1: restoring spfile from AUTOBACKUP /u01/oracle/fast_recovery_area/O11G/autobackup/2014_01_07/o1_mf_s_836219695_9dqnx0b4_.bkp
channel ORA_DISK_1: SPFILE restore from AUTOBACKUP complete
Finished restore at 07.01.2014 14:44:05
RMAN> shutdown
Oracle instance shut down
RMAN> startup
connected to target database (not started)
Oracle instance started
database mounted
database opened
Total System Global Area    1052233728 bytes
Fixed Size                     2235040 bytes
Variable Size                784336224 bytes
Database Buffers             260046848 bytes
Redo Buffers                   5615616 bytes

Sie sehen, mit Hilfe des RMAN ist auch das Neuerzeugen eines SPFILEs keine Hexerei mehr und auch bei Auftreten eines der oben beschriebenen Probleme relativ einfach durchzuführen.

Hinweis

Ist die Parameterdatei nur logisch zerstört worden, aber physisch noch vorhanden, muss sie vor dem Starten mittels RMAN erst komplett entfernt werden.

Diese und weitere Tipps rund um den RMAN erhalten Sie in einem unserer RMAN oder Backup & Recovery Kurse. Schauen Sie sich einfach einmal auf unserer Schulungsseite um.



Weitere Interessente Artikel zum Thema:


Empfohlene Schulungen zum Thema:


SQL*PLUS Hilfe Erweitern

Bereich:SQL, Version: ab RDBMS 11.x, Letzte Überarbeitung: 09.07.2020

Keywords:DBA, Oracle Tools, SQL

Die SQL*Plus Hilfe war bis zur Version 8.1 eigentlich recht hilfreich, da sie bei der Syntax von SQL und PL/SQL immer recht gut weitergeholfen hat. Leider wird dieser Bereich von Oracle nicht mehr gepflegt, es wurde nur noch die SQL*Plus Syntax in der Hilfe gelassen.

Es ist Zeit, hier etwas zu verbessern. Hinweis: Dies ist von Oracle nicht supported! und sollte deshalb nur auf Testdatenbanken verwendet werden.

Wenn die Hilfe-Tabelle noch nicht existiert, legen Sie diese bitte mit folgendem Skript an:

connect system/sys
@?/sqlplus/admin/help/hlpbld.sql helpus.sql

Dann schauen wir uns die Struktur der Tabelle einmal genauer an:

SQL> desc system.help
 Name    Null?    Typ
 ------ --------- ----------------
 TOPIC   NOT NULL VARCHAR2(50)
 SEQ     NOT NULL NUMBER
 INFO             VARCHAR2(80)

Die Topic-Spalte ist für den Text verantwortlich nach dem gesucht wird, die Info-Spalte gibt dann den Hilfetext zurück. Die SEQ-Spalte ist für mehrzeilige Texte gedacht. Sie muss pro Topic eindeutig sein.

Wenn wir in die Tabelle die Werte ('Muniqsoft-Training',1,'Schulung Tel.: 089 67909040') eintragen dann liefert der Befehl zurück:

SQL> help muniqsoft-training
Schulung Tel.: 089 67909040

Hinweis:
Für einige Hilfetexte ist in den Spalten zu wenig Platz, deswegen vergrößern wir zwei Spalten. In der Version 12.1 hat das funktioniert. In älteren Versionen kann es zu Problemen kommen, dann lassen Sie den Schritt weg. Jedoch können Sie dann natürlich auch nicht so viel Text in die Spalten eintragen.

ALTER TABLE system.help MODIFY (info VARCHAR2(140));
ALTER TABLE system.help MODIFY (topic VARCHAR2(100));

Zum warmwerden, tragen wir ein paar Texte ein (Ein bisschen Schleichwerbung muss schon sein :-) )

INSERT INTO system.help VALUES('MUNIQSOFT',0,'Muniqsoft GmbH www.muniqsoft-training.de Tel.: 089/67 90 90 40');
INSERT INTO system.help VALUES('MUNIQSOFT',1,'Beratung, Schulungen, Consulting, Lizenzvertrieb');
INSERT INTO system.help VALUES('MUNIQSOFT',2,'Ihr Oracle Partner, wenn es um Oracle DBA, Administration, Migration,');
INSERT INTO system.help VALUES('MUNIQSOFT',3,'Schulungen (auch Inhouse), Tuning oder Backup + Recovery geht');

Und testen das mit:

help muniqsoft-training

Nun legen wir richtig los:

INSERT INTO system.help VALUES('ALTER DATABASE',0,'REM +++ Muniqsoft Training GmbH www.muniqsoft-training.de Tel.: 089/67909040 +++');
INSERT INTO system.help VALUES('ALTER DATABASE',1,'ALTER DATABASE DEFAULT TEMPORARY TABLESPACE <temp>;');
INSERT INTO system.help VALUES('ALTER DATABASE',2,'ALTER DATABASE DATAFILE ''<file>'' RESIZE <x>M;');

 

INSERT INTO system.help VALUES('ALTER DATABASE',3,'ALTER DATABASE ADD LOGFILE <name> SIZE <x>M;');
INSERT INTO system.help VALUES('ALTER DATABASE',4,'ALTER DATABASE DROP LOGFILE GROUP <n>;');
INSERT INTO system.help VALUES('ALTER DATABASE',5,'ALTER DATABASE DROP LOGFILE MEMBER ''<file>'';');

 

INSERT INTO system.help VALUES('ALTER DATABASE',10,'ALTER DATABASE RENAME GLOBAL_NAME TO <demo.world.muniqsoft.com>;');
INSERT INTO system.help VALUES('ALTER DATABASE',11,'ALTER DATABASE CHARACTER SET <charset>;');
INSERT INTO system.help VALUES('ALTER DATABASE',12,'ALTER DATABASE NATIONAL CHARACTER SET <charset>;');
INSERT INTO system.help VALUES('ALTER DATABASE',13,'ALTER DATABASE OPEN RESETLOGS;');

 

REM RECOVERY
INSERT INTO system.help VALUES('RECOVER',100,'REM +++ Muniqsoft GmbH www.muniqsoft-training.de Tel.: 089/67909040 +++');
INSERT INTO system.help VALUES('RECOVER',101,'RECOVER DATABASE [USING BACKUP CONTROLFILE];');
INSERT INTO system.help VALUES('RECOVER',102,'RECOVER DATABASE UNTIL CANCEL [USING BACKUP CONTROLFILE];');
INSERT INTO system.help VALUES('RECOVER',103,'RECOVER DATABASE UNTIL TIME ''YYYY-MM-DD:HH24:MI:SS''[USING BACKUP CONTROLFILE];');
INSERT INTO system.help VALUES('RECOVER',104,'RECOVER DATABASE UNTIL CHANGE <nr>;');

Wie wäre es mit allen undokumentierten Parametern mit Beschreibung (in zwei Zeilen wegen der Länge):

connect sys/<pwd>@db as sysdba

 

INSERT INTO system.help
SELECT  b.ksppinm ,1,b.ksppinm||'='||a.ksppstvl||' (Def:'||a.ksppstdf||')'
FROM sys.x$ksppi b, sys.x$ksppcv a
WHERE a.indx = b.indx
AND substr(b.ksppinm,1,1)='_';

 

INSERT INTO system.help
SELECT b.ksppinm,2,'Info: '||substr(b.ksppdesc,1,130)
FROM sys.x$ksppi b, sys.x$ksppcv a
WHERE a.indx = b.indx
AND substr(b.ksppinm,1,1)='_';

Aber es geht noch besser: Wenn wir dynamische Infos wie z. B. aus V$SESSION oder DBA_USER oder ... mit der Hilfe anzeigen wollen, dann verschieben Sie die Ursprungstabelle help in eine Tabelle help_tab und konsolidieren alle Informationen in einer View:

GRANT SELECT ON dba_users TO system;
GRANT SELECT ON dba_data_files TO system;
GRANT SELECT ON sys.v_$session TO system;
GRANT SELECT ON system.help TO <user>; -- Geben Sie den Benutzer an, der mit dem HELP Befehl arbeiten soll. Dieser muss dann auch auf die obigen Views Rechte bekommen.
ALTER TABLE SYSTEM.help RENAME TO help_tab;
CREATE OR REPLACE VIEW system.help
AS
SELECT * FROM system.help_tab
UNION ALL /* ++++ Benutzer ++++++ */
SELECT 'USERS',0,rpad('ID',3,' ')||' '||rpad('USER',25,' ')||rpad(' Account Status',19,' ')
||rpad('DEF TBS',27,' ')||rpad('TMP TBS',18,' ') FROM DUAL
UNION ALL
SELECT 'USERS',0,rpad('-',3,'-')||' '||rpad('-',25,'-')||' '||rpad('-',17,'-')||' '||
rpad('-',26,'-')||' '||rpad('-',18,'-') FROM DUAL
UNION ALL
select 'USERS',user_id+1,rpad(user_id,3,' ')||' '||rpad(username,25,' ')||' '||rpad(account_status,17,' ')||
rpad(default_tablespace,18,' ')||temporary_tablespace
from dba_users
UNION ALL /* +++++ Tablespace und deren Größe ++++++*/
SELECT 'TABLESPACES',0,rpad('TABLESPACE',22,' ')||' F_id Fileame (Groesse)' FROM DUAL
UNION ALL
SELECT 'TABLESPACES',0,rpad('-',22,'-')||' '||rpad('-',2,'-')||' '||rpad('-',62,'-') FROM DUAL
UNION ALL
SELECT 'TABLESPACES',file_id,rpad(tablespace_name,25,' ')||' '||file_id||' '||file_name||' ('||
round(bytes/1024/1024,2)||' MB)'  FROM dba_data_files
UNION ALL /* +++++++ Sessions +++++++++ */
SELECT 'SESSIONS',0,rpad('USER',12,' ')||' Status          SQLID            Last CALL        EVENT' FROM DUAL
UNION ALL
SELECT 'SESSIONS',0,rpad('-',12,'-')||' '||rpad('-',15,'-')||' '||rpad('-',15,'-')
||' '||rpad('-',16,'-')||' '||rpad('-',34,'-') FROM DUAL
UNION ALL
select 'SESSIONS',sid,rpad(username,12,' ')||' Status:'||rpad(status,8,' ')||' SQLID:'||rpad(nvl(sql_id,'-'),10,' ')
||' Last Call:'||rpad(last_call_et,6,' ')||' Event:'||event from v$session
where type='USER' and username is not null
;

Das testen wir z. B. mit:

SQL> help sessions
USER  Status          SQLID            Last CALL        EVENT
----- --------------- --------------- ---------------- ----------------------------------
SYS   Status:ACTIVE   SQLID:b9jqaugh1w Last Call:0      Event:SQL*Net message to client
SYS   Status:INACTIVE SQLID:-          Last Call:75900  Event:SQL*Net message from client
SQL> help users
ID  USER    Account Status    DEF TBS         TMP TBS
--- ------- ----------------- --------------- ---------
0   SYS     OPEN              SYSTEM          TEMP
7   AUDSYS  EXPIRED & LOCKED  USERS           TEMP
8   SYSTEM  EXPIRED(GRACE)    SYSTEM          TEMP
13  OUTLN   EXPIRED           SYSTEM          TEMP
SQL> help tablespaces
TABLESPACE   F_id Fileame (Groesse)
------------ -- -------------------------------------------------
SYSTEM          1 D:\ORACLE\ORADATA\O19C\SYSTEM01.DBF (700 MB)
SYSAUX          3 D:\ORACLE\ORADATA\O19C\SYSAUX01.DBF (650 MB)
UNDOTBS1        5 D:\ORACLE\ORADATA\O19C\UNDOTBS01.DBF (630 MB)
USERS           6 D:\ORACLE\ORADATA\O19C\USERS01.DBF (5 MB)
KUNDEN_TBS      12 D:\ORACLE\ORADATA\O19C\KUNDEN01.DBF (6144 MB)

 

Weitere Ideen zu diesem Thema lernen Sie in unserem SQL*Plus Tageskurs. Sie können das Konzept natürlich selbst nach Belieben erweitern, z. B. SQL Area Befehle anhand der SQL_ID ausgeben oder alle Locks anzeigen. Wenn Sie weitere Punkte integriert haben, schicken Sie sie uns doch bitte, dann machen wir nochmal einen Bonus Track zu diesem Tipp.

 



Weitere Interessente Artikel zum Thema:


Empfohlene Schulungen zum Thema:


Autoincrement Spalten in 12c (Identity)

Bereich:SQL, Version: ab APEX 5.x, Letzte Überarbeitung: 29.06.2018

Keywords:SQL

Lange haben wir darauf gewartet, endlich ist sie da! Oracle 12c! Das "c" steht für Cloud.
Wir wollen in dieser Reihe einige neue Features vorstellen, die wir natürlich auch in unserem Oracle 12c Kurs ausführlich besprechen.

Oracle 12c: Identity Spalten
Der SQL Server kann es schon seit einiger Zeit, Oracle nun auch ab Version 12c.
Eine Primärschlüsselspalte kann automatisch mit einem Wert gefüllt werden. Einfachster Fall, Sequenz soll bei 1 starten, Schrittweite 1:

CREATE TABLE t (
id    NUMBER GENERATED AS IDENTITY PRIMARY KEY,
text  VARCHAR2(10));
INSERT INTO t (text) VALUES ('X');
SELECT * from t;
ID TEXT     
-- ----------
1   X


Oder etwas aufwendiger mit Startwert 100 und Schrittweite 10 (aber ohne Primärschlüssel):

CREATE TABLE t (
id    NUMBER GENERATED BY DEFAULT AS IDENTITY
       (START WITH 100 INCREMENT BY 10),
text  VARCHAR2(10));


Möchten Sie eine bestehende Identity löschen ?

ALTER TABLE t MODIFY
(id DROP IDENTITY);


Auch ein nachträgliches Setzen der Identity ist möglich, mit neuer Spalte:

ALTER TABLE T ADD (ID_NEU NUMBER GENERATED AS IDENTITY
(START WITH 100));


Wenn ein bestehender Sequenzwert um 100 erhöht werden soll, kann man die Identity auf der Spalte löschen und einfach neu setzen.

Sie können pro Tabelle nur eine Identity Spalte festlegen. Wird die Identity Klausel verwendet, dann muss der Datentyp in der Spaltendefinition numerisch sein. Ein benutzerdefinierter Datentyp kann nicht spezifiziert werden.

Sie müssen nicht zwingend eine Primärschlüsselspalte für die Identity verwenden.
Das Setzen eines Defaultwertes in der Spaltendefinition ist bei der Verwendung der Identity Klausel nicht erlaubt. Wird die Identity Klausel verwendet, so wird automatisch ein NOT NULL Constraint auf die Spalte gesetzt.

Wie funktionierts ?
Oracle legt eine Sequenz im Schema der Tabelle an (z. B. mit Namen ISEQ$$_12345 und als Default mit Cache=20). Einen Insert-Trigger haben wir jedoch nicht entdeckt.

Sie können feststellen, welche Tabelle Identity Spalten verwendet:

SELECT owner, table_name, has_identity
FROM all_tables
WHERE table_name='T';
        
OWNER  TABLE_ HAS_IDENTITY
------ ------ ------------
SCOTT  T      YES    


Oder wenn Sie wissen wollen, welche Spalte vom Typ Identity ist:

SELECT owner,table_name, column_name, identity_column
FROM all_tab_columns
WHERE table_name='T';


Ausführliche Optionssyntax:

…( START WITH ( <int> | LIMIT VALUE ) | INCREMENT BY <int>|
( MAXVALUE integer | NOMAXVALUE ) |
( MINVALUE integer | NOMINVALUE ) |
( CYCLE | NOCYCLE ) |
( CACHE integer | NOCACHE ) |
( ORDER | NOORDER ) }


Komplexes Beispiel:

CREATE TABLE T (ID NUMBER
GENERATED AS IDENTITY (START WITH 1000 INCREMENT
BY 10 MAXVALUE 10000 CYCLE CACHE 1000 ORDER),
text VARCHAR2(10));

 

>100 weitere neue Funktionen der Oracle 12c Version lernen Sie in unserem Oracle 12c Neuerungen Kurs kennen.



Weitere Interessente Artikel zum Thema:



Empfohlene Schulungen zum Thema:


Abhängige Objekte einer Tabelle anzeigen bzw. neu erstellen

Bereich:DBA, Version: ab RDBMS 12.x, Letzte Überarbeitung: 29.02.2024

Keywords:DBA, SQL, Views

An einer Tabelle hängen viele schöne Objekte, die man aber auf den ersten Blick gar nicht sieht.

So gibt es zum Beispiel:

  • Indizes
  • Constraints
  • Kommentare
  • Synonyme
  • Sequenzen
  • Trigger
  • Rechte
  • Audits
  • Statistiken
  • Materialized Views
  • Annotations (ab 23c)

Sequenzen stehen eigentlich nicht mit einer Tabelle in direktem Zusammenhang. Wenn sie aber in einem Trigger referenziert werden, der auf der Tabelle basiert, kann man (meistens) davon ausgehen, dass sie zum Füllen der Primärschlüsselspalte verwendet werden. Sequenzen, die durch die Applikation direkt aufgerufen werden, kann man leider keiner Tabelle zuordnen.

Der folgende Select zeigt Ihnen eine Zusammenfassung aller Objekte, die mit einer gegebenen Tabelle in Verbindung stehen.
In der ersten Zeile (WITH t ...) gibt man einfach den Benutzernamen und den Tabellennamen ein.
Hier wurde die Tabelle emp des Benutzers Scott verwendet:

 WITH t AS (SELECT UPPER('&username') as tab_owner,
                   UPPER('&tabname') as tab_name
            FROM dual)
  SELECT 'Index:' as Object, index_name AS Name, index_type as Typ, status
  FROM all_indexes, t
  WHERE owner = t.tab_owner
  AND table_name = t.tab_name
UNION ALL
  SELECT 'Constraints:' , constraint_name, constraint_type, null
  FROM all_constraints, t
  WHERE owner = t.tab_owner
  AND table_name = t.tab_name
UNION ALL
  SELECT 'Trigger:' , trigger_name, trigger_type, status
  FROM all_triggers, t
  WHERE owner = t.tab_owner
  AND table_name = t.tab_name
UNION ALL
  SELECT 'Privilege:', 'Von:'||grantor||' An:'||grantee, privilege, null
  FROM all_tab_privs, t
  WHERE grantor = t.tab_owner
  AND table_name = t.tab_name
UNION ALL
  SELECT 'Roles:', 'Spalte:'||column_name, privilege , null
  FROM role_tab_privs, t
  WHERE owner = t.tab_owner
  AND table_name = t.tab_name
UNION ALL
  SELECT 'Synonyme:', 'Owner:'||owner, synonym_name , null
  FROM all_synonyms, t
  WHERE owner = t.tab_owner
  AND table_name = t.tab_name
UNION ALL
  SELECT 'Mat. Views:', mv.mview_name, mv.refresh_mode||':'||
           mv.build_mode, mv.compile_state
  FROM all_mview_detail_relations amd, all_mviews mv, t
  WHERE amd.owner = mv.owner
  AND amd.mview_name = mv.mview_name
  AND amd.detailobj_owner = t.tab_owner
  AND amd.detailobj_alias = t.tab_name
UNION ALL -- Sequences (die vom Trigger der Tabelle referenziert werden)
  SELECT 'Sequence :', referenced_name , 'Ref by:'||tr.trigger_name, null
  FROM all_dependencies d, all_triggers tr, t
  WHERE d.owner = tr.owner
  AND d.name = tr.trigger_name
  AND type = 'TRIGGER'
  AND referenced_type = 'SEQUENCE'
  AND tr.table_name = t.tab_name
  AND tr.table_owner = t.tab_owner;

  Wenn Sie schon die Oracle Version 23c besitzen können Sie auch noch Annotations einer Tabelle anzeigen. Dann entfernen Sie bitte im obigen Select das letzte “;” und hängen folgenden Teil dran:

UNION ALL
SELECT 'Annotations',a.column_name, a.annotation_name, a.annotation_value
FROM all_annotations_usage a,t
WHERE a.owner=t.tab_owner
AND a.object_name = t.tab_name
ORDER BY column_name NULLS FIRST,domain_name NULLS FIRST,annotation_name;
Geben Sie einen Wert für username ein: scott
Geben Sie einen Wert für tabname ein: emp
OBJECT       NAME                 TYP                  STATUS
------------ -------------------- -------------------- ----------
Index:       PK_EMP               NORMAL               VALID
Constraints: FK_DEPTNO            R
Constraints: PK_EMP               P
Trigger:     EMP_PK_TRIG          BEFORE EACH ROW      ENABLED
Privilege:   Von:SCOTT An:HR      DELETE
Privilege:   Von:SCOTT An:HR      UPDATE
Sequence :   EMP_SEQ              Ref by:EMP_PK_TRIG

Und weil wir gerade warm gelaufen sind, wäre es doch praktisch, die Statements für alle Objekte zu erzeugen, nur für den Fall, dass man mal ein Objekt verliert oder neu erstellen möchte.
Auch dafür kann man einen SELECT schreiben.

Zunächst sorgen wir dafür, dass jeder der erzeugten DDL-Befehle mit einem Semikolon abgeschlossen und die Storage-Klausel nicht mit ausgegeben wird...

BEGIN    
    DBMS_METADATA.SET_TRANSFORM_PARAM(
           DBMS_METADATA.SESSION_TRANSFORM, 'SQLTERMINATOR', true);
    DBMS_METADATA.SET_TRANSFORM_PARAM(
           DBMS_METADATA.SESSION_TRANSFORM, 'STORAGE', false);
END;

... und benutzen das Package DBMS_METADATA für die Erstellung der nötigen SQL-Befehle:

WITH s as (SELECT UPPER('&username') as tab_owner,
                  UPPER('&tabname') as tab_name
           FROM dual)
 SELECT -- Indizes
        DBMS_METADATA.GET_DDL(
           object_type => 'INDEX',
                schema => i.owner,
                  name => i.index_name) sql_stmts
  FROM s, all_indexes i
  WHERE owner   = s.tab_owner
  AND table_name = s.tab_name
UNION ALL -- Constraints
  SELECT  DBMS_METADATA.GET_DDL(
           object_type => (CASE WHEN a.constraint_type= 'R'
                                THEN 'REF_CONSTRAINT'
                                ELSE 'CONSTRAINT'
                           END),
                  name => a.constraint_name,
                schema => a.owner)
  FROM s, all_constraints a
  WHERE owner    = s.tab_owner
  AND table_name = s.tab_name
UNION ALL -- Trigger
  SELECT DBMS_METADATA.GET_DDL(
           object_type => 'TRIGGER',
                  name => t.trigger_name,
                schema => t.owner)
  FROM s, all_triggers t
  WHERE owner = tab_owner
  AND table_name = tab_name
UNION ALL -- Rechte
  SELECT DBMS_METADATA.GET_DEPENDENT_DDL(
                 object_type => 'OBJECT_GRANT',
            base_object_name => s.tab_name,
          base_object_schema => s.tab_owner)
  FROM s, all_tab_privs a
  WHERE a.grantor = s.tab_owner
  AND a.table_name = s.tab_name
UNION ALL -- Synonyme
  SELECT DBMS_METADATA.GET_DDL(
          object_type => 'SYNONYM',
                 name => sy.synonym_name,
               schema => sy.owner)
  FROM s, all_synonyms sy
  WHERE (owner = s.tab_owner or owner = 'PUBLIC')
  AND table_name = s.tab_name
UNION ALL -- MV
  SELECT DBMS_METADATA.GET_DDL (
          object_type => 'MATERIALIZED_VIEW',
                 name => mdr.mview_name,
               schema => mdr.owner)
  FROM s, ALL_MVIEW_DETAIL_RELATIONS mdr
  WHERE detailobj_owner = s.tab_owner
  AND detailobj_name = s.tab_name AND ROWNUM = 1
UNION ALL -- Audit
  SELECT DBMS_METADATA.GET_DEPENDENT_DDL(
              object_type => 'AUDIT_OBJ',
         base_object_name => s.tab_name,
       base_object_schema => s.tab_owner)
  FROM s, dba_obj_audit_opts oao
  WHERE oao.object_name = s.tab_name
  AND oao.owner = s.tab_owner
  AND ROWNUM = 1
UNION ALL -- Tabellenkommentare
  SELECT TO_CLOB('COMMENT ON TABLE '||table_name||
                   q'[ IS ']'||atc.comments||''';')
  FROM s, all_tab_comments atc
  WHERE owner    = tab_owner
  AND table_name = tab_name
  AND comments IS NOT NULL
UNION ALL -- Spaltenkommentare
  SELECT TO_CLOB('COMMENT ON COLUMN '||acc.table_name||'.'||acc.column_name||
            q'[ IS ']'||acc.comments||''';')
  FROM s, all_col_comments acc
  WHERE owner    = s.tab_owner
  AND table_name = s.tab_name
  AND comments IS NOT NULL
UNION ALL -- Sequenzen, die von Triggern der Tabelle referenziert werden
  SELECT DBMS_METADATA.GET_DDL(
             object_type => 'SEQUENCE',
                  schema => referenced_owner,
                    name => referenced_name)
  FROM all_dependencies d, all_triggers tr, s
  WHERE d.owner = tr.owner
  AND d.name = tr.trigger_name
  AND type = 'TRIGGER'
  AND referenced_type = 'SEQUENCE'
  AND tr.table_name = s.tab_name
  AND tr.table_owner = s.tab_owner
/
SQL_STMTS
--------------------------------------------------------------------------------
  CREATE UNIQUE INDEX "SCOTT"."PK_EMP" ON "SCOTT"."EMP" ("EMPNO")
  PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS
  TABLESPACE "USERS" ;
  ALTER TABLE "SCOTT"."EMP" ADD CONSTRAINT "FK_DEPTNO" FOREIGN KEY ("DEPTNO")
          REFERENCES "SCOTT"."DEPT" ("DEPTNO") ENABLE;
  ALTER TABLE "SCOTT"."EMP" ADD CONSTRAINT "PK_EMP" PRIMARY KEY ("EMPNO")
  USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS
  TABLESPACE "USERS"  ENABLE;
  CREATE OR REPLACE TRIGGER "SCOTT"."EMP_PK_TRIG"
BEFORE INSERT ON emp FOR EACH ROW
BEGIN
  :NEW.empno := emp_seq.NEXTVAL;
END;
/
ALTER TRIGGER "SCOTT"."EMP_PK_TRIG" ENABLE;
   CREATE SEQUENCE  "SCOTT"."EMP_SEQ"  MINVALUE 1 MAXVALUE 999999999999999999999
9999999 INCREMENT BY 10 START WITH 8200 CACHE 20 NOORDER  NOCYCLE ;

 

Anmerkungen:
Man kann leider nicht in jedem Fall auf die ALL_... Variante  der Data Dictionary Views zurückzugreifen, weil es z.B. keine ALL_OBJ_AUDIT_OPTS View gibt. Wenn man keine DBA Rechte hat, ersetzt man einfach den Bezug (DBA_OBJ_AUDIT_OPTS) durch USER_OBJ_AUDIT_OPTS und wirft den Filter "AND oao.owner=s.orig_tab_owner" weg.
Die Filter "AND rownum=1" sind nicht zum Spaß da. Da das Package dbms_metadata abstürzt, wenn man nicht vorhandene Audit-Informationen oder Rechte eines Objekts abfragen will, muss man erst mal prüfen, ob es da etwas gibt. Mit einer Zeile bekommt man dann aber alle Rechte/Audit-Einstellungen zurück :-)

Weitere Möglichkeiten, diese Funktionen auch in einer Online Reorg einzusetzen, lernen Sie bei uns im Reorg- und Wartungskurs sowie im Standard Edition Kurs kennen. Wir freuen uns auf Ihr Kommen!!



Weitere Interessente Artikel zum Thema:



Empfohlene Schulungen zum Thema:


RMAN Recover Szenarien inkl. Wechsel der Inkarnation

Bereich:DBA, Version: ab RDBMS 12.x, Letzte Überarbeitung: 29.06.2018

Keywords:DBA, Oracle Backup & Recovery

Sind Sie als DBA Ihrer Oracle Datenbank auch für das Backup und Recovery zuständig? Dann standen Sie vielleicht schon einmal vor dem Problem, Ihre Datenbank aufgrund von Benutzer- oder Dateifehlern zurücksetzen zu müssen. Passiert dies einmal, ist das zwar sehr ärgerlich, aber in Verbindung mit dem RMAN noch sehr unkompliziert. Kommen Ihre Entwickler aber häufiger mit dem Wunsch auf Sie zu, die (Entwickler- oder Test-) Datenbank doch noch weiter zurückzusetzen als beim letzten Mal oder den letzten OPEN RESETLOGS ganz ungeschehen zu machen, dann wird das Ganze schon trickreicher und komplizierter.


Im folgenden Artikel werden Ihnen einige mögliche Szenarien vorgestellt, mit denen Sie in diesem Zusammenhang evtl. einmal konfrontiert werden.

Eines noch vorneweg: Diese Szenarien wurden nicht extra für diesen Tipp erfunden und ersponnen, sondern es handelt sich dabei um reale Probleme, mit denen Kunden im Laufe der letzten zehn Jahre Schulungs- und Supporttätigkeit auf uns zugekommen sind.

Zur Ausgangssituation: Die Beispiel-Datenbank wird regelmäßig ONLINE mittels RMAN im NOCATALOG-Modus gesichert. Zielverzeichnis ist die Fast (Flash) Recovery Area, kurz FRA.
 

1. SZENARIO: ZURÜCKSETZEN DER DATENBANK AUF EINEN ÄLTEREN STAND

Um 08:30 kommt einer Ihrer Entwickler mit der Bitte zu Ihnen, den gestrigen Löschvorgang eines Applikationsschemas (HR) rückgängig zu machen. Dazu setzen Sie die Datenbank auf 15:25 des Vortags zurück.

RMAN> SHUTDOWN ABORT
...
RMAN> STARTUP MOUNT
...
RMAN> RUN {
      SET UNTIL TIME "to_date('29.04.2013 15:25:00','dd.mm.yyyy hh24:mi:ss')";
      RESTORE DATABASE;
      RECOVER DATABASE;}
...
RMAN> ALTER DATABASE OPEN RESETLOGS;

Datenbank geöffnet

Damit ist das komplette Schema HR wiederhergestellt. Allerdings sind (verständlicherweise) sämtliche Änderungen, die seit 15:25 durchgeführt worden sind, verloren.
 

2. SZENARIO: ERNEUTES ZURÜCKSETZEN DER DATENBANK AUF EINEN STAND VOR DEM OPEN RESETLOGS

Gegen 09:00 kommt der selbe Entwickler und benötigt in "seiner" Datenbank nun einen Stand von 10:15, der aber zwei Tage zurückliegt. 

Der erste Lösungsansatz, den Sie analog zum Szenario 1 versuchen, wird mit folgendem Fehler quittiert:

...
RMAN> RUN {
      SET UNTIL TIME "to_date('28.04.2013 10:15:00','dd.mm.yyyy hh24:mi:ss')";
      RESTORE DATABASE;
      RECOVER DATABASE;}

Befehl wird ausgeführt: SET UNTIL clause

Starten restore um 30.04.13 09:07:56
RMAN-00571: ===========================================================
RMAN-00569: =============== ERROR MESSAGE STACK FOLLOWS ===============
RMAN-00571: ===========================================================
RMAN-03002: Fehler bei restore Befehl auf 04/30/2013 09:07:56
RMAN-20207: UNTIL TIME or RECOVERY WINDOW is before RESETLOGS time

Das Problem liegt darin, dass der gewünschte Zeitpunkt aus einer alten Inkarnation stammt, in die Oracle beim RESTORE/RECOVER nicht automatisch wechseln kann. Die Auflistung der einzelnen Inkarnationen stellt sich wie folgt dar:

RMAN> LIST INCARNATION;

Liste mit Datenbankinkarnationen
DB Schl  Ink Schl DB-Name  DB-ID            STATUS Rücks. SCN  Rücks.zeit
------- ------- -------- ---------------- --- ---------- ----------
1       1       O11G     242551857        PARENT  1          03.11.11 05:39:09
2       2       O11G     242551857        PARENT  994063     02.02.12 10:20:36
3       3       O11G     242551857        PARENT  2524398    25.04.13 09:46:37
4       4       O11G     242551857        CURRENT 2652781    30.04.13 08:45:35

Die aktuelle Inkarnation ist die Nummer 4. Damit der Recovervorgang erfolgreich durchgeführt werden kann, muss zuerst auf die Inkarnation Nummer 3 zurückgesetzt werden. Somit ergibt sich als korrekte Lösung folgendes Vorgehen:

RMAN> SHUTDOWN ABORT
...
RMAN> STARTUP MOUNT
...
RMAN> RESET DATABASE TO INCARNATION 3;

Datenbank auf Version 3 zurückgesetzt

RMAN> LIST INCARNATION;

Liste mit Datenbankinkarnationen
DB Schl  Ink Schl DB-Name  DB-ID            STATUS Rücks. SCN  Rücks.zeit
------- ------- -------- ---------------- --- ---------- ----------
1       1       O11G     242551857        PARENT  1          03.11.11 05:39:09
2       2       O11G     242551857        PARENT  994063     02.02.12 10:20:36
3       3       O11G     242551857        CURRENT 2524398    25.04.13 09:46:37
4       4       O11G     242551857        ORPHAN  2652781    30.04.13 08:45:35

RMAN> RUN {      
      SET UNTIL TIME "to_date('28.04.2013 10:15:00','dd.mm.yyyy hh24:mi:ss')";
      RESTORE DATABASE;
      RECOVER DATABASE;}
...
RMAN> ALTER DATABASE OPEN RESETLOGS;

Datenbank geöffnet

RMAN> LIST INCARNATION;

Liste mit Datenbankinkarnationen
DB Schl  Ink Schl DB-Name  DB-ID            STATUS Rücks. SCN  Rücks.zeit
------- ------- -------- ---------------- --- ---------- ----------
1       1       O11G     242551857        PARENT  1          03.11.11 05:39:09
2       2       O11G     242551857        PARENT  994063     02.02.12 10:20:36
3       3       O11G     242551857        PARENT  2524398    25.04.13 09:46:37
4       4       O11G     242551857        ORPHAN  2652781    30.04.13 08:45:35
5       5       O11G     242551857        CURRENT 2619672    30.04.13 09:24:37

 

3. SZENARIO: NEUTRALISIEREN VON OPEN RESETLOGS VORGÄNGEN

Nachdem Sie auch das zweite Szenario erfolgreich beendet haben und dem Entwickler "seine" Datenbank auf den gewünschten Stand zurückgesetzt haben, kommt um 09:45 der Chef der Entwickler, dessen Arbeit der vergangenen zwei Tage Sie gerade zunichte gemacht haben, und verlangt umgehend die Wiederherstellung der Datenbank vom heutigen Stand 08:30.

Bevor Sie jetzt in Hektik ausbrechen, nehmen Sie zunächst einen (Data Pump) Export der aktuellen Datenbank bzw. einzelner Schemata vor. Damit können Sie am Schluss (hoffentlich) alle Entwickler zufriedenstellen.

Nun geht es darum, alle Spuren der OPEN RESETLOGS Vorgänge zu verwischen. Dazu wird ein altes Controlfile eingespielt, das vor dem ersten RESETLOGS von 08:45 erstellt worden sein muss. Gehen Sie in das Verzeichnis, in das die Backupsets der Controlfiles geschrieben werden (idealerweise das AUTOBACKUP Verzeichnis in der FRA) und verschieben Sie alle aktuelleren Backups in ein temporäres Verzeichnis. Je nach Version von Oracle, müssen Sie evtl. vor dem RESTORE CONTROLFILE noch die DBID setzen (falls notwendig, werden Sie aber dazu aufgefordert).

RMAN> STARTUP NOMOUNT
...
-- Verschieben der aktuellen Backupsets vom Controlfile
-- evtl. RMAN> SET DBID <dbid>
RMAN> RESTORE CONTROLFILE FROM AUTOBACKUP;

Starten restore um 30.04.13 09:55:16
Zugewiesener Kanal: ORA_DISK_1
Kanal ORA_DISK_1: SID=63 Device-Typ=DISK

Ziel von Recovery-Bereich: /u01/app/oracle/fast_recovery_area
Datenbankname (oder eindeutiger Datenbankname) für Suche benutzt: O11G
Kanal ORA_DISK_1: AUTOBACKUP /u01/app/oracle/fast_recovery_area/O11G/autobackup/2013_04_29/o1_mf_s_814008854_8qw8mpl7_.bkp in Recovery-Bereich gefunden
Suche nach AUTOBACKUP mit Format "%F" nicht versucht, weil DBID nicht festgelegt wurde
Kanal ORA_DISK_1: Kontrolldatei wird aus AUTOBACKUP /u01/app/oracle/fast_recovery_area/O11G/autobackup/2013_04_29/o1_mf_s_814008854_8qw8mpl7_.bkp zurückgeschrieben
Kanal ORA_DISK_1: Zurückschreiben der Kontrolldatei aus AUTOBACKUP abgeschlossen
Ausgabedateiname=/u01/app/oracle/oradata/o11g/control01.ctl
Ausgabedateiname=/u01/app/oracle/fast_recovery_area/O11G/control02.ctl
Beendet restore um 30.04.13 09:55:18

RMAN> ALTER DATABASE MOUNT;

Datenbank angeschlossen

Spätestens jetzt müssen Sie die restlichen Backupsets der Datendateien und der Archivelogs verschieben. Wenn es noch Original-Archivelogs aus den neuen Inkarnationen gibt, müssen auch diese entfernt werden. Wenn Sie daraufhin den RESTORE DATABASE Befehl anstoßen, werden nur die Backups aus der ursprünglichen Inkarnation katalogisiert. Die letzten beiden Inkarnationen sind damit nicht mehr existent.

-- Verschieben der aktuellen Backups inkl. Archivelogs
RMAN> RESTORE DATABASE;

Starten restore um 30.04.13 09:56:30
Starten implicit crosscheck backup um 30.04.13 09:56:30
Zugewiesener Kanal: ORA_DISK_1
Kanal ORA_DISK_1: SID=63 Device-Typ=DISK
7 Objekte auf Übereinstimmung geprüft
Beendet implicit crosscheck backup um 30.04.13 09:56:31

Starten implicit crosscheck copy um 30.04.13 09:56:31
Kanal ORA_DISK_1 wird benutzt
Beendet implicit crosscheck copy um 30.04.13 09:56:31

Suche nach allen Dateien im Recovery-Bereich
Dateien werden katalogisiert...
Katalogisierung erfolgt

Liste mit katalogisierten Dateien
=======================
Dateiname: /u01/app/oracle/fast_recovery_area/O11G/AUTOBACKUP/2013_04_29/O1_MF_S_814008854_8QW8MPL7_.BKP
Dateiname: /u01/app/oracle/fast_recovery_area/O11G/BACKUPSET/2013_04_29/O1_MF_ANNNN_TAG20130429T101534_8QWC16N4_.BKP

Kanal ORA_DISK_1 wird benutzt

Kanal ORA_DISK_1: Zurückschreiben von Datendatei-Backup Set beginnt
Kanal ORA_DISK_1: Datendatei(en) werden zum Wiederherstellen aus Backup Set angegeben
Kanal ORA_DISK_1: Datendatei 00001 wird zu /u01/app/oracle/oradata/o11g/system01.dbf wiederhergestellt
Kanal ORA_DISK_1: Datendatei 00002 wird zu /u01/app/oracle/oradata/o11g/sysaux01.dbf wiederhergestellt
Kanal ORA_DISK_1: Datendatei 00003 wird zu /u01/app/oracle/oradata/o11g/undotbs01.dbf wiederhergestellt
Kanal ORA_DISK_1: Datendatei 00004 wird zu /u01/app/oracle/oradata/o11g/users01.dbf wiederhergestellt
Kanal ORA_DISK_1: Datendatei 00005 wird zu /u01/app/oracle/oradata/o11g/example01.dbf wiederhergestellt
Kanal ORA_DISK_1: Lesen aus Backup Piece /u01/app/oracle/fast_recovery_area/O11G/backupset/2013_04_29/o1_mf_nnndf_tag20130429t093307_8qw8kn1z_.bkp
Kanal ORA_DISK_1: Piece Handle=/u01/app/oracle/fast_recovery_area/O11G/backupset/2013_04_29/o1_mf_nnndf_tag20130429t093307_8qw8kn1z_.bkp Tag=TAG20130429T093307
Kanal ORA_DISK_1: Backup Piece 1 wurde wiederhergestellt
Kanal ORA_DISK_1: Restore abgeschlossen, abgelaufene Zeit: 00:01:05
Beendet restore um 30.04.13 09:57:56

RMAN> RECOVER DATABASE;

Starten recover um 30.04.13 09:58:05
Kanal ORA_DISK_1 wird benutzt

Media Recovery starten

Kanal ORA_DISK_1: Zurückschreiben von Archive Log in Standardziel wird begonnen
Kanal ORA_DISK_1: Archive Log wird zurückgeschrieben
Archive Log Thread=1 Sequence=115
Kanal ORA_DISK_1: Lesen aus Backup Piece /u01/app/oracle/fast_recovery_area/O11G/backupset/2013_04_29/o1_mf_annnn_tag20130429t093413_8qw8mo7k_.bkp
Kanal ORA_DISK_1: Piece Handle=/u01/app/oracle/fast_recovery_area/O11G/backupset/2013_04_29/o1_mf_annnn_tag20130429t093413_8qw8mo7k_.bkp Tag=TAG20130429T093413
Kanal ORA_DISK_1: Backup Piece 1 wurde wiederhergestellt
Kanal ORA_DISK_1: Restore abgeschlossen, abgelaufene Zeit: 00:00:01
Archive Log-Dateiname=/u01/app/oracle/fast_recovery_area/O11G/archivelog/2013_04_30/o1_mf_1_115_8qz4yy1t_.arc Thread=1 Sequence=115
Kanal default: Archive Logs werden gelöscht
Archive Log-Dateiname=/u01/app/oracle/fast_recovery_area/O11G/archivelog/2013_04_30/o1_mf_1_115_8qz4yy1t_.arc RECID=11 STAMP=814103422
Kanal ORA_DISK_1: Zurückschreiben von Archive Log in Standardziel wird begonnen
Kanal ORA_DISK_1: Archive Log wird zurückgeschrieben
Archive Log Thread=1 Sequence=116
...
Kanal ORA_DISK_1: Archive Log wird zurückgeschrieben
Archive Log Thread=1 Sequence=122
Kanal ORA_DISK_1: Lesen aus Backup Piece /u01/app/oracle/fast_recovery_area/O11G/backupset/2013_04_29/o1_mf_annnn_tag20130429t101534_8qwc16n4_.bkp
Kanal ORA_DISK_1: Piece Handle=/u01/app/oracle/fast_recovery_area/O11G/backupset/2013_04_29/o1_mf_annnn_tag20130429t101534_8qwc16n4_.bkp Tag=TAG20130429T101534
Kanal ORA_DISK_1: Backup Piece 1 wurde wiederhergestellt
Kanal ORA_DISK_1: Restore abgeschlossen, abgelaufene Zeit: 00:00:01
Archive Log-Dateiname=/u01/app/oracle/fast_recovery_area/O11G/archivelog/2013_04_30/o1_mf_1_116_8qz4yzfy_.arc Thread=1 Sequence=116
Kanal default: Archive Logs werden gelöscht
...
Archive Log-Dateiname=/u01/app/oracle/fast_recovery_area/O11G/archivelog/2013_04_30/o1_mf_1_122_8qz4yzfh_.arc Thread=1 Sequence=122
Kanal default: Archive Logs werden gelöscht
Archive Log-Dateiname=/u01/app/oracle/fast_recovery_area/O11G/archivelog/2013_04_30/o1_mf_1_122_8qz4yzfh_.arc RECID=18 STAMP=814103423
Media Recovery abgeschlossen, abgelaufene Zeit: 00:00:02
Beendet recover um 30.04.13 09:59:49

RMAN> RECOVER DATABASE;

Starten recover um 30.04.13 09:58:05
Kanal ORA_DISK_1 wird benutzt

Media Recovery starten

Kanal ORA_DISK_1: Zurückschreiben von Archive Log in Standardziel wird begonnen
Kanal ORA_DISK_1: Archive Log wird zurückgeschrieben
Archive Log Thread=1 Sequence=115
Kanal ORA_DISK_1: Lesen aus Backup Piece /u01/app/oracle/fast_recovery_area/O11G/backupset/2013_04_29/o1_mf_annnn_tag20130429t093413_8qw8mo7k_.bkp
Kanal ORA_DISK_1: Piece Handle=/u01/app/oracle/fast_recovery_area/O11G/backupset/2013_04_29/o1_mf_annnn_tag20130429t093413_8qw8mo7k_.bkp Tag=TAG20130429T093413
Kanal ORA_DISK_1: Backup Piece 1 wurde wiederhergestellt
Kanal ORA_DISK_1: Restore abgeschlossen, abgelaufene Zeit: 00:00:01
Archive Log-Dateiname=/u01/app/oracle/fast_recovery_area/O11G/archivelog/2013_04_30/o1_mf_1_115_8qz4yy1t_.arc Thread=1 Sequence=115
Kanal default: Archive Logs werden gelöscht
Archive Log-Dateiname=/u01/app/oracle/fast_recovery_area/O11G/archivelog/2013_04_30/o1_mf_1_115_8qz4yy1t_.arc RECID=11 STAMP=814103422
Kanal ORA_DISK_1: Zurückschreiben von Archive Log in Standardziel wird begonnen
Kanal ORA_DISK_1: Archive Log wird zurückgeschrieben
Archive Log Thread=1 Sequence=116
...
Kanal ORA_DISK_1: Archive Log wird zurückgeschrieben
Archive Log Thread=1 Sequence=122
Kanal ORA_DISK_1: Lesen aus Backup Piece /u01/app/oracle/fast_recovery_area/O11G/backupset/2013_04_29/o1_mf_annnn_tag20130429t101534_8qwc16n4_.bkp
Kanal ORA_DISK_1: Piece Handle=/u01/app/oracle/fast_recovery_area/O11G/backupset/2013_04_29/o1_mf_annnn_tag20130429t101534_8qwc16n4_.bkp Tag=TAG20130429T101534
Kanal ORA_DISK_1: Backup Piece 1 wurde wiederhergestellt
Kanal ORA_DISK_1: Restore abgeschlossen, abgelaufene Zeit: 00:00:01
Archive Log-Dateiname=/u01/app/oracle/fast_recovery_area/O11G/archivelog/2

Parallelisierung von DML - Operatoren mit DBMS_Parallel_Execute ab 11.2

Bereich:PL/SQL, Version: ab RDBMS 11.2, Letzte Überarbeitung: 05.08.2023

Keywords:PL/SQL, Standard Packages

Die Parallelisierung von größeren DELETE- und UPDATE-Aktionen bietet diverse Vorteile:

  • Die Performance wird durch den parallelen Zugriff erhöht.
  • Es wird weniger Platz in den Undo-Segmenten verbraucht, da die Transaktion auf mehrere kleinere Einheiten verteilt wird.
  • Die Tabellen sind nicht so lange offline, da die Sperren schneller aufgehoben werden können.


Wenn man selber prozedurale Lösungen zum parallelen Löschen oder Aktualisieren großer Datensatzmengen erstellen will, wird das allerdings schnell mühsam, weil man erst einmal sicherstellen muss, dass die Pakete so aufgeteilt werden, dass sich die einzelnen Transaktionen nicht gegenseitig behindern.
Ab der Oracle Version 11 Release 2 gibt es eine Oracle-eigene Lösung für die Parallelisierung von DML-Aktionen über den Scheduler, die den Anwendern sehr viel Arbeit abnehmen kann, das Package DBMS_PARALLEL_EXECUTE.
In diesem Fall werden die Tabellen über einen DBMS_SCHEDULER-Prozess automatisch in Teilbereiche, sog. Chunks, unterteilt (die aber nichts mit den Chunks bei der Speicherung von LOBs zu tun haben). Auf diese Abschnitte werden die DML-Befehle parallel abgesetzt. Ein COMMIT erfolgt nach jeder Fertigstellung des DML-Befehls auf dem jeweiligen Tabellenbereich. Dadurch werden die Undo-Segmente weniger stark belastet.
Um das Fehlerlogging und automatische Wiederholungen im Fehlerfall kümmert sich ebenfalls der Scheduler.
Die einzige Voraussetzung für die Nutzung ist das CREATE JOB-Recht. Das Package an sich ist an Public gegrantet. Im Gegensatz zu den meisten anderen Optionen, die mit Parallelisierung zu tun haben, ist die Nutzung des Packages nicht auf die Enterprise Edition beschränkt!!
Das Package besteht aus folgenden Prozeduren, die alle einen Commit beinhalten.

  • CREATE_TASK erstellt einen Auftrag für die parallele Abarbeitung.

 

  • Über CREATE_CHUNKS_BY_ROWID kann man die Teilbereiche der Tabelle über die  ROWID definieren. Diese Unterteilung ist die unkomplizierteste Methode. Die Abschnitte überlappen sich garantiert nicht, insofern hat man auch keine Probleme mit Sperren und die Tabelle muss für das Chunking nicht extra abgefragt werden, da die Informationen über die ROWIDs bequem aus dem Data Dictionary bezogen werden können.

 

  • Wenn man die Aufteilung der Tabelle über ein SQL-Statement bestimmen will, benutzt man CREATE_CHUNKS_BY_SQL.
  • Auch eine Stückelung anhand der Werte einer numerischen Spalte ist möglich, die entsprechende Prozedur heißt CREATE_CHUNKS_BY_NUMBER_COL.
  • Nach dem Erstellen der Chunks startet man die Abarbeitung über die Prozedur RUN_TASK.
  • TASK_STATUS liefert Informationen über den Status.
  • STOP_TASK würgt den Auftrag ab.
  • RESUME_TASK startet die Wiederaufnahme.
  • DROP_TASK löscht den Task.


Die Funktion TASK_STATUS kann zum Monitoren und zur Fehlerbehandlung genutzt werden.

 

BEISPIEL: PARALLELER UPDATE, EINTEILUNG ÜBER DIE ROWID

 


Probieren's wir mal aus. Für den ersten Test nehmen wir Tom Kytes bewährte Tabelle Initiates file downloadbig_tab mit 2 Millionen Datensätzen. Weil wir später die Session_ids bei der parallelen Ausführung ermitteln wollen, benennen wir die data_object_id-Spalte in session_id um.

Zunächst braucht Scott das Create Job-Recht:

conn / as sysdba
GRANT CREATE JOB TO scott;
conn scott/tiger
@ d:/create_bigtab.sql
ALTER TABLE big_tab RENAME COLUMN data_object_id TO session_id;

Jetzt kann Scott einen Task erstellen. Der Taskname ist einfach ein VARCHAR2-Parameter. Er muss nicht den Regeln für Oracle-Bezeichner entsprechen:

BEGIN
   DBMS_PARALLEL_EXECUTE.create_task (task_name => 'update big_tab, chunks by rowid');
END;

Ob die Erstellung geklappt hat, kann man über die View USER_PARALLEL_EXECUTE_TASKS herausfinden:

col task_name for a50
SELECT task_name, status FROM user_parallel_execute_tasks;
=>
TASK_NAME                             STATUS
------------------------------------- ----------
update big_tab, chunks by rowid       CREATED

Falls einem grad kein passender Name einfällt, kann man die Funktion GENERATE_TASK_NAME verwenden, die eine interne Sequenz hochzählt:

SELECT DBMS_PARALLEL_EXECUTE.generate_task_name FROM dual;
=> TASK$_1

Der nächste Schritt ist die Unterteilung der Tabelle. Wenn der Parameter by_row auf TRUE gesetzt wird, bezieht sich die chunk_size auf die Anzahl der Zeilen, wenn er auf FALSE steht, auf die Anzahl der Blöcke.
Optimale Werte für die chunk_size muss man selber ermitteln. Je kleiner die chunk_size, desto schneller sind die Tabellenabschnitte wieder frei von Sperren:

BEGIN
  DBMS_PARALLEL_EXECUTE.create_chunks_by_rowid(
          task_name => 'update big_tab, chunks by rowid',
        table_owner => 'SCOTT',
         table_name => 'BIG_TAB',
             by_row => TRUE,
         chunk_size => 10000);
END;
/

 

SELECT task_name, status FROM user_parallel_execute_tasks;
TASK_NAME                             STATUS
------------------------------------- -------------
update big_tab, chunks by rowid       CHUNKED

Genauere Informationen über die einzelnen Abschnitte liefert die View USER_PARALLEL_EXECUTE_CHUNKS:

SELECT chunk_id, status, start_rowid, end_rowid
FROM   user_parallel_execute_chunks
WHERE  task_name = 'update big_tab, chunks by rowid'
ORDER BY chunk_id;
=>
CHUNK_ID STATUS               START_ROWID        END_ROWID
-------- -------------------- ------------------ ------------------
       2 UNASSIGNED           AAAVbmAAEAAAGegAAA AAAVbmAAEAAAGenCcP
       3 UNASSIGNED           AAAVbmAAEAAAGeoAAA AAAVbmAAEAAAGevCcP
       4 UNASSIGNED           AAAVbmAAEAAAGewAAA AAAVbmAAEAAAGe3CcP
       5 UNASSIGNED           AAAVbmAAEAAAGe4AAA AAAVbmAAEAAAGe/CcP
       6 UNASSIGNED           AAAVbmAAEAAAGfAAAA AAAVbmAAEAAAGfHCcP
....
331 Zeilen ausgewählt.

Jetzt folgt die eigentliche Ausführung:
In diesem Beispiel werden 10 parallele Prozesse (Scheduler-Jobs) gestartet, die sich jeweils einen der nicht zugeordneten (unassigned) Abschnitte vornehmen, die über den Parameter sql_stmt vorgegebene DML-Anweisung durchführen, festschreiben und zum nächsten freien Chunk übergehen. Wenn man z. B. nur 4 Prozessoren hat, laufen die Jobs natürlich teilweise seriell.
Der Quotation-Operator (q'[...]') erlaubt die Schreibweise ohne Maskierung der inneren Hochkommata:

DECLARE
  l_stmt VARCHAR2(32767);
BEGIN
  l_stmt := q'[UPDATE big_tab
               SET data_object_id = sys_context('userenv', 'sessionid'),
                      object_type = object_type||'*',
                          created = sysdate
                 WHERE rowid BETWEEN :start_id AND :end_id]';
  DBMS_PARALLEL_EXECUTE.run_task(
           task_name => 'update big_tab, chunks by rowid',
            sql_stmt => l_stmt,
       language_flag => DBMS_SQL.NATIVE,
      parallel_level => 10);
END;
/
Abgelaufen: 00:00:48.98

Die Bindvariablen :start_id and :end_id beziehen sich auf die erste bzw. letzte Rowid jedes Chunks.
Der Eintrag der Session_id über die Sys_constext-Funktion ermöglicht auch nachträglich, festzustellen, welcher Anteil der Zeilen in welcher der parallelen Sessions erledigt wurde:

SELECT data_object_id session_id, COUNT(*)
FROM   big_tab
GROUP BY data_object_id
ORDER BY data_object_id;
=>
SESSION_ID   COUNT(*)
---------- ----------
   7491384     279693
   7491385     283494
   7491386     273875
   7491387     260867
   7491388     141022
   7491389     143615
   7491390     153054
   7491391     154875
   7491392     161342
   7491393     148163

 

STATUS INFORMATIONEN UND DIAGNOSE-MÖGLICHKEITEN


Ob alles glatt gegangen ist, erfährt man über die Data Dictionary-Views:

SELECT status,
       job_prefix, -- für die parallelen Scheduler-Jobs
       chunk_type,
       -- sql_stmt,
       parallel_level
FROM user_parallel_execute_tasks
WHERE TASK_NAME = 'update big_tab, chunks by rowid';
=>
STATUS     JOB_PREFIX     CHUNK_TYPE   PARALLEL_LEVEL
---------- -------------- ------------ --------------
FINISHED   TASK$_241      ROWID_RANGE              10
SELECT status, COUNT(*)
FROM user_parallel_execute_chunks
GROUP BY status;
STATUS                 COUNT(*)
-------------------- ----------
PROCESSED                   331

Aufschlussreich ist auch die Scheduler-DD-View user_scheduler_job_run_details. Hierfür muss man nur das Job-Präfix aus der View user_parallel_execute_tasks für die Job-Namen einsetzen:

SELECT job_name,
       status,
       error#,
       SUBSTR(run_duration, INSTR(run_duration,':')+1) "Dauer [Sek]",
       REGEXP_SUBSTR(actual_start_date, '[^ ]+') uhrzeit
FROM user_scheduler_job_run_details
WHERE job_name LIKE (SELECT job_prefix || '%'
                     FROM user_parallel_execute_tasks
                     WHERE task_name = 'update big_tab, chunks by rowid');
=>
JOB_NAME       STATUS         ERROR# Dauer [Sek]    UHRZEIT
-------------- ---------- ---------- -------------- ----------------
TASK$_241_6    SUCCEEDED           0 00:40          17:46:52,578000
TASK$_241_9    SUCCEEDED           0 00:40          17:46:52,781000
TASK$_241_1    SUCCEEDED           0 00:46          17:46:46,781000
TASK$_241_10   SUCCEEDED           0 00:40          17:46:52,843000
TASK$_241_2    SUCCEEDED           0 00:46          17:46:46,890000
TASK$_241_3    SUCCEEDED           0 00:46          17:46:46,953000
TASK$_241_4    SUCCEEDED           0 00:46          17:46:47,109000
TASK$_241_7    SUCCEEDED           0 00:40          17:46:52,656000
TASK$_241_8    SUCCEEDED           0 00:40          17:46:52,718000
TASK$_241_5    SUCCEEDED           0 00:40          17:46:52,578000

Nach der erfolgreichen Ausführung kann man den Task löschen:

BEGIN
   DBMS_PARALLEL_EXECUTE.drop_task('test_task');
END;

FEHLERLOGGING


Was macht man, wenn bei der Prozessierung Fehler auftreten? Wir bauen ein paar Fallen in die Tabelle ein. Die Spalte object_type ist vom Datentyp VARCHAR2(19). Während des Updates wird der Wert mit einem Sternchen konkateniert, also muss ich nur ein paar Einträge verlängern, so dass es während der DML-Aktion kracht:

UPDATE big_tab SET object_type = RPAD(object_type, 19, '+') WHERE MOD(id, 43) = 0;
=> 46511 Zeilen wurden aktualisiert.

In der PL/SQL Packages and Types Reference  findet sich ein Beispiel für die Ausführung der Prozedur incl. Fehlerbehandlung. Statt RUN_TASK wird hier die Prozedur GET_ROWID_CHUNK eingesetzt:

DECLARE
  v_stmt        VARCHAR2(32767);
  v_chunk_id    NUMBER;
  v_start_rowid ROWID;
  v_end_rowid   ROWID;
  v_rows_left   BOOLEAN;
  v_errcnt      NUMBER := 0;
BEGIN
  BEGIN
     DBMS_PARALLEL_EXECUTE.drop_task('update big_tab, chunks by rowid');
  EXCEPTION
     WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE('Task bereits vorhanden, wird gelöscht');
  END;
  v_stmt := q'[UPDATE big_tab
               SET session_id = sys_context('userenv', 'sessionid'),
                  object_type = object_type||'*',
                      created = sysdate
                 WHERE rowid BETWEEN :start_id AND :end_id]';
-- Task erstellen
   DBMS_PARALLEL_EXECUTE.create_task (
       task_name => 'update big_tab, chunks by rowid');
-- Aufteilung in Chunks
   DBMS_PARALLEL_EXECUTE.create_chunks_by_rowid(
          task_name => 'update big_tab, chu

Archivierung in ein Remote-Verzeichnis

Bereich:DBA, Version: ab RDBMS 12.x, Letzte Überarbeitung: 12.12.2018

Keywords:DBA, Oracle Backup & Recovery

Sie betreiben Ihre Oracle Datenbank unter Windows und Ihnen geht der lokale Speicherplatz allmählich aus? Trotzdem soll natürlich weiterhin archiviert werden und auf die regelmäßigen RMAN-Backups wollen Sie verständlicherweise auch nicht verzichten. Haben Sie sich nicht schon häufiger gefragt, ob es keine Möglichkeit gibt, die Archivelogs und die RMAN Backupsets direkt auf einem Remote-Rechner zu erzeugen? Im vorliegenden Tipp erfahren Sie, wie Sie genau dies erreichen können.

Die Frage nach Remote-Archivierung (ohne eine Standby Datenbank aufbauen zu müssen) ist bestimmt nicht neu und eigentlich auch schon seit Längerem realisierbar gewesen. Der Versuch der Umsetzung führte allerdings oftmals zu größeren Frustrationserlebnissen. Stattdessen wurde der verfügbare Speicherplatz einfach um eine lokale Platte erweitert.

Hier nun die Schritte, die für eine (hoffentlich) erfolgreiche Einrichtung notwendig sind.

 

SCHRITT 1: EINRICHTEN EINES LOKALEN ADMINISTRATORS AUF ZIELSEITE


Da es in aller Regel an den Berechtigungen scheitert, muss auf Zielseite zunächst ein Administrator-Account eingerichtet werden. Dafür ist der gleiche Name und das gleiche Passwort wie auf Quellseite zu vergeben. In unserem Beispiel ist das der Benutzer "schulung" mit einem (natürlich streng geheimen) Passwort.

 

SCHRITT2: EINRICHTEN EINER NETZWERKFREIGABE AUF ZIELSEITE


Damit remote archiviert werden kann, benötigen Sie eine Freigabe auf dem Remote-Rechner. Geben Sie z.B. das Verzeichnis C:\Temp frei, so dass der Benutzer "schulung" lesend und schreibend darauf zugreifen kann. Als Zielverzeichnis für die archivierten Redo Logfiles und evtl. die RMAN Backupsets wird innerhalb von C:\Temp noch das Verzeichnis "FRA" angelegt.

 

SCHRITT 3: UMSTELLUNG DER ORACLE DIENSTE AUF DEN ADMINISTRATOR-ACCOUNT


Standardmäßig laufen alle Oracle Dienste unter dem "Local System" Account. Stellen Sie diese auf den lokalen Administrator "schulung" um. Dazu gehen Sie in den Eigenschaften des Dienstes auf die Registerkarte "Anmelden" und wählen "Dieses Konto" für die Anmeldung aus. Tragen Sie nun den Benutzer "schulung" und sein Kennwort ein.


Anschließend erhalten Sie die folgenden Windows-Meldungen

Dem Konto .\schulung wurde die Berechtigung zum Anmelden als Dienst zugewiesen.
Der neue Anmeldename wird erst wirksam, wenn der Dienst beendet und neu gestartet wurde.

Danach starten Sie den Dienst neu. Achten Sie darauf, dass alle gestarteten Oracle Dienste unter dem selben Account laufen. Hier sind das die Dienste "OracleService<sid>" und "Oracle<Home>TNSListener".


SCHRITT 4: SETZEN DER PARAMETER DB_RECOVERY_FILE_DEST BZW. LOG_ARCHIVE_DEST_<N>


Im letzten Schritt definieren Sie Ihr Remote-Verzeichis als neues Zielverzeichnis für die Archivelogs und/oder Backupsets. Bei der Pfadangabe sind allerdings nur UNC-Pfade erlaubt. Die Verwendung von Laufwerksbuchstaben wird bei Remote-Verzeichnissen nicht unterstützt.

Das Zielverzeichnis kann sowohl über die Flash bzw. Fast Recovery Area gesetzt werden, als auch direkt über einen der log_archive_dest<n> Parameter. Zulässig sind folgende Beispiele:

SQL> ALTER SYSTEM SET log_archive_dest_2='location=\\remoteserver\temp\FRA';
System wurde geändert.
SQL> ALTER SYSTEM SWITCH LOGFILE;
System wurde geändert.
SQL> ALTER SYSTEM SET db_recovery_file_dest='\\remoteserver\temp\FRA';
System wurde geändert.
SQL> ALTER SYSTEM SET log_archive_dest_1='location=use_db_recovery_file_dest';
System wurde geändert.
SQL> ALTER SYSTEM SWITCH LOGFILE;
System wurde geändert.
SQL> SELECT name FROM v$archived_log;
NAME
---------------------------------------------------------------------------------
...
C:\ORACLE\FAST_RECOVERY_AREA\O18C\ARCHIVELOG\2018_08_24\O1_MF_1_137_83G8X0SD_.ARC
\\remoteserver\TEMP\FRA\ARC0000000138_0756558819.0001
\\remoteserver\TEMP\FRA\O18C\ARCHIVELOG\2018_08_24\O1_MF_1_139_83G9R8FY_.ARC
\\remoteserver\TEMP\FRA\ARC0000000139_0756558819.0001


Falls Sie die Fast Recovery Area auch für Ihre RMAN Backups verwenden, lassen sich somit auch die Backupsets direkt auf einem Remote-Server erzeugen, ohne dass Sie irgendetwas an Ihren Backup-Skripten verändern müssen.

Nicht zulässig ist folgende Angabe (trotz eingerichteter Netzlaufwerksverbindung Z:)

SQL> ALTER SYSTEM SET log_archive_dest_2='location=z:\FRA';
ALTER SYSTEM SET log_archive_dest_2='location=z:\FRA'
*
FEHLER in Zeile 1:
ORA-02097: Parameter kann nicht verändert werden, da angegebener Wert ungültig ist
ORA-16032: Zielzeichenfolge von Parameter LOG_ARCHIVE_DEST_2 kann nicht übersetzt werden
ORA-09291: sksachk: Ungültiges Verzeichnis für Archivierungsziel angegeben
OSD-04018: Auf das angegebene Verzeichnis oder Gerät kann nicht zugegriffen werden.
O/S-Error: (OS 3) Das System kann den angegebenen Pfad nicht finden.
SQL> ALTER SYSTEM SET db_recovery_file_dest='z:\FRA';
ALTER SYSTEM SET db_recovery_file_dest='z:\FRA'
*
FEHLER in Zeile 1:
ORA-02097: Parameter kann nicht verändert werden, da angegebener Wert ungültig ist
ORA-01261: Zielfolge für Parameter db_recovery_file_dest kann nicht übersetzt werden
ORA-01263: Für Datei-Zielverzeichnis angegebener Name ist ungültig
OSD-04018: Auf das angegebene Verzeichnis oder Gerät kann nicht zugegriffen werden.
O/S-Error: (OS 3) Das System kann den angegebenen Pfad nicht finden.

 

HINWEISE


Sind die oben beschriebenen Schritte 1-3 vorgenommen worden, so lassen sich ebenfalls Control- und Online Redo Log-Dateien sowie das SPFILE remote verwenden.

-- Beispiel für Controldateien
SQL> ALTER SYSTEM set control_files=
     'C:\ORACLE\ORADATA\O11G\CONTROL01.CTL',
     'C:\ORACLE\FAST_RECOVERY_AREA\O18C\CONTROL02.CTL',
     '\\remoteserver\temp\FRA\CONTROL03.CTL' SCOPE=SPFILE;
-- Beispiel für Redo Logs
SQL> ALTER DATABASE ADD LOGFILE MEMBER '\\remoteserver\temp\FRA\REDO01B.RDO'
     TO GROUP 1;

Soll das SPFILE remote genutzt werden, verschieben Sie es in das Remote-Verzeichnis und erstellen Sie eine INIT<sid>.ORA Datei mit folgendem Inhalt:

*.spfile='\\remoteserver\temp\FRA\spfile<sid>.ora'

Auch Datendateien lassen sich auf diese Weise remote anlegen, allerdings kam es bei unseren Tests zu wiederkehrenden Fehlermeldungen in der Alertdatei.

-- Anlegen eines neuen Tablespaces
SQL> CREATE TABLESPACE remote_tbs DATAFILE '\\remoteserver\temp\FRA\tbs01.dbf'
     SIZE 100M;

 

-- Auszug aus der Alertdatei
...
Errors in file C:\ORACLE\diag\rdbms\o11g\o18C\trace\o11g_m001_3996.trc:
Error in computing freespace for file 000007FF546180B0 \\remoteserver\TEMP\FRA\TBS01.DBF
ORA-27037: Dateistatus nicht abrufbar
OSD-04011: GetFileInformationByHandle() failure, Datei-Info kann nicht abgerufen werden
O/S-Error: (OS 161) Der angegebene Pfadname ist ungültig.

Dies hatte zwar keine (merkbare) Auswirkung auf die Verfügbarkeit der Datendatei und deren Inhalt, aber besonders beruhigend ist diese Fehlermeldung auch nicht.


ABSCHLUSS


Das Ablegen von Oracle Dateien auf einem Remote-Rechner ist also keine Hexerei und mit wenigen Schritten durchführbar.

Allerdings ist zu beachten, dass Sie in all diesen Fällen auf eine permanent bestehende (und auch schnelle) Netzwerkverbindung angewiesen sind, ansonsten hängt Ihr Datenbanksystem oder stürzt im schlimmsten Fall sogar ab.

 

Viele weitere interessante Informationen rund um die Administration von Oracle Datenbanken erhalten Sie in unseren DBA Kursen. Schauen Sie doch mal vorbei.



Weitere Interessente Artikel zum Thema:



Empfohlene Schulungen zum Thema:


Export von Tabellen als CSV-Files mit UTL_File

Bereich:PL/SQL, Version: ab APEX 4.x, Letzte Überarbeitung: 05.08.2023

Keywords:PL/SQL, Standard Packages

Der Export von Tabellen als csv-Files ist immer wieder ein Thema in unseren PL/SQL- und Packages-Kursen, weil die meisten gerne mit Excel arbeiten. Deshalb gebe ich unseren Teilnehmern immer eine einfache Prozedur mit, über die man Inhalte beliebiger Tabellen (solange sie keine LOB-Spalten oder ähnliches enthalten) mittels UTL_FILE als semikolon-separierte Ascii-File exportieren kann. Der Einsatz von UTL_FILE ist nicht der schnellste Weg, aber man kann diese Prozeduren sehr gut in Datenbank-Jobs einbinden oder für andere PL/SQL-Programme  verwenden.

Bei Tom Kyte findet man übrigens eine schöne Übersicht seiner diversen Export-Utilities. Für seine PL/SQL-Funktion dump_csv verwendet er DBMS_SQL

Das Problem ist allerdings: Mit den üblichen Beispieltabellen wie scott.emp oder all_objects klappt das alles natürlich prima, aber wie sieht's denn aus, wenn man "historisch gewachsene" Tabellen mit fast 100 oder mehr Spalten vor sich hat, in denen wegen der Verwendung von CHAR-Datentypen u. U. viel heiße Luft gespeichert ist? Deshalb wollte ich mal ausprobieren, wie man einserseits die kleine Prozedur ausbauen müßte, um solche Problemfälle in den Griff zu kriegen und welche Möglichkeiten es gibt, das Ganze schneller zu machen.

Von Adrian Billington stammt ein sehr lesenswerter Artikel von 2008: PL/SQL-File-IO über verschiedene Ansätze, die einfache Ausgabe über UTL_FILE zu beschleunigen und Performance-Vergleiche zwischen dem Einsatz von UTL_FILE auf der einen und der Kombination von DBMS_LOB und DBMX_XSLPROCESSOR auf der anderen Seite. Seine Codebeispiele für UTL_FILE habe ich leicht abgewandelt, in meine alte Exportprozedur eingebaut und mit verschiedenen Tabellen getestet.


FUNKTIONEN UND PROZEDUREN


Die Erstellung der Spaltenlisten wird in eine Funktion ausgelagert. Die TRIM-Funktion macht beim späteren Export die CHAR- und VARCHAR2-Spalten-Inhalte "schlanker", indem sie Leerzeichen von beiden Seiten löscht.

CREATE OR REPLACE FUNCTION col_list (
      p_tabname   VARCHAR2,
      p_schema    VARCHAR2 DEFAULT user,
      p_trim      NUMBER DEFAULT 0,
      p_delim     VARCHAR2 DEFAULT ',')  RETURN VARCHAR2
AS
    l_col_list VARCHAR2(4000) := ' ';
BEGIN
    FOR rec IN (SELECT column_name,data_type FROM all_tab_columns
                WHERE table_name = UPPER(p_tabname)
                AND owner        = UPPER(p_schema)
                ORDER BY column_id) LOOP
      IF p_trim = 1 AND rec.data_type IN ('CHAR', 'VARCHAR2') THEN
        l_col_list  := l_col_list ||p_delim||'TRIM('||rec.column_name||')';
      ELSE
        l_col_list := l_col_list ||p_delim||rec.column_name;
      END IF;
    END LOOP;
    RETURN NVL(LTRIM(l_col_list, p_delim||' '), 'ungültiger Tabellenname oder fehlende Berechtigung');
END;
/


Diese Prozedur exportiert den Inhalt einer Tabelle als csv-File (mit der Angabe von Datum und Uhrzeit). Das Directory muss natürlich existieren und der Ersteller der Prozedur muss Lese- und Schreibrechte darauf haben.

CREATE OR REPLACE PROCEDURE tab2csv (
   p_directory VARCHAR2,
   p_tabname   VARCHAR2,
   p_schema    VARCHAR2 DEFAULT USER)
IS
    tab_refcur        SYS_REFCURSOR;
    l_header          VARCHAR2(4000);
    l_zeile           VARCHAR2(4000);
    l_col_list        VARCHAR2(4000);
    l_stmt            VARCHAR2(4000);
    l_file            UTL_FILE.FILE_TYPE;
 BEGIN
    l_file := UTL_FILE.FOPEN(
        location  => UPPER(p_directory),
        filename  => p_tabname||'_'||TO_CHAR(sysdate,'yyyy-mm-dd-hh24-mi')||'.csv',
        open_mode => 'w',
     max_linesize => 32767);
  -- Zusammenstellung der Spaltenliste für die Überschriften-Zeile
    l_header := col_list(
            p_tabname => p_tabname,
             p_schema => p_schema,
              p_delim => ';');
  -- Die Überschriften werden in die Datei geschrieben
     UTL_FILE.PUT_LINE(l_file, l_header);
  -- Zusammenstellung der Spaltenliste für den Select
     l_col_list := col_list(
             p_tabname => p_tabname,
              p_schema => p_schema ,
               p_delim => q'[||';'||]', -- alternativ '||'';''||'
                p_trim => 1);
     l_stmt   := 'SELECT '||l_col_list||' FROM '||p_schema||'.'||p_tabname;
  -- Über den Ref Cursor werden die Spalteninhalte jeder Zeile
  -- als Strings aneinandergehängt
     OPEN tab_refcur FOR l_stmt;
     LOOP
  -- und in die Variable eingelesen
       FETCH tab_refcur INTO l_zeile;
       EXIT WHEN tab_refcur%NOTFOUND;
       -- mit der Prozedur PUT_LINE wird Zeile für Zeile geschrieben
     UTL_FILE.PUT_LINE(l_file, l_zeile);
    END LOOP;
    CLOSE tab_refcur;
   UTL_FILE.FCLOSE(l_file);
EXCEPTION
 WHEN OTHERS THEN
   -- hier sollte natürlich eine vernünftige Fehleraufzeichnung passieren
    DBMS_OUTPUT.PUT_LINE(SQLERRM);
    UTL_FILE.FCLOSE(l_file);
   RAISE;
END tab2csv;
/


In der folgenden Prozedur habe ich Adrian Billingtons Idee zur Zwischenspeicherung der Zeilen in einer 32KB-VARCHAR2-Variable verwendet. Erst wenn der Puffer voll ist, wird die Zeile geschrieben. Vor allem für größere Tabellen macht das einen Unterschied.

CREATE OR REPLACE PROCEDURE tab2csv_buffered (
   p_directory VARCHAR2,
   p_tabname   VARCHAR2,
   p_schema    VARCHAR2 DEFAULT USER)
IS
    tab_refcur        SYS_REFCURSOR;
    l_header          VARCHAR2(4000);
    l_zeile           VARCHAR2(4000);
    l_col_list        VARCHAR2(4000);
    l_stmt            VARCHAR2(4000);
    l_file            UTL_FILE.FILE_TYPE;
    l_buffer          VARCHAR2(32767);
 BEGIN
  l_file := UTL_FILE.FOPEN(
        location => UPPER(p_directory),
        filename => p_tabname||'_'||TO_CHAR(sysdate,'yyyy-mm-dd-hh24-mi')||'.csv',
       open_mode => 'w',
    max_linesize => 32767);
    l_header := col_list(
            p_tabname => p_tabname,
              p_schema => p_schema,
              p_delim => ';');
   UTL_FILE.PUT_LINE(l_file, l_header);
    l_col_list := col_list(
             p_tabname => p_tabname,
               p_schema => p_schema ,
               p_delim => q'[||';'||]',
                p_trim => 1);
    l_stmt := 'SELECT '||l_col_list||' FROM '||p_schema||'.'||p_tabname;
    OPEN tab_refcur FOR l_stmt;
    LOOP
       FETCH tab_refcur INTO l_zeile;
       EXIT WHEN tab_refcur%NOTFOUND;
  -- solange der Buffer nicht voll ist, werden weitere Zeilen
  -- getrennt durch Linefeed (chr(10)) eingeladen
       IF LENGTH(l_buffer) + 1 + LENGTH(l_zeile) <= 32767 THEN
         l_buffer := l_buffer || CHR(10) ||l_zeile;
       ELSE
         IF l_buffer IS NOT NULL THEN
  -- der volle Buffer wird in die Datei geschrieben
            UTL_FILE.PUT_LINE(l_file, l_buffer);
         END IF;
  -- der Buffer wird zurückgesetzt
         l_buffer := l_zeile;
       END IF;
    END LOOP;
  -- was nach dem Ende der Schleife noch im Buffer ist
  -- wird hier rausgeschrieben
    UTL_FILE.PUT_LINE(l_file, l_buffer);
    CLOSE tab_refcur;
   UTL_FILE.FCLOSE(l_file);
EXCEPTION
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE(SQLERRM);
    UTL_FILE.FCLOSE(l_file);
    RAISE;
END tab2csv_fast;
/


Und jetzt Billingtons Turboversion mit Parallel-Antrieb (etwas vereinfacht). Die parallele Ausgabe funktioniert nur mit einer Table Function und ist deshalb etwas komplexer. Zudem ist die Parallelisierung nur bei der Enterprise-Edition möglich.

Statt mit einem Cursor durch die Ergebnismenge zu laufen, wird hier eine nested Table verwendet, die in Portionen von je 100 Zeilen befüllt wird (Dies allein bringt noch keinen Geschwindigkeitsgewinn, da auch bei der Ref Cursor-Version im Hintergrund ein Bulk Collect von je 100 Zeilen durchgeführt wird). Diese 100 Zeilen werden dann über die Puffermethode ins File geschrieben, dann holt sich die PL/SQL Engine die nächsten 100.
Nach dem Schließen des Files wird als Dummy ein Sequenzwert über PIPE ROW ausgegeben, damit die Funktion einen Rückgabewert hat.

-- Nested Table-Objekttyp zum Auffangen der Ergebnismenge
CREATE TYPE num_nt_type AS TABLE OF NUMBER;
/
CREATE SEQUENCE file_seq;
CREATE OR REPLACE FUNCTION parallel_output (
      p_refcur    SYS_REFCURSOR,
      p_directory VARCHAR2,
      p_tabname   VARCHAR2,
      p_schema    VARCHAR2 DEFAULT USER) RETURN num_nt_type PIPELINED
 -- Parallelisierte Abarbeitung aktivieren
   PARALLEL_ENABLE
 -- Oracle soll sich die Anzahl der Slave-Prozesse selber aussuchen
   (PARTITION p_refcur BY ANY)
AS
 -- Nested Table für den zu exportierenden Text
   TYPE string_nt_type IS TABLE OF VARCHAR2(32767);
   string_nt    string_nt_type;
    l_name       VARCHAR2(200);
    l_header     VARCHAR2(4000);
    l_zeile      VARCHAR2(4000);
    l_col_list   VARCHAR2(4000);
    l_stmt       VARCHAR2(4000);
    l_file       UTL_FILE.FILE_TYPE;
    l_buffer     VARCHAR2(32676);
BEGIN
   l_name := p_tabname||'_'||TO_CHAR(sysdate,'yyyy-mm-dd-hh24-mi');
   l_file := UTL_FILE.FOPEN(
        location => UPPER(p_directory),
        filename => l_name||'.csv',
       open_mode => 'w',
    max_linesize => 32767);
    l_header := col_list(
            p_tabname => p_tabname,
              p_schema => p_schema,
              p_delim => ';');
   UTL_FILE.PUT_LINE(l_file, l_header);
   l_name := l_name|| '_' ||file_seq.NEXTVAL|| '.csv';
   l_file := UTL_FILE.FOPEN(
        location => UPPER(p_directory),
        filename => l_name,
       open_mode => 'w',
    max_linesize => 32767);
    LOOP
     FETCH p_refcur BULK COLLECT INTO string_nt LIMIT 100;
     EXIT WHEN string_nt.COUNT = 0;
      FOR i IN 1 .. string_nt.COUNT LOOP
         IF LENGTH(l_buffer) + 1 + LENGTH(string_nt(i)) <= 32676 THEN
            l_buffer := l_buffer ||CHR(10)|| string_nt(i);
         ELSE
            IF l_buffer IS NOT NULL THEN
               UTL_FILE.PUT_LINE(l_file, l_buffer);
            END IF;
            l_buffer := string_nt(i);
         END IF;
      END LOOP;
   END LOOP;
   CLOSE p_refcur;
   UTL_FILE.PUT_LINE(l_file, l_buffer);
   UTL_FILE.FCLOSE(l_file);
   PIPE ROW (file_seq.NEXTVAL);
   RETURN;
 EXCEPTION
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE(SQLERRM);
    UTL_FILE.FCLOSE(l_file);<

Oracle Arithmetische Funktionen und String Funktionen

Bereich:SQL, Version: ab RDBMS 11.2:RDBMS 12.x:RDBMS 19.1:RDBMS 21.1:RDBMS 23.1:RDBMS 18.1, Letzte Überarbeitung: 11.02.2025

Keywords:Arithmetische Funktionen

Oracle´s Arithmetische Funktionen

INFOBESCHREIBUNGBEISPIEL
CEIL (x)rundet auf nächsthöhere ganze Zahl aufSELECT ceil(-9.7) FROM dual;
MOD (m, n)gibt Rest der Division m:n als (ganze) Zahl wiederSELECT mod(5,2) FROM dual;
POWER (x, y)ermittelt den Wert der Potent x^ySELECT power(2,3) FROM dual;
ROUND (x[,n])rundet auf n Dezimalstellen auf oder abSELECT round(2.456,2) FROM dual;
SQRT (x)zieht die Wurzel von XSELECT sqrt(256) FROM dual;
TRUNC (x[,n])schneidet Wert nach n Dezimalstellen ohne           
Runden ab
SELECT trunc(94.89, 1) FROM dual;

 

 

Oracle Datumsfunktionen

INFOBESCHREIBUNGBEISPIEL
ADD_MONTHS('DD.MM.YYYY',n)fügt n Monate hinzuSELECT add_months(sysdate, 3) FROM dual;
LAST_DAY('DD.MM.YYYY')gibt den letzten Tag des Monats zurückSELECT last_day('07.05.19') FROM dual;
NEXT_DAY('DD.MM.YYYY', day)gibt den darauffolgenden Tag anSELECT next_day(sysdate,'MONDAY') FROM dual;
MONTHS_BETWEEN('...','...')berechnet Anzahl der Monate zwischen zwei DatenSELECT months_between(sysdate, '01.01.19') FROM dual;
ROUND(date[,fmt])rundet Datum auf fmt-Einheit auf oder abSELECT round(to_date('16.05.19'), 'MM') FROM dual;
TRUNC(date[,fmt])schneidet Datum auf fmt-Einheit abSELECT trunc(sysdate, 'YY') FROM dual;

 

 

Oracle Konvertierungsfunktionen

INFO

 

BESCHREIBUNGBEISPIEL
TO_CHAR(date,'fmt')Datumskonvertierung (Datum -> Zeichenkette)SELECT to_char(sysdate, 'DAY DD-MM-YYYY') FROM dual;
TO_CHAR(number, 'fmt')zeigt eine Zahl als Zeichen an (Zahl -> Zeichenkette)SELECT to_char(sal, 'L99,999.99') FROM emp;
TO_NUMBER(char[,'fmt'])konvertiert Zeichenkette in ZahlSELECT to_number(sal) FROM emp;
TO_DATE('...', 'fmt')konvertiert Zeichenkette mit Datum in DatumswertSELECT ename, hiredate FROM emp           
WHERE hiredate = to_date('Februar 22,           
1981', 'Month DD. YYYY');)
NVL(spalte, ausdruck)konvertiert NULL-Werte in aktuellen WertSELECT ename, sal*12+nvl(comm, 0) FROM emp;
DECODE (col, search1, result1[, search2, result2, ...][,default])ermöglicht bedingte Abfragen (If-Then-Else)SELECT ename, deptno, sal           
decode(depnto, 10, sal*1.05,20,sal*1.15,sal)           
erhöhung           
FROM emp;

 

 

Oracle Stringfunktionen

INFOBESCHREIBUNGBEISPIEL
GREATEST/LEAST(Ausdruck)ermittelt den größten/kleinsten Wert aus einer WertemengeSELECT least(-1,3,456) FROM dual;
LENGTH('...')gibt Länge einer Zeichenkette als Zahl wiederSELECT ename, length(ename) FROM emp;
INSTR('...','a'[,x,y])gibt Position eines bestimmten Zeichens als Zahl anSELECT instr('Suchmaschine', 'h', 1)           
FROM dual;           
=>4           
SELECT instr('Suchmaschine', 'h', 1, 2)           
FROM dual;           
=>9           
SELECT instr('Suchmaschine', 'h', 5)           
FROM dual;           
=>9
SUBSTR('...',x[,y])extrahiert Zeichenkette der angegebenen LängeSELECT substr('Marco', 1, 3) FROM dual;           
=>Mar           
SELECT substr('Marco', 4) FROM dual;           
=>co           
SELECT substr('Marco',-1) FROM dual;           
=>o
LTRIM/RTRIM(Ausdruck[,'SET'])entfernt Zeichen eines Strings von Links nach RechtsSELECT ename, RTRIM(ename, 'ER') FROM emp;
TRIM(leading|trailing|both 'X' FROM Ausdruck)entfernt Zeichen eines Strings am Anfang oder Ende oder beidesSELECT TRIM(leading ' ' FROM ' Hallo '           
FROM dual;
LPAD/RDAP(spalte)füllt Zeichenwert mit Leerzeichen zum rechts-/ linksbündigen Blocksatz aufSELECT ename, LDAP(sal, 10, '*') gehalt           
FROM emp;
LOWER(spalte/ausdruck)wandelt in Kleinbuchstaben umSELECT lower(ename) FROM emp;
UPPER(spalte/ausdruck)wandelt in Großbuchstaben umSELECT upper(ename) FROM emp;
INITCAP(spalte/ausdruck)wandelt jeweils den ersten Buchstaben eines Wortes umSELECT initcap(ename) FROM emp;
REPLACE(spalte,'x','y')Ersetzt Zeichen 'x' durch 'y' innerhalb eines StringsSELECT ename, replace(ename, 'R', '#' FROM emp;
TRANSLATE(spalte, 'str1', 'str2')ersetzt String innerhalb eines Strings durch einen neuen StringSELECT ename, translate(ename, 'ABCDEF', 'GHJKL') FROM emp;

 

 

 



Weitere Interessente Artikel zum Thema:


Empfohlene Schulungen zum Thema:


Itemcheck in APEX Anwendungen über alle Items einer Seite

Bereich:APEX, Version: ab RDBMS 12.x, Letzte Überarbeitung: 18.12.2018

Keywords:APEX Itemcheck

Seit APEX Version 5.1 können ja nun zusätzliche Standard-Prüfungen für die Items durchgeführt werden. Diese sind:

  • Whitelist for a-z 0-9       Nur Zeichen und Zahlen sind erlaubt (Leider sind auch keine Umlaute möglich)
  • Blacklist HTML Command Characters (<> '')
  • Blacklist &<>"/;*|=% and --
  • Blacklist &<>"/;*|=% or -- and new line

Wenn das aber alles nicht passt, können wir uns eine eigene Validation schreiben:

DECLARE
stmt VARCHAR2(32000);
v_sqlerrm varchar2(32000);
BEGIN
FOR c IN (SELECT   PAGE_ID,  PAGE_NAME,  ITEM_NAME
FROM APEX_APPLICATION_PAGE_ITEMS
WHERE application_id=:APP_ID and page_id=:APP_PAGE_ID  ) LOOP
     stmt:=q'!BEGIN
     IF instr(v('!'||c.item_name||q'!'),'<')>0 OR
        instr(v('!'||c.item_name||q'!'),'>')>0 OR
        instr(v('!'||c.item_name||q'!'),'^')>0 OR
        instr(v('!'||c.item_name||q'!'),'&')>0 OR
        instr(v('!'||c.item_name||q'!'),'"')>0 OR
        instr(v('!'||c.item_name||q'!'),chr(39))>0
    THEN
      RAISE_APPLICATION_ERROR(-20500,'Ungültige Zeichen verwendet (^<>&")');
     END IF;
      EXCEPTION WHEN OTHERS THEN
       IF sqlcode=-20500 then raise; end if;
     END; !';
commit;
    EXECUTE IMMEDIATE stmt;

END LOOP;
  RETURN true;
EXCEPTION WHEN OTHERS THEN
   v_sqlerrm:=sqlerrm;
  RETURN false;
END;


Wenn jetzt jemand in irgendein Item eines der Zeichen <>'" &^ einträgt, wird diese Eingabe abgewiesen.



Weitere Interessente Artikel zum Thema:


Empfohlene Schulungen zum Thema:


Rebuild von defekten Indizes

Bereich:DBA:Monitoring, Version: ab RDBMS 12.x, Letzte Überarbeitung: 10.04.2019

Keywords:Oracle Workspace Manager

Es ist wohl eines der best gehütesten Geheimnisse, dass defekte Indizes in der Tabelle dba_objects in der Spalte weiterhin mit dem Status VALID stehen.
Wenn man also festellen möchte, welche Indizes defekt sind, muss man in DBA_INDEXES bzw. DBA_PART_INDEXES nachsehen.

Das nachfolgende Skript macht einen Rebuild auf alle defekten Indizes, ohne dass die Ausgabe zuerst gespoolt werden muss.


WITH FUNCTION do_ddl (cmd IN VARCHAR2)
RETURN VARCHAR2
IS
PRAGMA AUTONOMOUS_TRANSACTION; -- damit ist die Funktion eigenständig und beeinflusst niemanden :-)
v_cmd VARCHAR2(32000);
BEGIN
v_cmd:=rtrim(cmd,';'); -- Semmikolon ggf.herausfiltern
EXECUTE IMMEDIATE v_cmd; -- Befehl ausführen
RETURN v_cmd||'; --OK'; -- Befehl erfolgreich ausgeführt
EXCEPTION WHEN OTHERS THEN
 RETURN v_cmd||' --'||sqlerrm;
END;
SELECT do_ddl('ALTER INDEX "'||owner||'"."'||index_name||'" REBUILD;')
FROM dba_indexes i
WHERE status='UNUSABLE'
UNION ALL
SELECT do_ddl('ALTER INDEX  "'||index_owner||'"."'||index_name||'" REBUILD PARTITION "'||partition_name||'";')
FROM dba_ind_partitions
WHERE status='UNUSABLE';


Weitere Interessente Artikel zum Thema:



Empfohlene Schulungen zum Thema:


Upgrade und Migration einer Non-Container-DB (12.1.0.2) in eine Container-DB (12.2.0.1)

Bereich:DBA:SQL, Version: ab RDBMS 12.x, Letzte Überarbeitung: 16.04.2020

Keywords:Non-Container-DB, Container-DB

In diesem Tipp geht es um die Migration einer Non-Container Datenbank in die Container Architektur. Dabei soll auch gleichzeitig ein Upgrade auf die Version 12.2.0.1 erfolgen.

Notwendigkeit des Umstiegs

Seitdem im März 2013 die Version 12c erschienen ist, wird verstärkt über das zentrale neue Feature "Pluggable Database"
diskutiert. Für die Einen endlich die Möglichkeit, ihre zahlreichen, kleinen (ähnlich gestrickten) Datenbanksysteme in einem
zentralen DBMS zu konsolidieren und sich damit die Verwaltung zu vereinfachen, für die Anderen aus den verschiedensten
Gründen nicht umsetzbar. Nicht zuletzt wegen der anfallenden Lizenzkosten für die Multitenant Option.

Aber egal, wie sehr man diese neue Architektur auch ablehnt, wer Oracle weiterhin produktiv einsetzen und dabei auch Support
"genießen" möchte, der kommt um die Migration auf die Container-Architektur nicht herum. Der Oracle Premier Support für 12.1.0.2
ist (derzeit) bis Mitte 2019 und für 12.2.0.1 bis Mitte 2022 festgelegt worden. Danach wird laut Oracle Dokumentation (vermutlich)
nur noch die neue Architektur supportet. Hier ein Auszug aus der Doku dazu:

Note:

Starting with Oracle Database 12c, release 1 (12.1), non-CDB architecture is deprecated. It
can be desupported in a future release. Oracle Database deployed with the multitenant
architecture is the default configuration option. All Oracle Database releases earlier than
Oracle Database 12c release 1 (12.1.0.1) use non-CDB architecture.

Oracle strongly recommends that you upgrade your source and target databases to the most
recent bundle patch or patch set update (BP or PSU) before starting an upgrade ...


Also wollen wir uns in diesem Monatstipp einmal ansehen, wie der eigentliche Migrations- und Upgrade-Vorgang aussehen
könnte. An der Stelle sei darauf hingewiesen, dass es verschiedene Alternativen gibt, um eine Migration von Non-CDB in CDB
durchzuführen, auf die hier nicht ganzheitlich eingegangen wird.

Folgende Vorbereitungen müssen aber für alle Varianten getroffen werden:

Die Binaries der Zielversion (12.2.0.1) sind bereits in einem neuen ORACLE_HOME installiert und eine neue (Ziel-)Datenbank (hier
mit Namen O122CDB) wurde als Container-DB angelegt.

Aber Achtung: Sobald Sie mehr als eine Pluggable Database innerhalb einer CDB installiert haben, müssen Sie die Multitenant-
Lizenz erwerben. Falls dies nicht gewünscht ist, muss die CDB zunächst ohne Pluggable Database erzeugt werden. Dort hinein
wird in den folgenden Schritten die Non-Container Quell-DB (o12c der Version 12.1.0.2) als PDB (pdb_o12c der Version 12.2.0.1)
eingehängt.

Die Empfehlung von Oracle, vor dem Upgrade den neuesten Bundle Patch (BP) oder PSU einzuspielen ignorieren wir zunächst
einmal, was sich allerdings sehr bald rächen soll ...

Preupgrade Prüfung

Für die Vorabprüfung eines Upgrades auf 12.2 gibt es ein neues Skript (PREUPGRADE.JAR). Dieses wird aus dem (alten)
ORACLE_HOME heraus aufgerufen:

$ cd $ORACLE_HOME_<old>/jdk/bin
$ ./java -jar $ORACLE_HOME_<new>/rdbms/admin/preupgrade.jar


Im Idealfall sieht die Rückmeldung folgendermaßen aus:

Preupgrade generated files:

/u01/app/oracle/cfgtoollogs/o12c/preupgrade/preupgrade.log
/u01/app/oracle/cfgtoollogs/o12c/preupgrade/preupgrade_fixups.sql
/u01/app/oracle/cfgtoollogs/o12c/preupgrade/postupgrade_fixups.sql


Falls bei der Durchführung Probleme auftreten, sollten die OS-Variablen ORACLE_HOME, PATH, PERL und PERL5LIB überprüft
werden.

Durchführung des Upgrades und Fehlerbehandlung

Im Internet findet man etliche Anleitungen für das Upgrade, allerdings habe ich gemerkt, dass keine davon zusammenfassend
alle Probleme behandelt, in die ich gelaufen bin, geschweige denn Lösungen dafür anbietet. Der folgende Tipp bietet hoffentlich
eine hilfreiche Liste der möglichen Schritte und Hindernisse, erhebt jedoch ebenfalls keinen Anspruch auf Darstellung aller
möglichen Fehler.

Als Grundlage für den Upgrade, soll an dieser Stelle eine Variante gewählt werden, die zwar in der Doku enthalten, aber nach
meiner Erkenntnis noch nicht allzu weit verbreitet ist: (siehe Mike Dietrich: Öffnet externen Link in neuem Fensterhttps://mikedietrichde.com/2015/05/18/create
-a-pdb-directly-from-a-stand-alone-database/ )
Das remote Klonen einer Non-CDB über die NON$CDB Option mit Database Link.

Dazu melden Sie sich am Root-Container der Ziel-Datenbank an und erzeugen einen Database Link zur Quell-Datenbank. Achten
Sie auf den korrekten Eintrag in der TNSNAMES.ORA.

Der Benutzer, mit dem die Verbindung über den DB-Link zur Quell-DB vorgenommen wird, benötigt das CREATE PLUGGABLE
DATABASE Recht.
 

SQL> conn / as sysdba
SQL> CREATE DATABASE LINK o12c CONNECT TO system
       IDENTIFIED BY <pwd> using 'o12c';


Nun der erste Versuch des Klonvorgangs:
 

$ mkdir /u01/app/oracle/oradata/o122cdb/pdb_o12c
SQL> CREATE PLUGGABLE DATABASE pdb_o12c FROM NON$CDB@o12c
       file_name_convert=('/u01/app/oracle/oradata/o12c',
          '/u01/app/oracle/oradata/o122cdb/pdb_o12c');
/*
 CREATE PLUGGABLE DATABASE pdb_o12c FROM NON$CDB@o12c ...
*
ERROR at line 1:
ORA-17628: Oracle error 17630 returned by remote Oracle server
ORA-17630: Mismatch in the remote file protocol version client  server
*/


Die Internetsuche ergab einen Bug, der durch das Einspielen des Patches 18633374 auf der Quellseite behoben wird (bitte
Readme dazu lesen).
 


$ cd /tmp/18633374
$ opatch apply

## CODE ##

Danach der zweite Versuch:

## CODE ##
SQL> CREATE PLUGGABLE DATABASE pdb_o12c FROM NON$CDB@o12c
       file_name_convert=('/u01/app/oracle/oradata/o12c',
          '/u01/app/oracle/oradata/o122cdb/pdb_o12c');
/*
 CREATE PLUGGABLE DATABASE pdb_o12c FROM NON$CDB@o12c ...
*
ERROR at line 1:
ORA-00600: internal error code, arguments: [25029], [3], [3], [2], [], [], [], [], [], [], [], []
*/


Dieser Fehler erfordert das Einspielen des aktuellen BP auf der Zielseite. In diesem Fall wird der Patch 27105253 im neuen
ORACLE_HOME eingespielt (Achtung Readme dazu).
 


$ cd /tmp/27105253
$ opatch apply


SQL> conn / as sysdba
SQL> startup
SQL> ALTER PLUGGABLE DATABASE ALL OPEN;

$ cd $ORACLE_HOME/OPatch
$ ./datapatch -verbose


Und der dritte Versuch:
 

SQL> CREATE PLUGGABLE DATABASE pdb_o12c FROM NON$CDB@o12c
       file_name_convert=('/u01/app/oracle/oradata/o12c',
          '/u01/app/oracle/oradata/o122cdb/pdb_o12c');
/*
 CREATE PLUGGABLE DATABASE pdb_o12c FROM NON$CDB@o12c ...
*
ERROR at line 1:
ORA-65353: The undo tablespace is missing from the XML metadata file.
*/


Das Problem ist in diesem Fall der in 12.2 standardmäßig eingestellte LOCAL UNDO Modus. Der UNDO-Tablespace aus der Quell-
DB kommt nicht mit, wird aber im Ziel erwartet. Also wird temporär LOCAL UNDO in der Ziel-DB auf FALSE gesetzt.
 

SQL> shutdown immediate
SQL> startup upgrade
SQL> ALTER DATABASE LOCAL UNDO OFF;
SQL> shutdown immediate
SQL> startup


Aber jetzt, der vierte Versuch - na geht doch:
 

SQL> CREATE PLUGGABLE DATABASE pdb_o12c FROM NON$CDB@o12c
       file_name_convert=('/u01/app/oracle/oradata/o12c',
          '/u01/app/oracle/oradata/o122cdb/pdb_o12c');
-- Pluggable database created.


In jedem Fall sollte man sich die PDB_PLUG_IN_VIOLATIONS View ansehen, um über WARNINGs und ERRORs informiert zu
werden.
 

SQL> col message for a200
     col action for a150
     col cause for a20
     col time for a30
     col name for a10
SQL> SELECT time, name, cause, type, message, status, action
       FROM pdb_plug_in_violations;

...
VSN not match
ERROR
PDB's version does not match CDB's version: PDB's version 12.1.0.2.0. CDB's version 12.2.0.1.0.
PENDING
Either upgrade the PDB or reload the components in the PDB.
...
Non-CDB to PDB       
WARNING             
PDB plugged in is a non-CDB, requires noncdb_to_pdb.sql be run.
PENDING       
Run noncdb_to_pdb.sql.


Also muss die Pluggable Database PDB_O12C zunächst upgegradet werden. Dies erfolgt über den Aufruf des Perl-Skripts
CATCTL.PL
 

SQL> ALTER SESSION SET CONTAINER=pdb_o12c;
SQL> ALTER PLUGGABLE DATABASE pdb_o12c OPEN UPGRADE;

$ export ORACLE_HOME=/u01/app/oracle/product/12.2.0.1/dbhome_1
$ export ORACLE_SID=o122cdb
$ export PERL=$ORACLE_HOME/perl/bin
$ export PERL5LIB=$ORACLE_HOME/rdbms/admin
$ export PATH=$ORACLE_HOME/bin:$PERL:$PATH
$ cd $ORACLE_HOME/rdbms/admin

$ $ORACLE_HOME/perl/bin/perl catctl.pl -c 'PDB_O12C' catupgrd.sql

/*
Argument list for [catctl.pl]
Run in                c = PDB_O12C


catctl.pl VERSION: [12.2.0.1.0]
           STATUS: [production]
            BUILD: [RDBMS_12.2.0.1.0_LINUX.X64_170125]

/u01/app/oracle/product/12.2.0.1/dbhome_1/rdbms/admin/orahome =
[/u01/app/oracle/product/12.2.0.1/dbhome_1]
/u01/app/oracle/product/12.2.0.1/dbhome_1/bin/orabasehome =
[/u01/app/oracle/product/12.2.0.1/dbhome_1]
catctlGetOrabase = [/u01/app/oracle/product/12.2.0.1/dbhome_1]

Analyzing file /u01/app/oracle/product/12.2.0.1/dbhome_1/rdbms/admin/catupgrd.sql


*****************   Post Upgrade   *****************
°Serial   Phase #:112  [PDB_O12C] Files:1    Time: 55s
****************   Summary report   ****************
Serial   Phase #:113  [PDB_O12C] Files:1    Time: 1s
Serial   Phase #:114  [PDB_O12C] Files:1    Time: 25s
Serial   Phase #:115  [PDB_O12C] Files:1     Time: 0s

------------------------------------------------------
Phases [0-115]         End Time:[2018_01_30 11:33:48]
Container Lists Inclusion:[PDB_O12C] Exclusion:[NONE]
------------------------------------------------------

Grand Total Time: 1620s [PDB_O12C]

*/


Da sich die neue PDB im Modus RESTRICTED befindet, muss anschließend das Skript NONCDB_TO_PDB.SQL aufgerufen werden

SQL> ALTER SESSION SET container=pdb_o12c;
SQL> @?/rdbms/admin/noncdb_to_pdb.sql


Falls beim erneuten Versuch die PDB zu öffnen eine Warnung kommt
 

SQL> startup
Warning: PDB altered with errors.
Pluggable Database opened.
sollte folgendermaßen vorgegangen werden:
SQL> ALTER SESSION SET CONTAINER=CDB$ROOT;
SQL> ALTER PLUGGABLE DATABASE pdb_o12c CLOSE;
SQL> ALTER PLUGGABLE DATABASE pdb_o12c OPEN RESTRICTED;
SQL> exec dbms_pdb.sync_pdb();
SQL> ALTER PLUGGABLE DATABASE pdb_o12c CLOSE;
SQL> ALTER PLUGGABLE DATABASE pdb_o12c OPEN;


Zu überprüfen ist, ob der STATUS bei allen Meldungen von PENDING auf RESOLVED geändert wurde. Falls nicht, gibt die Spalte
ACTION Auskunft darüber, was zu tun ist. Nun sollte sich die PDB (ohne Probleme, Warnungen oder Fehler) öffnen lassen.

Tipp: Überprüfen Sie ebenfalls die ungültigen Objekte und, falls welche vorhanden, versuchen Sie diese zu rekompilieren.
 

SQL> SELECT COUNT(*) FROM dba_invalid_objects;
SQL> @?/rdbms/admin/utlrp.sql


Falls Sie wieder auf den LOCAL UNDO Modus wechseln wollen, gehen Sie analog zum Ausschalten vor.
 

SQL> shutdown immediate
SQL> startup upgrade
SQL> ALTER DATABASE LOCAL UNDO ON;
SQL> shutdown immediate
SQL> startup


Oracle erzeugt dabei automatisch einen UNDO-Tablespace für die PDB. Dazu der Auszug aus der Alertdatei:
 

...
PDB_O12C(3):CREATE SMALLFILE UNDO TABLESPACE undo_1 DATAFILE '/u01/app/oracle/oradata/o122cdb/pdb_o12c/system01_i1_undo.dbf'
SIZE 104857600 AUTOEXTEND ON NEXT 5242880 MAXSIZE 34359721984 ONLINE
...


FAZIT

Sie haben nun einen Eindruck davon bekommen, was auf Sie zukommen kann, wenn Sie auf die Container-Architektur umstellen
und dabei auch gleich noch einen Versions-Upgrade durchführen möchten.

Es sei an dieser Stelle aber noch einmal ausdrücklich erwähnt, dass dieser Tipp keine Besonderheiten wie RAC, Data Guard, ASM
oder andere Oracle Optionen berücksichtigt. Deshalb kann nicht vorhergesagt werden, welche zusätzlichen Hürden sich Ihnen
in den Weg stellen …



Weitere Interessente Artikel zum Thema:


Empfohlene Schulungen zum Thema:


Bereinigen der DB-Umgebung mittels ADRCI

Bereich:DBA, Version: ab RDBMS 11.x, Letzte Überarbeitung: 16.04.2020

Keywords:ADRCI

Bei Oracle Datenbanken werden viele Log-Files, Trace-Files, Audit-Files etc. erzeugt, die an unterschiedlichen Orten im Dateisystem und in der Datenbank gespeichert werden.

Bereits seit Oracle 11g gibt es den Parameter diagnostic_dest, der den Pfad angibt, wo die Trace-Files, Log-Files, das Alertlog, etc. abgelegt werden. Dieser Parameter zeigt auf den zentralen Ort, in dem alle Information zusammengefasst werden: das Diagnostic Repository.

 

Oracle stellt ein leistungsfähiges Tool zur Verfügung, das neben Dateien löschen, bei auftretenden ORA-Fehlern auch zugeordnete Incidents und Problems darstellen kann. Außerdem können Sie mit diesem Tool alle notwendigen Files für den Oracle Support komfortabel zusammenstellen und zippen, das Alertlog anzeigen, sowie Trace-Dateien im laufenden Betrieb löschen. In diesem Monatstipp wird nun näher beleuchtet, wie aufgetretene Incidents (Vorfälle) und Problems (Probleme) ermittelt und die zugehörigen Counter zurückgesetzt werden können. Die Informationsquelle für die aufgetretenen Problems und Incidents stellt die Tabelle V$DIAG_INFO dar.

Erster Schritt

Über die Tabelle V$DIAG_INFO ermitteln wir zunächst die Anzahl der aufgetretenen Probleme und Incidents:
 

SQL> select name,value from v$diag_info;

NAME                   VALUE
---------------------  --------------------------------------------
Diag Enabled           TRUE
ADR Base               D:\APP\ROLAND
ADR Home               D:\oracle\diag\rdbms\o19c\o19c
Diag Trace             D:\oracle\diag\rdbms\o19c\o19c\trace
Diag Alert             D:\oracle\diag\rdbms\o19c\o19c\alert
Diag Incident          D:\oracle\diag\rdbms\o19c\o19c\incident
Diag Cdump             D:\oracle\diag\rdbms\o19c\o19c\cdump
Health Monitor         D:\oracle\diag\rdbms\o19c\o19c\hm
Default Trace File    
D:\oracle\diag\rdbms\o19c\o19c\trace\o19c_ora_9620.trc
Active Problem Count   2
Active Incident Count  2


Um die zugrundeliegenden Problems bzw. Incidents genauer zu analysieren, wird der Automatic Diagnostic Report Command
Interpreter [ADRCI] auf OS-Ebene aufgerufen.
 

Zweiter Schritt - Aufruf des ADRCI auf Betriebssystem-Ebene

C:\Windows\System32>adrci

ADRCI: Release 12.2.0.1.0 - Production on Mo Jun 26 11:11:12 2017

Copyright (c) 1982, 2017, Oracle and/or its affiliates.  All rights reserved.

ADR base = "D:\app\roland"
adrci>

 

Die Ausgabe oben zeigt das per Default gesetzte ADR BASE an. Durch Aufruf der Tool-Hilfe mittels help können Sie sich weitere Informationen anzeigen lassen.

Der Output sieht dann folgendermaßen aus:

adrci> help

 HELP [topic]
   Available Topics:
        CREATE REPORT
        ECHO
        ESTIMATE
        EXIT
        HELP
        HOST
        IPS
        PURGE
        RUN
        SELECT
        SET BASE
        SET BROWSER
        SET CONTROL
        SET ECHO
        SET EDITOR
        SET HOMES | HOME | HOMEPATH
        SET TERMOUT
        SHOW ALERT
        SHOW BASE
        SHOW CONTROL
        SHOW HM_RUN
        SHOW HOMES | HOME | HOMEPATH
        SHOW INCDIR
        SHOW INCIDENT
        SHOW LOG
        SHOW PROBLEM
        SHOW REPORT
        SHOW TRACEFILE
        SPOOL

 There are other commands intended to be used directly by Oracle, type
 "HELP EXTENDED" to see the list

Dritter Schritt - ADRCI-Homes anzeigen und setzen

Um das korrekte ADRCI-Home anzuzeigen, lassen wir uns alle verfügbaren ADRCI-Homes mit dem Befehl show homes auflisten.
 

adrci> show homes
ADR Homes:
diag\clients\user_roland\host_1513561619_107
diag\clients\user_roland\host_1513561619_82
diag\clients\user_SYSTEM\host_1513561619_107
diag\clients\user_SYSTEM\host_1513561619_82
diag\rdbms\o12c\o12c
diag\rdbms\o12c2\o12c2
diag\tnslsnr\Host02\listener
diag\tnslsnr\Host02\listener_o12c2
adrci>


Setzen Sie dann per set Befehl das richtige ADRCI-HOME:

adrci> set home diag\rdbms\o12c2\o12c2
adrci> show home
ADR Homes:
diag\rdbms\o12c2\o12c2


Nachdem nun das richtige ADRCI-HOME gesetzt wurde, beginnen wir mit der Auswertung der in V$DIAG_INFO angezeigten Problems und Incidents.

Vierter Schritt - Feststellen der Problems bzw. Incidents

Dafür stehen Ihnen die Befehle SHOW PROBLEM oder SHOW INCIDENT zur Verfügung. Diese Befehle verschaffen schnell einen guten Überblick über den Datenbankserver.
 

adrci> SHOW PROBLEM

ADR Home = D:\app\roland\diag\rdbms\o12c2\o12c2:
***************************************************************

PROBLEM_ID   PROBLEM_KEY         LAST_INCIDENT  LASTINC_TIME
----------   -----------------   -------------  -------------------
1            ORA 63999              69258          2017-06-26 09:52:10
2            ORA 7445 [kslwtbctx]   74242          2017-06-26 09:55:32
2 rows fetched

 

Die Ausgabe zeigt die PROBLEM_KEYs ORA 7445 und ORA 63999, die den gleichnamigen Oracle-Fehlermeldungen zugrunde liegen, mit den zugeordneten INCIDENT Nummern an.

Ausgabe gelistet nach den aufgetretenen Incidents:

adrci> SHOW INCIDENT

ADR Home = D:\app\roland\diag\rdbms\o12c2\o12c2:
***************************************************************

INCIDENT_ID          PROBLEM_KEY          CREATE_TIME
-----------          -------------------- -------------------
69258                ORA 63999            2017-06-26 09:52:10
74242                ORA 7445 [kslwtbctx] 2017-06-26 09:55:32
2 rows fetched


Eine detailliertere Ausgabe zum aufgetretenen Incident bekommen Sie über die Abfrage:
 

adrci> show incident -mode detail


            ==> gibt Details zu ALLEN Incidents
 

adrci> show incident -mode detail -p "incident_id=<Nr>"


            ==> gibt Details zu einer speziellen Incident ID
Beispielausgabe für Incident 74242:

adrci> show incident -mode detail -p "incident_id=74242" 


ADR Home = D:\app\roland\diag\rdbms\o12c2\o12c2:
*************************************************************************

**********************************************************
INCIDENT INFO RECORD 1
**********************************************************
   INCIDENT_ID                   74242
   STATUS                        ready
   CREATE_TIME                   2017-06-26 09:55:32.403000 +02:00
   PROBLEM_ID                    3
   CLOSE_TIME                    <NULL>
   FLOOD_CONTROLLED              none
   ERROR_FACILITY                ORA
   ERROR_NUMBER                  7445
   ERROR_ARG1                    kslwtbctx
   ERROR_ARG2                    ACCESS_VIOLATION
   ERROR_ARG3                    ADDR:0x30
   ERROR_ARG4                    PC:0x7FF6E2F9A5AF
   ERROR_ARG5                    UNABLE_TO_READ
   ERROR_ARG6                    <NULL>
   ERROR_ARG7                    <NULL>
   ERROR_ARG8                    <NULL>
   ERROR_ARG9                    <NULL>
   ERROR_ARG10                   <NULL>
   ERROR_ARG11                   <NULL>
   ERROR_ARG12                   <NULL>
   SIGNALLING_COMPONENT          <NULL>
   SIGNALLING_SUBCOMPONENT       <NULL>
   SUSPECT_COMPONENT             <NULL>
   SUSPECT_SUBCOMPONENT          <NULL>
   ECID                          <NULL>
   IMPACTS                       0
   CON_UID                       0
   PROBLEM_KEY                   ORA 7445 [kslwtbctx]
   FIRST_INCIDENT                74242
   FIRSTINC_TIME                 2017-06-26 09:55:32.403000 +02:00
   LAST_INCIDENT                 74242
   LASTINC_TIME                  2017-06-26 09:55:32.403000 +02:00
   IMPACT1                       0
   IMPACT2                       0
   IMPACT3                       0
   IMPACT4                       0
   KEY_NAME                      Client ProcId
   KEY_VALUE                     ORACLE.EXE.4072_11416
   OWNER_ID                      1
   INCIDENT_FILE                
   D:\app\roland\diag\rdbms\o12c2\o12c2\trace\o12c2_ora_11416.trc
   OWNER_ID                      1
   INCIDENT_FILE                
   D:\app\roland\diag\rdbms\o12c2\o12c2\incident...
   ...\incdir_74242\o12c2_ora_11416_i74242.trc


Die Ausgabe zeigt, dass eine Zugriffsverletzung vorlag.

Optionales Package erzeugen für den Oracle Support

Diese Informationen können nun verwendet werden, um ein sogenanntes Incident- bzw. Problempaket zu erzeugen, welches anschließend an den Oracle Support hochgeladen werden kann.
 

adrci> ips pack incident 69258 in C:\temp
Generated package 2 in file C:\temp\ORA63999_20170626152636_COM_1.zip,
mode complete

adrci> ips pack problem 1 in C:\temp
Generated package 1 in file C:\temp\IPSPKG_20170626152435_COM_1.zip,
mode complete


Auch im Alertlog werden Problems mit den entsprechenden ORA-Fehlermeldungen mitprotokolliert:

Mon Jun 26 09:09:15 2017
Errors in file D:\APP\ROLAND\diag\rdbms\o12c\o12c\trace\o12c_ckpt_7244.trc:
ORA-63999: Datenträgerfehler bei Datendatei
ORA-01122: Überprüfung von Datenbank-Datei 6 nicht erfolgreich
ORA-01110: Datendatei 6: 'D:\TEMP\DATA01.DBF'
ORA-01210: Datendateiheader hat physikalischen Fehler
Mon Jun 26 09:09:15 2017
Errors in file D:\APP\ROLAND\diag\rdbms\o12c\o12c\trace\o12c_ckpt_7244.trc:
ORA-63999: Datenträgerfehler bei Datendatei
ORA-01122: Überprüfung von Datenbank-Datei 6 nicht erfolgreich
ORA-01110: Datendatei 6: 'D:\TEMP\DATA01.DBF'
ORA-01210: Datendateiheader hat physikalischen Fehler
Mon Jun 26 09:09:16 2017
System state dump requested by (instance=1, osid=7244 (CKPT)), summary=[abnormal instance
termination].


Diese Fehlermeldungen (für Incidents) würden von Oracle nun für die Dauer von einem Jahr vorgehalten und in V$DIAG_INFO als Alarmcounter solange angezeigt werden. Der SHOW CONTROL Aufruf gibt nähere Auskunft zu den Aufbewahrungsfristen.
 

adrci> SHOW CONTROL

ADR Home = D:\app\roland\diag\rdbms\o12c2\o12c2:
************************************************************
ADRID      SHORTP_POLICY  LONGP_POLICY  LAST_MOD_TIME
---------- -------------  ------------  --------------------
946440162            720          8760  2017-05-03 12:18:21

 

SHORTP_POLICY (kurzfristig) ist standardmäßig auf 30 Tage (720 Stunden) voreingestellt.

==> verwaltet die Trace- und Core-Dump-Dateien.

LONGP_POLICY (langfristig) ist standardmäßig auf 365 Tage (8760 Stunden) voreingestellt.

==> verantwortlich für Incidents (Vorfälle) und Health-Monitoring Warnungen.

Ebenso legt der Wert auch fest, wann veraltete Alert_<nummer>.xml Dateien gelöscht werden dürfen.

Um den Counter wieder auf null zurückzusetzen steht Ihnen der purge Befehl zur Verfügung:

Syntax für Incident:

adrci>  purge -i <Incident id>  [<Incident id> <Incident id> ...]
adrci>  purge -age < älter als Minuten> -type incident


Beispiel für Incident:
 

adrci> purge -i  69258 74242
adrci> purge -age 5 -type incident


Angewendet auf das vorherige Beispiel:
 

adrci> purge -age 5 -type incident
adrci> show incident

ADR Home = D:\app\roland\diag\rdbms\o12c2\o12c2:
*************************************************************************
0 rows fetched

SQL>  select name,value from v$diag_info;

NAME                   VALUE
---------------------- ------------------------------------------
Diag Enabled           TRUE
ADR Base               D:\APP\ROLAND
ADR Home               D:\APP\ROLAND\diag\rdbms\o12c2\o12c2
Diag Trace             D:\APP\ROLAND\diag\rdbms\o12c2\o12c2\trace
Diag Alert             D:\APP\ROLAND\diag\rdbms\o12c2\o12c2\alert
Diag Incident          D:\APP\ROLAND\diag\rdbms\o12c2\o12c2\incident
Diag Cdump             D:\app\roland\diag\rdbms\o12c2\o12c2\cdump
Health Monitor         D:\APP\ROLAND\diag\rdbms\o12c2\o12c2\hm
Default Trace File    
D:\APP\ROLAND\diag\rdbms\o12c2\o12c2\trace\o12c2_ora_9620.trc
Active Problem Count   0
Active Incident Count  0

 

Fazit

Um eine saubere Datenbank zu betreiben und zu erhalten, bedarf es ein wenig adminstrativer Tätigkeiten. ADRCI ist eines der Tools, die dem DBA die Arbeit zum großen Teil erleichtern.

Wenn wir Sie neugierig gemacht haben und Sie weitere nützliche Informationen zu ADRCI benötigen, besuchen Sie doch einfach unseren Reorg & Wartungskurs.


 



Weitere Interessente Artikel zum Thema:


Empfohlene Schulungen zum Thema:


Konvertierung von Ref Cursor in dbms_sql Cursor (ab 11g)

Bereich:DBA, Version: ab RDBMS 11.x, Letzte Überarbeitung: 25.05.2020

Keywords:Oracle REF_Cursor in SQL Kovertieren

Wir hatten in einem Projekt das Problem, einen Ref Cursor auszuwerten, von dem nicht bekannt war, wie viele Spalten er zurückliefert. Dies ist aber notwendig, um eine
entsprechende Anzahl an Parametern (bzw. Records/Arrays) zu definieren.

Seit Version 11g kann man nun einen Ref Cursor in einen dbms_sql Cursor umwandeln. Dann ist es möglich, die Spaltenzahl zu ermitteln und dynamisch Variablen für diese Spalten zu definieren.

Zuerst benötigen wir ein Package, das den Datentyp Ref Cursor als Rückgabewert erlaubt:

CREATE OR REPLACE PACKAGE ref_demo IS
TYPE my_curs_type IS REF CURSOR;
FUNCTION ret_curs( p_order_col IN VARCHAR2 DEFAULT 'ENAME')
RETURN my_curs_type;
END;
/


Dann definieren wir ein Package Body mit einer Funktion, die einen Ref Cursor zurückgibt:
 

CREATE OR REPLACE PACKAGE BODY ref_demo
is
FUNCTION ret_curs(
p_order_col IN VARCHAR2 DEFAULT 'ENAME') RETURN my_curs_type
IS
curs my_curs_type;
BEGIN
      OPEN curs FOR 'select * from scott.emp order by '||p_order_col||'       desc';
      RETURN curs;
END;
END;
/


Zu guter Letzt schreiben wir einen anonymen Block, der dann den Ref Cursor aus der Funktion zurückbekommt.
Et voila, wir können mit einer beliebigen Spaltenanzahl das Ergebnis weiterverarbeiten:
 

DECLARE
curs scott.ref_demo.my_curs_type;
cur_id PLS_INTEGER;
p_colcount PLS_INTEGER;
p_cursdesc dbms_sql.desc_tab;
p_colvalue VARCHAR2(4000);
BEGIN
-- Wir rufen unsere Funktion im Package mit dem Parameter auf, nach dem wir sortieren möchten
curs:=scott.ref_demo.ret_curs
('SAL');-- Nun wird der Ref Cursor in einen dbms_sql Cursor konvertiert
cur_id:=dbms_sql.to_cursor_number(curs); --- Ab 11g
-- Das describe_columns liefert uns Anzahl und Typen der Rückgabemenge
dbms_sql.describe_columns(cur_id,p_colcount,p_cursdesc);
dbms_output.put_line('Anzahl Spalten:'||p_colcount);
-- Nun definieren wir uns für jede Spalte der Rückgabemenge einen Container (hier Varchar2 mit der Länge 4000 Bytes)
FOR i IN 1 .. p_colcount LOOP
            dbms_sql.define_column(cur_id,i,p_colvalue,4000);
END LOOP;
-- Solange Zeilen in der Ergebnismenge gefunden werden gehen wir Spalte für Spalte vor
WHILE (dbms_sql.fetch_rows(cur_id)>0) LOOP
                  FOR i IN 1 .. p_colcount LOOP
                        dbms_sql.column_value(cur_id,i,p_colvalue);
                        -- Im Beispiel geben wir die gefunden Spalten
                        alle in einer Zeile aus
                        dbms_output.put(p_colvalue||';');
                  END LOOP;
                  -- Damit es schöner aussieht am Ende der Zeile einen                   Zeilenumbruch
                  dbms_output.new_line;
      END LOOP;
-- Cursor wird sauber schließen --raus
dbms_sql.close_cursor(cur_id);
-- Hier könnte Ihr Fehlerbehandlungsteil stehen :-). Den haben wir aus Vereinfachungsgründen weggelassen
END;
/


Weitere Interessente Artikel zum Thema:


Empfohlene Schulungen zum Thema:


Oracle ORDS Parameter der Datei defaults.xml

Bereich:APEX, Version: ab ORDS 21.2, Letzte Überarbeitung: 02.06.2020

Keywords:Oracle ORDS Parameter

Wer mit APEX und ORDS zusammenarbeitet ist immer  froh, wenn er eine Übersicht über die aktuellen ORDS Parameter findet.
Anbei haben wir einige zusammengestellt:
Sie erhalten eine Komplett-Übersicht aller Oracle ORDS Parameter für die Oracle Version 19.4 hier.
Aktuell ist derzeit die Oracle ORDS Version 19.4.6 (1.6.2020)

Parameter in der Datei defaults.xml können manuell mit einem Texteditor geändert werden, oder auch durch:

$JAVA_HOME/bin/java -jar ords.war set-property <parametername> <parametervalue>
also z.B.
$JAVA_HOME/bin/java -jar ords.war set-property jdbc.auth.enabled true



Die folgenden Parameter betreffen die Datenbank

<entry key="db.connectionType">basic</entry>
<entry key="db.hostname">my_db_server</entry>
<entry key="db.port">1521</entry>
<entry key="db.sid">o20c</entry>


oder bei einem Failover Eintrag

<entry key="db.connectionType">customurl</entry>
<entry key="db.customURL">jdbc:oracle:thin:@(DESCRIPTION=(FAILOVER=ON)(ADDRESS_LIST=
  (LOAD_BALANCE=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=muniqsoft-training.de)(PORT=1521)))
  (CONNECT_DATA=(SERVICE_NAME=ISPRD)))|</entry>


Wenn es Probleme beim Verbinden gibt (z.B. Could not map a database) sollte versucht werden, eine Verbindung der Datenbank von der Shell/DOS direkt zur Datenbank durchzuführen mit:

sqlplus apex_public_user/<pwd>@my_db_server:1521/o20c


Die folgenden 4 Parameter sind verantwortlich für das Debugging und Logging
Hinweiß: Für Produktivmaschinen sollte Debuggung auf "false" stehen !

<entry key="debug.debugger">true</entry>
<entry key="debug.printDebugToScreen">true</entry>
<entry key="log.logging">true</entry>
<entry key="log.maxEntries">500</entry>


Wenn Sie eine eigene Datei in einem selbst gewählten Pfad für die Fehlermeldungen verwenden möchten:

<entry key="error.externalPath">/opt/tomcat/latest/logs</entry>


Diese Datei muss das Format {servererrorcode}.html besitzen
also z.B 404.html oder 500.html

Die nächsten 4 Parameter kümmern sich um die Konfiguration der internen Java Parameter und spielen für die Performance des REST Service eine wichtige Rolle:

<entry key="jdbc.InitialLimit">20</entry>
<entry key="jdbc.MaxConnectionReuseCount">3000</entry>
<entry key="jdbc.MaxLimit">100</entry>
<entry key="jdbc.MaxStatementsLimit">100</entry>


Maximale Anzahl der Zeilen definieren,die bei einer Abfrage zurückommen dürfen (Default: 500)

<entry key=”misc.pagination.maxRows”>2500</entry>


REST Enables SQL freischalten:

<entry key="restEnabledSql.active">true</entry>


Datenbank API via REST freischalten: 

<entry key="database.api.enabled">true</entry>

oder auf der Kommandozeile: 

java -jar ords.war set-property database.api.enabled true


Pluggable Database API abschalten (ab 19.2):

<entry key="database.api.management.services.disabled">true</entry>

 

oder wieder auf der Kommandozeile: 

java -jar ords.war set-property database.api.management.services.disabled true


Weitere Interessente Artikel zum Thema:


Empfohlene Schulungen zum Thema:


Oracle Forms 6i mit Win 10 und Oracle 18 XE

Bereich:Forms, Version: ab RDBMS 18.x, Letzte Überarbeitung: 23.05.2020

Keywords:Oracle Forms 6i auf Oracle 18c XE

Manchmal hat man doch Sehnsucht nach der alten Zeit, in unserem Fall nach einer Oracle Forms 6i Version aus dem Jahre 1998.
Im folgenden Tipp versuchen wir dieses alte Release nochmal zum Laufen zubringen. Ein kleiner Spoiler vorab: DAS GANZE IST NICHT SUPPORTED !
Das bedeutet, nicht von mir, noch von Oracle. Also bitte erstellen Sie von allem was Ihnen lieb und teuer ist ein funktionierendes (und damit getestetes) Backup.

Vorbereitung:
Wir besorgen uns eine alte Oracle Forms6i / Reports6i Version (Oracle Developer 6i) (Die gab es damals noch auf CD :-) )
Wenn Sie einen Wartungsvertrag mit Oracle haben, besorgen Sie sich bitte zusätzlich die Patches 3 und 19.

Installieren Sie das Grundprodukt und danach den Patch 18.

MOS: 6194129 Patch 18
 
Kleine Anmerkung aus der Oracle Doku: Forms 6 war nie Zertifiziert für Oracle 10.2 oder höher


Jetzt kommt der spannende Teil (hat mich fast 2 Tage gekostet):
Tauschen Sie die Dateien (Pfad c:\orant\bin) nn60.dll und nnmb60.dll aus dem Patch 3 gegen die aktuellen aus.
Wenn Sie die Dateien nicht zur Verfügung haben, einfach die beiden Namen in die Suchmaschine Ihrerer Wahl eingeben und schon findet man zwei Treffer zum Download.

Praktische Einträge in der Registry für die Oracle Forms6i Software:
Pfad: Computer\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\ORACLE\HOME0

LOCAL=XE
NLS_LANG=GERMAN_GERMANY.UTF8


Nun in  der SQLNET.ORA auf der Serverseite folgendes editieren:

SQLNET_ALLOWED_LOGON_VERSION=8
SQLNET.ALLOWED_LOGON_VERSION_SERVER=8
SQLNET.ALLOWED_LOGON_VERSION_CLIENT=8

Anmerkung: Eine Oracle 18c DB verwendet einen anderen Hash-Algo für die Passwort-Verschlüsselung im Vergleich zu Oracle 8.
Wir sind durch diese Einstellung also auf dem Sicherheitsniveau einer Oracle 8 Datenbank. Kredikarten-Daten würde ich in der DB nicht speichern :-)

Oracle Forms 6i arbeitet mit dem Zeichensatz AL16UTF8 nicht zusammen, deswegen müssen wir den bei der XE DB als Default eingestellten Zeichensatz ändern.
Achtung das sollte direkt nach der Installation der DB und noch vor dem Datenimport passieren !!!

SHUTDOWN IMMEDIATE;
STARTUP MOUNT;
ALTER SYSTEM ENABLE RESTRICTED SESSION;
ALTER DATABASE OPEN;
ALTER DATABASE CHARACTER SET INTERNAL_USE UTF8; -- oder der geliebte Zeichensatz WE8MSWIN1252
ALTER SYSTEM DISABLE RESTRICTED SESSION;


Nun machen wir einen Test auf den alten Oracle 8 Passwort-Hash (sonst bekommen Sie bei der Forms Anmeldung immer einen ORA-01017 Fehler zurück):

select password from sys.user$
where name='SCOTT'; --Leer


Neues Passwort für den Benutzer SCOTT setzen

ALTER USER SCOTT IDENTIFIED BY tiger;


Nun sollte das "alte" Password wieder zu sehen sein:

select password from sys.user$
where name='SCOTT';
=>F894844C34402B67


Jetzt gehen wir in die Client-Datei c:\orant\net80\tnsnames.ora und tragen ein:

xe =
  (DESCRIPTION =
    (ADDRESS_LIST =
        (ADDRESS =
          (PROTOCOL = TCP)
          (Host = <Ihr(e) IP oder Hostname>)
          (Port = 1521)
        )
    )
    (CONNECT_DATA = (SID = xe)
    )
  )

 

Versuchen Sie sich jetzt von Forms bei der DB anzumelden: File => Connect

User Name: scott
Password: tiger
Database: xe


Und nun viel Spaß mit nostalgischen Forms Sessions.
Wenn Sie Ihre alten Forms 6i Masken von uns auf Oracle APEX portieren lassen möchten, melden Sie sich gerne bei uns, wir helfen Ihnen gerne...



Weitere Interessente Artikel zum Thema:



Empfohlene Schulungen zum Thema:


Oracle APEX Patch für 20.1 (30990551)

Bereich:APEX, Version: ab APEX 20.1, Letzte Überarbeitung: 09.07.2020

Keywords:APEX, Patch 20.1

Oracle APEX 20.1 ist gerade mal 4 Wochen heraus und schon gibt es den ersten Patch, der ein paar praktische Bugfixes mitbringt (u.A. die falsche Timeout Warnung wird behoben)
Sie können den Patch von Oracles Support Seite herunterladen und in einer Minute installieren.

Vorgehensweise:
1. Webserver stoppen
1 a, Sie haben das EPG im Einsatz:

SELECT dbms_xdb.gethttpport FROM dual;

Wenn ein Port <>0 zurückommt, dann haben Sie das EPG im Einsatz. Dann merken Sie sich den Port bitte.

EXEC dbms_xdb.sethttpport(0); -- EPG abschalten


1 b, Sie haben den TomCat im Einsatz

systemctl stop tomcat


1 c, Sie haben TomCat und Apache als Reverse Proxy im Einsatz:

systemctl stop httpd      # z.B. für Centos / RedHat Linux
systemctl stop apache2    # z.B. für SUSE Linux


2. Patch einspielen (z.B. in den Ordner /u01/software)

cd /u01/software
unzip p30990551_2010_Generic.zip
cd /u01/software/30990551
sqlplus "/ as sysdba" @catpatch.sql

Falls Sie in einer CDB installieren möchten:
sqlplus "/ as sysdba" @catpatch_con.sql

oder in einer CDB in einem Application Container:
sqlplus "/ as sysdba" @catpatch_appcon.sql


3. Image Ordner aktualisieren

3 a. bei EPG (hier werden die Bilder in der datenbank gespeichert)

sqlplus "/ as sysdba" @epg_install_images.sql /u01/software/30990551


3 b, bei TomCat (ohne Apache)
Prüfen Sie bitte zuerst, ob das Verzeichnis dort existiert (kann je nach Installation auch auf einem anderem Pfad liegen)

find /opt/tomcat/latest/webapps/i -type f | wc -l
=> da sollten dann mehr als 14.000 Dateien liegen
cp -rf /u01/software/30990551/images /opt/tomcat/latest/webapps/i


3 c, Bei TomCat (mit Apache)

find /var/www/html/i -type f | wc -l
=> da sollten dann mehr als 14.000 Dateien liegen (bei uns waren es ca 20.000)
cp -rf /u01/software/30990551/images/* /var/www/html/i


4. Webserver wieder starten
4 a. Bei EPG:

EXEC dbms_xdb.sethttpport(<port aus 1a>);
Beispiel:
EXEC dbms_xdb.sethttpport(8080);


4 b, Tomcat ohne Apache

systemctl start tomcat


4 c, Sie haben TomCat und Apache als Reverse Proxy im Einsatz:

systemctl start httpd      # z.B. für Centos / RedHat Linux
systemctl start apache2    # z.B. für SUSE Linux


5. Schlußprüfungen:
 5 a. Patch installiert:

SELECT patch_version, installed_on
FROM apex_patches
WHERE patch_number = 30990551;
 

PATCH_VERSIONINSTALLED_ON
2020.05.202020-05-25 08:45:24

Gibt es ungültige Objekte?

select count(*) from dba_objects
where status='INVALID';
=> hier sollte 0 zurückkommen


Dies und noch mehr lernen Sie bei einem unserer fünf verschiedenen APEX-Kurse, die wir seit 2005 unterrichten!



Weitere Interessente Artikel zum Thema:


Empfohlene Schulungen zum Thema:


Postgres Backup einer Datenbank mit tar

Bereich:Postgres, Version: ab PG 13, Letzte Überarbeitung: 14.06.2021

Keywords:Postgres Backup

Wer seine Postgres Datenbank liebt/braucht, sollte regelmäßig ein Backup von ihr machen.
Der nachfolgende Tipp soll ein Skript dafür zur Verfügung stellen. Bitte testen Sie das Skript bitte ausgiebif durch bevor Sie es auf einer Produktiv-Datenbank ausführen.
Wir übernehmen keine Haftung/Gewähr für evtl Fehler.

export PG_BACKUP_PATH=/u01/pg/backup
export PG_DATABASE=postgres
export PG_DATA=/var/lib/pgsql/12/data
export PG_PATH=/usr/pgsql-12/bin
export PG_USER=postgres
export PG_ARCHIVE_PATH=/u01/pg/archivedir
export BACKUP_TIME=`date +"%Y_%m_%d_%H_%M_%S"`
export DAY_FOLDER=$PG_BACKUP_PATH/$BACKUP_TIME/PG_BAK_$BACKUP_TIME.tar.bz2

mkdir -p $PG_BACKUP_PATH/$BACKUP_TIME

echo "Starte Postgres Datenbank Backup (DB=$PG_DATABASE, Backup-Pfad=$PG_BACKUP_PATH/$BACKUP_TIME)"
WAL_START=`ls -rt1 $PG_ARCHIVE_PATH | tail -1`
BACKUP_START=`$PG_PATH/psql -q -U $PG_USER -d $PG_DATABASE -c "SELECT pg_start_backup('PG_BACKUP');" -P tuples_only -P format=unaligned`
BACKUP_START_TIME=`date +"%m.%d.%Y %H:%M:%S"`

if [ $? -ne 0 ]; then
  echo "PG Backup konnte nicht gestartet werden um `date`"
  exit 1;
fi
# Backup Befehl
cd $PG_DATA
tar -cjf $DAY_FOLDER --exclude='pg_wal' *
# Backup Befehl Ende

BACKUP_END=`$PG_PATH/psql -q -U$PG_USER -d $PG_DATABASE -c "SELECT pg_stop_backup();" -P tuples_only -P format=unaligned`

echo "Sichern der WAL´s ab $WAL_START"
find $PG_ARCHIVE_PATH -type f -newer $PG_ARCHIVE_PATH/$WAL_START  -exec cp {} $PG_BACKUP_PATH/$BACKUP_TIME \;
#zip -m $PG_BACKUP_PATH/$BACKUP_TIME/wal_files.zip $PG_BACKUP_PATH/$BACKUP_TIME/0*
cd $PG_BACKUP_PATH/$BACKUP_TIME
tar -cjf PG_WAL_$BACKUP_TIME.tar.bz2 0* --remove-files

######################################### R E S T O R E #####################

echo "Erstelle Restore Skript:$PG_BACKUP_PATH/$BACKUP_TIME/restore_$BACKUP_TIME.sh"
cat << EOF > $PG_BACKUP_PATH/$BACKUP_TIME/restore_$BACKUP_TIME.sh
export PG_BACKUP_PATH=$PG_BACKUP_PATH
export PG_DATABASE=$PG_DATABASE
export PG_DATA=$PG_DATA
export PG_PATH=$PG_PATH
export PG_USER=$PG_USER
export PG_ARCHIVE_PATH=$PG_ARCHIVE_PATH
export BACKUP_TIME=$BACKUP_TIME
EOF

cat << 'EOF' >> $PG_BACKUP_PATH/$BACKUP_TIME/restore_$BACKUP_TIME.sh
$PG_PATH/pg_ctl stop
mv $PG_DATA $PGDATA.old
mkdir -p $PG_DATA/pg_wal
chmod -R 700 $PG_DATA
cd $PG_BACKUP_PATH/$BACKUP_TIME
tar xvjf PG_BAK*.tar.bz2 -C $PG_DATA
if [ "$?" -ne 0 ]; then
        echo "Konnte DB nicht auspacken, Abbruch"
        exit;
fi
tar xvjf PG_WAL*.tar.bz2 -C $PG_DATA/pg_wal

if [ "$?" -ne 0 ]; then
        echo "Konnte WAL Dateien nicht auspacken, Abbruch"
        exit;
fi

touch $PG_DATA/recovery.signal
echo "Sie können recovern vom Zeit des Backups ($BACKUP_TIME) bis aktuell (latest)"
echo "Geben Sie das gewünschte Datum in der Form YYYY-MM-DD HH24:MI:SS an (z.B. 2020-06-23 15:34:01)"
read -p 'Zeitpunkt für Recovery: [latest]' -r RECOVER_TIME
RECOVER_TIME=${RECOVER_TIME:-latest}
if [ "$RECOVER_TIME" = "latest" ]; then
    echo "recovery_target_timeline = '$RECOVER_TIME'" >> $PG_DATA/postgresql.auto.conf
else
    echo "recovery_target_time = '$RECOVER_TIME'" >> $PG_DATA/postgresql.auto.conf
fi

echo "restore_command = 'cp /u01/pg/archivedir/%f %p'" >> $PG_DATA/postgresql.auto.conf
$PG_PATH/pg_ctl start
if [ "$?" -ne 0 ]; then
        cat `ls -tpr $PG_DATA/log | grep -v / | tail -n 1` | tail -10
fi
EOF
chmod u+x $PG_BACKUP_PATH/$BACKUP_TIME/restore_$BACKUP_TIME.sh

BACKUP_END_TIME=`date +"%m.%d.%Y %H:%M:%S"`
echo "Backup beendet: Start:$BACKUP_START_TIME Ende:$BACKUP_END_TIME "
echo "Größe/Ordner:`du -sh $PG_BACKUP_PATH/$BACKUP_TIME`"
ls -l  $PG_BACKUP_PATH/$BACKUP_TIME | awk {' print $6,$7,$8,$9'}


Kleine Erklärung zum Postgres Datenbank Backup Skript:

Im ersten Block werden die Variablen definiert. Prüfen Sie hier bitte, ob alle Voreinstellungen bei Ihnen passen.
in der Variablen WAL_START steht die letzte WAL-Datei vor dem Backup. Damit benötigen wir alle WAL Dateien, die danach angefallen sind, um das Backup wieder funktionstüchtig zu bekommen.
BACKUP_START setzt die Datenbank in den Backup-Modus. Das ist wichtig, weil wir danach erst Dateien aus der Datenbank Online kopieren dürfen.
Der Backup Befehl
tar -cjf $DAY_FOLDER --exclude='pg_wal' *
erzeugt ein Backup im Tar-Zip Format vom Hauptordner der Datenbank. Achtung: Eventuell vorhandene externe Tablespace-Ordner werden dadurch nicht mitkopiert!!!
BACKUP_END nimmt die Postgres Datenbank wieder aus dem Backupmodus heraus.

Der find Befehl findet alle nach Begin des Backups erzeugten WAL-Dateien und kopiert die auch in den Backup-Ordner.
Der nachfolgende Befehl packt die WAL Dateien auch in eine TAR-Datei.

Zusätzlich wird ein Restore Skript erzeugt. Dieses fragt sich beim Start auf welche Zeit denn zurückgesetzt werden soll. Wenn Sie auf den letztmöglichen Zeitpunkt zurückgehen möchten, geben Sie bitte latest ein.
Danach wird noch eine kleine Nachricht über den Erfolg des Backups ausgegeben.

HAPPY BACKUP

Für weitere Fragen rund um Postgres, besuchen Sie doch einen unserer Postgres-Kurse. 
 



Weitere Interessente Artikel zum Thema:


Empfohlene Schulungen zum Thema:


Lange Laufzeiten bei Zugriff auf DBA_FREE_SPACE oder DBA_EXTENTS verbessern

Bereich:DBA, Version: ab RDBMS 12.x:RDBMS 18.1:RDBMS 18.3:RDBMS 19.3, Letzte Überarbeitung: 18.01.2021

Keywords:X$KTFBUE, DBA_FREE_SPACE, X$KTFBUE

Wir hatten jüngst eine (vermeintlich) einfache Query, die aber 5 Min lief. Sie lautete:
 

SELECT sum("8_Bis_64"), sum("65_Bis_256"), sum("257_Bis_512")
  FROM (SELECT
           CASE WHEN BLOCKS between 8 and 64 THEN '1' END as "8_Bis_64",
           CASE WHEN BLOCKS between 65 and 256 THEN '1' END as "65_Bis_256",
           CASE WHEN BLOCKS between 257 and 512 THEN '1' END as "257_Bis_512"
         FROM dba_free_space);


Warum lief sie so lange?
Wenn man sich den Ausführungsplan ansieht, stellt man fest, dass eine der internen Tabellen keine Statistiken besitzt.

Tipp 1:
Die Tabelle X$KTFBUE hat keine Statistiken und bekommt auch keine, weil es sehr lange dauern könnte diese zu berechnen (ist also ein Feature und kein Bug)
Übrings, bekommt die Tabelle auch keine Statistiken durch den Aufruf:
 

EXEC DBMS_STATS.GATHER_FIXED_OBJECTS_STATS


Achtung, es sind schon Fälle beobachtet worden, da lief die Statistikerzeugung über 8 Stunden !
Also bitte nicht einfach nur:
 

EXEC dbms_stats.gather_table_stats('SYS','X$KTFBUE');


sondern zuerst:
 

alter session set "_optimizer_cartesian_enabled" = false;
-- und danach:
EXEC dbms_stats.gather_table_stats('SYS', 'X$KTFBUE');


Laufzeit bei uns: 14.8s.
Oder als abgeschätzte Statistik (5%):
 

exec dbms_stats.gather_table_stats('SYS', 'X$KTFBUE', estimate_percent=>5);


Laufzeit bei uns: 6.1s.
Oder als abgeschätzte, parallelisierte Statistik:
 

exec dbms_stats.gather_table_stats('SYS', 'X$KTFBUE', estimate_percent=>5, degree=>4);


Laufzeit bei uns: 6.0s (bringt also bei unserer Hardware nichts, evtl. haben Sie ja eine bessere Maschine :-) )
P.S.: Auch mit noch kleineren Sample-Sizes (1%, 0.1% wurde es bei uns nicht schneller)

Wer auf die Berechnung der Statistik verzichten möchte und sich gleich traut, einfach selbst den Statistik-Wert einzutragen (traue keiner Statistik, ....)

BEGIN
dbms_stats.set_table_stats (
ownname=>'SYS',
tabname=>'X$KTFBUE',
numrows=>50000,
avgrlen=>47,
statown=>'SYS');
END;


Bei uns gab es keine signifikanten Unterschiede in der Laufzeit, auch wenn wir Faktor 10 die Statistik zu hoch eingesetzt hatten, aber das ist hier jetzt unsupported ...


Oder noch besser zuerst:
Tipp 2: Leeren Sie den Mülleimer:
 

purge dba_recyclebin;


und erzeugen Sie neue Statistiken für den Mülleimer:

 exec dbms_stats.gather_table_stats('SYS','RECYCLEBIN$')


Am Besten schaltet man dem Mülleeimer sowieso ganz aus (sagt auch Oracle):

alter system set recyclebin=off scope=spfile;




Behind the scenes:
Wer wissen möchte, wieviele Zeilen nun in der Tabelle X$KTFBUE enthalten sind:
 

SELECT ft.name, ts.analyzetime, ts.rowcnt, ts.samplesize
  FROM tab_stats$ ts, v$fixed_table ft
 WHERE ts.obj#=ft.object_id
   AND ft.name='X$KTFBUE';
NAME     ANALYZETIME         ROWCNT SAMPLESIZE
-------- ------------------- ------ ----------
X$KTFBUE 19.10.2020 16:05:51  18640       4895


Laufzeit der Query nach den Tipps:
0.031 Sekunden, das kann sich doch sehen lassen, oder ?

Mike Dietrich von Oracle hat das Thema Fixed Table Stats in einem seiner Blogs auch schon mal thematisiert.



Weitere Interessente Artikel zum Thema:


Empfohlene Schulungen zum Thema:


Oracle APEX 20.2 Installation

Bereich:APEX, Version: ab APEX 20.2, Letzte Überarbeitung: 20.11.2020

Keywords:Oracle APEX

Mit dem Datum 21.10.2020 kam die Oracle APEX 20.2 Version nun doch sehr überrraschend frühzeitig heraus (im Vergleich zu den letzten Jahren). Aber gute Nachrichten kann man in den aktuell schweren Zeiten ja immer gebrauchen ... :-). Wir haben uns natürlich gleich am Tag 1 der Veröffentlichung um eine Migration unserer APPs auf das neue Release gekümmert.

1. Herunterladen und Auspacken der Software

APEX 20.2 Download
 

unzip apex_20.2.zip


2. Installation / Upgrade auf Oracle APEX 20.2 (in einer Non-CDB Datenbank)

wir haben mal das relativ neue Installationsskript apexsilentins.sql für die Installation verwendet.
Es ersetzt die folgenden Skripte:

  • apexinsql.sql
  • apex_rest_config.sql
  • apxchpwd.sql

Ausserdem erstellt es eine neue ACL (power_users.xml) und der Benutzer APEX_PUBLIC_USER wird automatisch entsperrt (endlich!)
Dadurch benötigt es jedoch auch mehr Parameter als das alte apexins.sql Skript. Der Aufruf lautet hier:
 

sqlplus "sys/syspass as sysdba" @apxsilentins <tablespace_1> <tablespace_2> <temp_tablespace> </image_path/> <pwd_1>  <pwd_2> <pwd_3> <pwd_4> <pwd_5>


Parameter 1: Name des Tablespace für den Application Express Application Benutzer (APEX_200200)
Parameter 2: Name des Tablespace für den Application Express files user (FLOWS_FILES)
Parameter 3: Name des temporären Tablespace (bzw. Tablespace Gruppe) für Sortierungen / Indexerstellung / Sort Merge Joins u.v.w.
Parameter 4: Virtuelles Verzeichnis für APEX Bilder/CSS/Javascript Dateien
Parameter 5: Passwort für APEX_PUBLIC_USER
Parameter 6: Passwort für APEX_LISTENER
Parameter 7: Passwort für APEX_REST_PUBLIC_USER
Parameter 8: Passwort für Workspace Internal APEX internal ADMIN user (Achtung, Sie müssen sich hier an strenge Passwort-Richtlinien erhalten, also bitte nicht APEX123 verwenden :-) )

Also als Beispiel:
 

sqlplus "sys/syspass as sysdba" @apxsilentins SYSAUX SYSAUX TEMP /i/ apex apex apex Super13#


Die Installationszeit betrug auf unserem Server: 15 min

Wenn Sie einen Webserver verwenden z.B. Apache TomCat Version 9.x, dann kopieren Sie bitte den images Ordner aus dem Apex Zip File auf
Windows: C:\Program Files\Apache Software Foundation\Tomcat 9.0\webapps
Linux: /opt/tomcat/latest/webapps
als Ordner i !, bzw. den Namen, den Sie bei der Installation als Parameter 4 angegeben haben. Wenn es dort schon einen Ordner gibt, bietet es sich an, den erstmal nur umzubennen und nicht gleich zu löschen!
 

z.B. mv i i_old_22_10_2020
bzw für Windows.
move i i_old_22_10_2020


Wenn Sie das interne EPG verwenden (also keinen externen Werserver), muss noch zusätzlich das folgende Skript gestartet werden:
@apex_epg_config.sql <Pfad zu den Images>

Achten Sie aber darauf, den Ordner-Namen apex nicht mit im Pfad anzugeben, weil das Skript ihn selbst dazufügt, also wenn die Bilder unter (Windows: c:\temp\apex oder Linux: /tmp/apex) liegen, lautet der Aufruf:
 

Windows: @apex_epg_config c:\temp
Linux: @apex_epg_config /tmp


 



Weitere Interessente Artikel zum Thema:


Empfohlene Schulungen zum Thema:


Oracle JSON Date Datentyp Problem

Bereich:PL/SQL:SQL, Version: ab RDBMS 21.1, Letzte Überarbeitung: 14.01.2021

Keywords:Oracle JSON json_serialize json_scalar

Wer schon mal mit dem JSON Date Datentyp gearbeitet hat, ist sich dessen Problemen sicherlich bewusst.
Wir wollen uns im nachfolgenden Atrikel dem Problem mal annehmen:

Nehmen wir mal die in Oracle 21c neue eingeführte Funktion json_scalar und json_serialize

JSON_SCALAR wandelt einen Text/ ein Datum oder eine Anzahl in eine interne BLOB Repräsentanz.

SELECT json_scalar(date '2021-01-01') as datum FROM dual;
DATUM                                                                           
--------------------------------------------
22323032312D30312D30315430303A30303A303022


Wenn wir das zurückwandeln passiert folgendes:

SELECT json_serialize(  (date '2021-01-01')) as datum FROM dual;

DATUM                                                                                                           
---------------------
"2021-01-01T00:00:00"


Da fallen einem gleich zwei Problemzonen ins Auge:
1. "" am Anfang / Ende
2. ein T zwischen Datum und Uhrzeit

Der erste Versuch scheitert deswegen auch:

select to_date(json_serialize(json_scalar(date '2021-01-01')),'YYYY-MM-DD"T"HH24:MI:SS') as scalar_datum
from   dual;

ORA-01841: (Volles) Jahr muss zwischen -4713 und +9999 liegen und darf nicht 0 sein
01841. 00000 -  "(full) year must be between -4713 and +9999, and not be 0"


Das liegt an der etwas eigenartigen Behandlung von Gänsefüßen bei Oracle Strings

select to_date('"01.01.01"') from dual;
ORA-01858: Ein nicht numerisches Zeichen wurde gefunden, während ein numerisches Zeichen erwartet wurde
01858. 00000 -  "a non-numeric character was found where a numeric was expected"

oder auch

select to_date('"01.01.01"','"DD.MM.YY"') from dual;
RA-01861: Literal stimmt nicht mit Formatzeichenfolge überein
01861. 00000 -  "literal does not match format string"

auch das geht schief:

select to_date('"01.01.01"','"""DD.MM.YY"""') from dual;
ORA-01861: Literal stimmt nicht mit Formatzeichenfolge überein
01861. 00000 -  "literal does not match format string"


Deshalb lösen wir das Problem mit zwei Funktionen:
1. Wir ersetzen "" durch nix
2. Wir konvertieren den String mit Hilfe der Funktion to_date und dem Format String 'YYYY-MM-DD"T"HH24:MI:SS'

select
to_date(replace(json_serialize(json_scalar(date '2021-01-01')),'"',''),'YYYY-MM-DD"T"HH24:MI:SS') as scalar_datum
from   dual;

SCALAR_DATUM       
-------------------
01.01.2021 00:00:00


Weitere Tipps erhalten Sie in einem unserer Oracle Kurse...



Weitere Interessente Artikel zum Thema:


Empfohlene Schulungen zum Thema:


Tablespace Map as ASCII Ausgabe

Bereich:DBA, Version: ab RDBMS 19.1:RDBMS 21.1:RDBMS 18.1, Letzte Überarbeitung: 12.03.2025

Keywords:TBS Map, Tablespace Darstellung

Ein Bild sagt ja bekanntlich mehr als 1001 Worte, deswegen ist es manchmal ganz praktisch sich die Belegung seines Tablespace mal grafisch anzuzeigen.
 

WITH 
FUNCTION tbs_map  (
    p_file_id   IN NUMBER DEFAULT 1,
    p_width     IN NUMBER DEFAULT 200,
    p_blocks    IN NUMBER DEFAULT 128)
RETURN CLOB
IS
    t_clob              CLOB:=empty_clob();
    t_clob_out          CLOB:=empty_clob();
    t_clob_len          INT;
    tbs_name            VARCHAR2(128);
    tbs_free_space      NUMBER;
    tbs_file_size       NUMBER;
    tbs_block_size      INT;
    cnt                 INT:=0;
BEGIN
t_clob:=' ';
FOR c IN (select tablespace_name,round(sum(bytes)/1024/1024) sum_bytes
INTO tbs_name,tbs_free_space
from dba_free_space
where file_id=p_file_id
group by tablespace_name ) LOOP
    tbs_name:=c.tablespace_name;
    tbs_free_space :=c.sum_bytes;
END LOOP;
IF tbs_name IS NULL THEN
    RETURN 'Datafile '||p_file_id||' not existing';
END IF;
FOR c IN (select round(bytes/1024/1024) file_size,block_size from v$datafile
where file#=p_file_id) LOOP
    tbs_file_size  := c.file_size;
    tbs_block_size := c.block_size;
END LOOP;

t_clob_out :='Tablespace='||tbs_name ||', File='||p_file_id||', Size='||tbs_file_size||', MB Free='||tbs_free_space||
' MB, Blocks='||p_blocks||', (c) 2025 by Muniqsoft Training '||chr(10);
--t_clob:=' ';
    FOR c IN (
        SELECT block_seg,replace(lpad('.',ceil(sum_blocks/p_blocks)+1,CASE segment_type
            WHEN 'TABLE' THEN 'T'
            WHEN 'INDEX' THEN 'I'
            WHEN 'INDEX PARTITION' THEN 'J'
            WHEN 'LOBSEGMENT' THEN 'L'
            WHEN 'LOB PARTITION' THEN 'L'
            WHEN 'CLUSTER' THEN 'C'
            WHEN 'TYPE2 UNDO' THEN 'U'
            WHEN 'ROLLBACK' THEN 'U'
            WHEN 'TABLE PARTITION' THEN 'P'
            WHEN 'TABLE SUBPARTITION' THEN 'P'
            WHEN 'LOBINDEX' THEN 'X'
            WHEN 'NESTED TABLE' THEN 'N'
            WHEN 'O' THEN 'O'
            ELSE '?' END),'.','') as stype FROM (
SELECT block_seg,sum_blocks,segment_type ,row_number()
over (partition by block_seg order by sum_blocks desc) rn
from (
select floor(block_id/p_width) block_seg ,sum(blocks) sum_blocks,segment_type
from
(select block_id,blocks,segment_type from dba_extents where file_id=p_file_id
UNION ALL
select block_id, blocks, 'O' from dba_free_space where file_id=p_file_id
)
group by floor(block_id/p_width),segment_type
order by 1))
where rn=1)
    LOOP
        dbms_lob.writeAppend(t_clob,length(c.stype),c.stype);
        cnt:=cnt+1;
    END LOOP;
    t_clob_len:= (dbms_lob.getlength(t_clob))/p_width;

    FOR i IN 1 .. ceil(t_clob_len) LOOP
        dbms_lob.writeAppend( t_clob_out, p_width+7, lpad(to_char((i-1)*p_width*tbs_block_size),5,'0')||' '||DBMS_LOB.SUBSTR(t_clob,p_width,(i-1)*p_width+6)||chr(10) );
    END LOOP;

       RETURN DBMS_LOB.SUBSTR(t_clob_out,dbms_lob.getlength(t_clob_out)-6,1)
    ||    chr(10)||'(O)=Leer, (T)able, (I)ndex, (J)ndex Partition, (P)artition, (U)ndo/Rollback, (C)luster, (L)obsegment, (N)ested Table, Lob Index(x)';


END;
select TBS_MAP(p_file_id =>2, p_blocks=>128,p_width=>128) from dual;
/


Folgende Parameter können übergeben werden:
p_file_id Nummer der Datendatei (aus v$DATAFILE)
p_blocks Anzahl der Blöcke, die zu einem Zeichen zusammengefasst werden sollen (Derzeit nur sinnvoll mit Wert 128 einsetzbar, andere Werte werden bald nachgereicht)
Das bedeutet, wenn 128 Tabellen Blöcke hintereinander leiegen , wird dafür ein (T) ausgegeben für 256 Tabellenblöche zwei (TT) u.s.w.
Liegen in diesen 128 Blöcken mehrere verschiedene Objekte, wird das größte nur angezeigt!
p_width= Ausgabebreite in Zeichen, alle mit Monitoren größer als 40" können da natürlich die Breite mehr ausreizen :-)

Sollte der SELECT lange laufen, fehlt ihnen evtl eine Statistik auf X$KTBFUE (siehe Link unten im Artikel)

Die Ausgabe sieht dann z.B. so aus

Tablespace=SYSAUX, File=2, Size=2304, MB Free=796 MB, Blocks=128, (c) 2025 by Muniqsoft Training
00000 LLIIIIILIITLIILLITTIIITTIIIIJILIIIILILIIILTIILITTTTTITIIJJJJJJJJTTIPIIITIPIIIPPIIIPTTIIPIIIIITPPIITITTTPPLLLLLLLLLLLLLLLLL
10485 LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLJJJJJITIIIIIIITILLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLILLIITILLTIIIIIIIIIIIIIII
20971 LLPPTIIITIIPPPIIIIIIPTPIIITIPIIIIIIILILLITPPPPPPJPIPIPLLLPPPPPPPPITPPPIIPPPIIPPPPPPPIIPCIPPIPIIPPITIIPPPPPIIPIIPPIPPIPITCPPPPPIP
31457 IPPIIPPIPIPPLIPPPPTPIIITTTPIPPIPIIPIPPPTIIPIIPTIIPPIPPIJPIIIPPITIIIIPIIPIILTTPJJPTPIIIIPITTIITIJPTIIIPIIPPPPPPPIIPPJTIIIJIPIJPPI
41943 ITTIPIIPIPIIPJPIPPPIIPTIITIPIPPPITIIPIIPIIIIPPPIIPTIIIJITJIIPIIIPPIPIITPITPIIIPPIIPIIIIIIIIIJPLIIJPPIPIIIJJILPTIIIIIIIIIIITOPIII
52428 ITJTTTIPIIIIIIIIIIIPIIITPPPITJPIIIIIIIIIPTPIIPTJIIPJIIIIIIIIIPIILTTIIPOIIIPIIIIIIIIPPIIIIIIPOPIIJIOTIIIIIIIIIITPIITIILITOTITPOII
62914 IIIIIOPIPIIIIOITIIIIIIIIIIIIPIPTPIPIIIIIIIIIIITIIIIIIIIPTPLTIIPPIIIIIIIIIJIPPTPTPTIIIIIIIIIIIIIIIIIITPIPTPPIPLPPIIIIIIIITPIPTPTT
73400 PPIIIIIIIITIIIIIIIIITPIITTIPIIIIIIIIITIIIITJIIIIIIIIIIPJIPPTPIIIIIIIIIIPPITTJTPIPTIIIIIIIIPTITITPPPTITPLTPPLPILLPPPLPPTPTPPPTTPL
83886 LOTTIIIIIIIIOTPITPIIPIIIIIIIIOOPTIIIIIIIIPITOOPPPPPPOIIIIIIIIPOTPPPPTITTTPPIPPTJPPILLLLLLLLLLLLLLLLILLLLLLLLLLLLLLLLLLLLLLLLLLLL
94371 LLLLILLLLLLLLIIIIILIIILLILIIIIIIIIIIIIIILLIIIIITLLLITLIIIIILIIIILIIIIILILLIIITLIILLTIIIILLLLLLLLLLLLLLLLLLLIIIIIIIILIIIIPIITIITI
10485 LLLLILTIIILIIIITTLTTTTLLTITTTLTILTLTTTILTTLTTTTTTIITTTILITITTTIITTTTITLTIILILLLLLLLLLLLLLLLLILLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
11534 LLLLLLLLLLLLLLLLLLLLLLLLLLLLLIIILTIILIIIITOPOLOPPPPPPPOPPPPPPIPPJPPPLOPPOPOPPPPJPPPJTPPJOOOTOOOOOOOOIOOOOOOOOOOOOOOOOOOOOOOOOOOO
12582 OOOOOIOOOOOOOOOOOOOOLOOOOOOOOOOOIOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOTOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
13631 OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOTOOOIOOOOOOOOOOOOOOOOOOOTLOLOOOOOOOOOOTOOOOOOOIOOOOOOOOOOOOOOOOOOOOOOOOOOO
14680 OOOOOOOOOOOOOTOOOOIOOOOOOOOOOOOOOOOOOOOOOOOOOOOOLLLLLLLLLLLLLLLLOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
15728 OOOOOOOOOOOOOOTOOOOOOOOOOOOIIIIIIIIOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOTOOOOTOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
16777 OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
17825 OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO

(O)=Leer, (T)able, (I)ndex, (J)ndex Partition, (P)artition, (U)ndo/Rollback, (C)luster, (L)obsegment, (N)ested Table, Lob Index(x)


So bekommt man doch gleich einen Überblick, ob eine Reorg sich auf dem Tablespace lohnt.
Un unserem Fall ja, weil viele (O) am Ende zu finden sind



Weitere Interessente Artikel zum Thema:


Empfohlene Schulungen zum Thema:


PostgreSQL Listener Adresse ändern und Zugriff regulieren

Bereich:Postgres, Letzte Überarbeitung: 04.03.2021

Keywords:PostgreSQL, Linux, Windows, Postgres, Postgre

Wenn mann seine ersten Schritte mit PostgreSQL durchführt, ist meist das erste Problem, dass man sich nicht Remote mit der DB verbinden kann. Die Datenbank lauscht standartmäßig auf Clientanfragen erst einmal nur lokal.
Um das zu ändern öffnet man die Datei postgresql.conf.
Dort sucht man nach der Zeile listen_addresses und setzt sie auf *
 

listen_addresses = '*'                 # what IP address(es) to listen on;
                                        # comma-separated list of addresses;
                                        # defaults to 'localhost', '*' = all


Jetzt startet man die Datenbank über z. B. den Systemd Service neu und schon 
horcht sie auch auf Verbindungen die nicht vom lokalen System kommen.
Das nächste Probleme folgt aber direkt jetzt. Man kann sich nicht direkt
mit jedem Benutzer Remote anmelden dazu muss man eine Einstellung in einer
weiteren Config Datei treffen. Dort findet man z. B. folgende Zeile:
 

host    all     all     localhost   trust


Hier mal eine kleine Erklärung für diese Zeile.
Der erste Eintrag steht für die Verbindungsart. Der 2te für die Datenbank
auf die sich verbunden werden soll. Der dritte Eintrag ist für den User
dort kann man einen bestimmten User angeben der z. B. nur auf eine bestimmte
Datenbank zugreifen kann. Die Adresse kommt im vierten Eintrag so kann man
z. B. entweder ein ganzes Subnetz oder auch einfach mittels 0.0.0.0 das 
ganze Internet erlauben. Als letztes kommt noch die Authentizifierungsmethode

Wenn Sie genau wissen wollen welche Werte sie dort alles genau eintragen können
finden Sie hier die Doc Seite von Postgre zur pg_hba.conf 
(https://www.postgresql.org/docs/current/auth-pg-hba-conf.html).

Wenn Sie jetzt z. B. wollen das jeder auf diese Datenbank zugreifen kann
schreiben Sie ganz nach oben folgende Zeile:
 

hostssl    all     all     all     scram-sha-256


Ja mir ist bewusst das md5 geknackt wurde. Deshalb nutzen wir als Verbindungsart
hostssl. Das lässt nur eine Verbindung zu wenn diese mittels SSL verschlüsselt ist.

Noch ein kleiner Hinweis zum Ende.
Möchte man sich mittels psql zu einer Datenbank verbinden muss der Benutzername kleingeschrieben sein.
Das hat mich auch ca. 10 bis 15 Minuten gekostet bis ich da drauf gekommen bin.
 



Weitere Interessente Artikel zum Thema:


Empfohlene Schulungen zum Thema:



    Oracle Datenaustausch mit Postgres via REST

    Bereich:Postgres:REST, Version: ab RDBMS 12.x:RDBMS 18.1:RDBMS 19.1, Letzte Überarbeitung: 25.03.2021

    Keywords:Oracle, Postgres, REST

    Wenn man mit zwei Datenbanken parallel arbeiten möchte, stellt sich immer die Frage, wie geht das am Besten?

    Wir wollen hier mal die REST Schnittstelle als Verbindung zwischen Oracle und Postgres verwenden.
    Wir verwenden hier PostgREST
    https://postgrest.org/en/v7.0.0/

    Installationsverzeichnis aussuchen:

    cd /var/lib/pgsql


     Software herunterladen:

    wget https://github.com/PostgREST/postgrest/releases/download/v7.0.1/postgrest-v7.0.1-linux-x64-static.tar.xz

     
    Auspacken:

    tar xJf postgrest-<version>-<platform>.tar.xz


    REM Rest Service Dienst starten

    ./postgrest


    Schema und Beispieltabelle anlegen:

    CREATE SCHEMA muniq;
    CREATE TABLE muniq.todos (   id serial primary key,   done boolean not null default false,   task text not null,   due timestamptz );
    insert into muniq.todos (task) values   ('finish tutorial 0'), ('pat self on back');


    Rolle anlegen

    CREATE ROLE web_anon nologin;


    Nur Leserechte auf Schema

    grant usage on schema muniq to web_anon;
    grant select on muniq.todos to web_anon;


    Anmelde Benutzer anlegen:

    CREATE ROLE authenticator NOINHERIT LOGIN PASSWORD 'muso2021muso2021';
    GRANT web_anon TO authenticator;


    vi tutorial.conf

    db-uri = "postgres://authenticator:muso2021muso2021@localhost:5432/xe18c"
    db-schema = "muniq"
    db-anon-role = "web_anon"


    Passwort eintragen:

    echo jwt-secret ="\"`date | md5sum | head -c32`\"" >> tutorial.conf
    Rest Dienst mit Konfig-Datei starten
    ./postgrest tutorial.conf


    Mit Ihrem Passwort gehen Sie bitte in die Webseite und führen folgende Schritte aus

    cat tutorial.conf | grep jwt-secret
    https://jwt.io/#debugger-io
    1. Header:
    {   "alg": "HS256",   "typ": "JWT" }
    2. Payload:
    {"role": "todo_user"}
    3. PWD aus jwt-secret
    4. Secret nicht anwählen
    5. Token rauskopieren


    Verwenden Sie ein zweite Session:
    Lesetest:

    curl http://localhost:3000/todos

    Schreibtest:

    export TOKEN="eyJhbGciOaJIUzI1NiIsInR5cCI6IkpXxCJ8.eyJyb2xlIjoidd9kb191c2Vycn0.Vl2f3BH_4iLc-PzY1SE74svS9mDvgdSkEFH7_2ReJsA"
    curl http://localhost:3000/todos -X POST \      -H "Authorization: Bearer $TOKEN"   \      -H "Content-Type: application/json" \      -d '{"id":4,"task": "mp"}'


    Beispiele für Lesefilter:

    curl http://localhost:3000/todos -X GET \      -H "Authorization: Bearer $TOKEN"   \      -H "Content-Type: application/json"

    Eine Zeile anzeigen

    id=1 (equal 1)
    curl http://localhost:3000/todos?id=eq.1
    id <3  (lower Then <)
    curl http://localhost:3000/todos?id=lt.3
    id > 3 (greater then >)
    curl http://localhost:3000/todos?id=gt.3
    id >3 or id <2
    curl "http://localhost:3000/todos?or=(id.gt.3,id.lt.2)"


    Weitere Beispiele: https://postgrest.org/en/v7.0.0/api.html

    Insert neue Zeile

    curl http://localhost:3000/todos -X POST \     
    -H "Authorization: Bearer $TOKEN"   \     
    -H "Content-Type: application/json" \     
    -d '{"id":4,"task": "mp"}'


    Update (auf alle Zeilen)

    curl http://localhost:3000/todos -X PATCH \     
    -H "Authorization: Bearer $TOKEN"    \     
    -H "Content-Type: application/json"  \      -
    d '{"done": true}'


    Update (auf eine Zeile (alle Spalten müssen angegeben werden!))

    curl http://localhost:3000/todos?id=eq.3 -X PUT \     
    -H "Authorization: Bearer $TOKEN"    \     
    -H "Content-Type: application/json"  \     
    -d '{"id":3, "task":"Test","done": true}'


    Delete (eine Zeile)

    curl http://localhost:3000/todos?id=eq.4 -X DELETE \     
    -H "Authorization: Bearer $TOKEN"    \     
    -H "Content-Type: application/json"


    Sehen wir uns mal die Seite von Oracle aus an. Wie kann man die Postgres Schnittstelle ansprechen?
    Dafür bietet sich das Package apex_web_service an, das installiert ist, wenn Sie auch APEX/ORDS installiert haben.
    Wir schreiben uns ein kleines Package, das die Daten im JSON Format an die Postgres REST Schnittstelle übergibt:
     

    CREATE OR REPLACE PACKAGE postgres_rest IS
    FUNCTION get (
    table_name IN VARCHAR2,
    filter IN VARCHAR2 DEFAULT NULL)
    RETURN CLOB;

    PROCEDURE write (
    table_name  IN VARCHAR2,
    modus       IN VARCHAR2,
    filter      IN VARCHAR2 DEFAULT NULL,
    body        IN CLOB  DEFAULT NULL);
    END;
    /


    und der dazugehörige Body. Bitte beachten Sie, dass der Bearer Schlüssel in Zeile 11 angepasst werden muss. Diese haben Sie im Schritt auf der Seite https://jwt.io/#debugger-io
    (siehe oben) bekommen.
    Wenn der Schlüssel nicht passt, bekommen Sie eine Fehlermeldung, dass Sie keine Rechte auf der Tabelle haben.
    Passen Sie auch bitte die IP Adresse vom Ziel Server an (bei uns 172.30.30.8)
     


    CREATE OR REPLACE PACKAGE BODY postgres_rest IS
    vRestResult  CLOB;
    v_request VARCHAR2(32000);

    PROCEDURE INIT
    IS BEGIN
    apex_web_service.g_request_headers.delete();
    apex_web_service.g_request_headers(1).name  := 'Content-Type';
    apex_web_service.g_request_headers(1).value := 'application/json';
    apex_web_service.g_request_headers(2).name  := 'Authorization';  
    apex_web_service.g_request_headers(2).value := 'Bearer eyJhbGciOiJIUzI1NiIsInR5cdI7IkpXVCJ9.eyJyb2xlIjoidG9kb101c2VyIn0.CUZ02WPyLV-QdVtokEZmdqDkWrOqSStlWbADj1Sz_uU';
    END INIT;

    -- ################################################################################################

    FUNCTION get (table_name IN VARCHAR2, filter IN VARCHAR2 DEFAULT NULL ) RETURN CLOB
    IS  

    BEGIN  
    init;
    IF filter IS NOT NULL THEN
        v_request:=table_name||'?'||filter;
    ELSE
         v_request:=table_name;
    END IF;

    vRestResult := apex_web_service.make_rest_request(    
    p_url => 'http://172.30.30.8:3000/'||v_request,    
    p_http_method => 'GET');
    RETURN vRestResult;
    END;

    -- ################################################################################################

    PROCEDURE write (
    table_name  IN VARCHAR2,
    modus       IN VARCHAR2,
    filter      IN VARCHAR2 DEFAULT NULL,
    body        IN CLOB DEFAULT NULL
    )
    IS
    v_mode VARCHAR2(2001);
    BEGIN
    init;
    IF    substr(upper(modus),1,1)='I' THEN
    v_mode:='POST';
    ELSIF substr(upper(modus),1,1)='U' THEN
    v_mode:='PUT';
    ELSIF substr(upper(modus),1,1)='D' THEN
    v_mode:='DELETE';
    ELSE
        RAISE_APPLICATION_ERROR(-20000,'Invalid Mode (I)nsert, (U)pdate, (D)elete');
    END IF;
    IF v_mode IN ('PUT','DELETE') THEN
        v_request:=table_name||'?'||filter;
    ELSE
        v_request:=table_name;
    END IF;

    vRestResult := apex_web_service.make_rest_request(    
    p_url => 'http://172.30.30.8:3000/'||v_request,
    p_body =>body,
    p_http_method => v_mode);
    --dbms_output.put_line('Request:'||v_request);
    --dbms_output.put_line(substr(vRestResult,1,4000));
    END write;
    END;
    /

        
    Als Bonus-Track schreiben wir uns einen Trigger, der das obige Oracle  Package nutzt und alle Änderungen auf Oracle Seite in der Tabelle in eine gleiche Tabelle auf Postgres synchon spiegelt.
    Hinweis die Tabelle (mit Primary Key) muss in Postgres jedoch angelegt werden:

    Hinweis: Auf Postgres Seite ausführen !
    CREATE TABLE muniq.emp (
        empno numeric CONSTRAINT PK_EMP PRIMARY KEY,
        ename character varying(10),
        job character varying(9),
        mgr numeric,
        hiredate date,
        sal numeric(7,2),
        comm numeric(7,2),
        deptno numeric(2,0));

     

    Hinweis: Auf Oracle Seite ausführen
    CREATE OR REPLACE TRIGGER EMP_TRG
    AFTER DELETE OR INSERT OR UPDATE ON EMP
    FOR EACH ROW
    DECLARE
    v_tab_name   VARCHAR2(2001):='emp';
    v_tab_filter VARCHAR2(2001):='empno=eq.'||nvl(:new.empno,:old.empno);
    v_body CLOB;
    BEGIN

      v_body:='{
         "empno": "'  ||:new.empno ||'"' ||
         CASE WHEN :new.mgr     IS NOT NULL THEN ',"ename":"'   ||:new.ename    ||'"' END ||
         CASE WHEN :new.job     IS NOT NULL THEN ',"job":"'     ||:new.job      ||'"' END ||
         CASE WHEN :new.mgr     IS NOT NULL THEN ',"mgr":"'     ||:new.mgr      ||'"' END ||
         CASE WHEN :new.hiredate IS NOT NULL THEN ',"mgr":"'    ||:new.hiredate ||'"' END ||
         CASE WHEN :new.sal     IS NOT NULL THEN ',"sal":"'     ||:new.sal      ||'"' END ||
         CASE WHEN :new.comm    IS NOT NULL THEN ',"comm":"'    ||:new.comm     ||'"' END ||
         CASE WHEN :new.deptno  IS NOT NULL THEN ',"deptno":"'  ||:new.deptno   ||'"' END ||
         '}';

      --dbms_output.put_line('Debug:'||v_body);   
      IF INSERTING THEN  
       postgres_rest.write(
       table_name   =>v_tab_name,
       modus        =>'I',
       body         =>v_body);
      ELSIF UPDATING THEN
        postgres_rest.write(
        table_name  =>v_tab_name,
        modus       =>'U',
        body        =>v_body,
        filter=>v_tab_filter);
      ELSIF DELETING THEN
        postgres_rest.write(
        table_name  =>v_tab_name,
        modus       =>'D',
        filter      =>v_tab_filter);
      END IF;
    END;
    /


    Beispiele zum Packageaufruf:

    select postgres_rest.get(table_name=>'todos') from dual;
    EXEC postgres_rest.write(table_name=>'todos',modus=>'I',body=>'{"id":6, "task": "1234"}');
    EXEC postgres_rest.write(table_name=>'todos',modus=>'U',filter=>'id=eq.6',body=>'{"id":6, "task": "12345"}');
    EXEC postgres_rest.write(table_name=>'todos',modus=>'D',filter=>'id=eq.6');


    Beispiele zum Trigger-Testen:

    insert into emp (empno,ename) values (8008,'Marco');
    update emp set
    ename='Marco2' where empno=8008;
    delete from emp where empno=8008;   



    Nun kann man Oracle und Postgres wunderbar miteinander verbinden. Weitere Tipps & Tricks erfahren Sie u.a. in unserm Oracle ORDS Kurs.

     



    Weitere Interessente Artikel zum Thema:


    Empfohlene Schulungen zum Thema:


    Oracle Workspace Manager (Package dbms_wm)

    Bereich:DBA:PL/SQL, Version: ab RDBMS 10.x, Letzte Überarbeitung: 15.05.2019

    Keywords:Workspace Manager, dbms_wm, Tabellenversionierung

    Der Workspace Manager dient zur Versionierung von Tabellendaten in verschieden Versionen (Workspaces)
    Vorteile

    • Langlaufende Transaktionen können in einem eigenen Workspace laufen, ohne dass sie andere Sessions behindern
    • Unterschiedliche Versionsstände einer Tabelle können unendlich lange gespeichert werden
    • Was wäre, wenn Analysen in beliebiger Anzahl durchgeführt werden, ohne dass die Produktionsdaten dadurch verändert werden?
    • Verfügbar in Standard und Enterprise Edition

    Vorbereitungen:

    • Tabellen müssen für den Workspace vorbereitet werden (DBMS_WM.EnableVersioning)
    • Dadurch wird die Tabelle umbenannt in <tabellenname>_LT
    • Dann wird eine View erzeugt mit dem Ursprungsnamen der Tabelle
    • Auf die View wird ein Instead of Trigger gelegt, der die Daten dann wieder in die Tabelle einträgt
    • Der Tablespace für die Tabelle wird nicht gewechselt

    Sie können eine Administrationsrolle mit allen Rechten für die Workspacebearbeitung vergeben:

    GRANT WM_ADMIN_ROLE TO <user>;


    Folgende Einzelrechte können vergeben werden:

    • ACCESS_WORKSPACE
    • CREATE_WORKSPACE
    • MERGE_WORKSPACE
    • REMOVE_WORKSPACE
    • ROLLBACK_WORKSPACE
    • FREEZE_WORKSPACE

    Beispiel:

    BEGIN
    DBMS_WM.GrantWorkspacePriv('ACCESS_ANY_WORKSPACE, MERGE_ANY_WORKSPACE', 'my_workspace_1','SCOTT','NO');
    END;
    /


    Folgendes gilt für die Tabellen:

    • Nur der Eigentümer oder der Inhaber des Rechts WM_ADMIN_ROLE kann die Versionierung aktivieren
    • Die Tabelle muss einen Primärschlüssel besitzen
    • SYS Tabellen können nicht versioniert werden
    • Wenn eine Parent Tabelle versioniert wurde, muss die Child Tabelle es auch sein
    • Aber eine Child Tabelle kann auch ohne Parent Tabelle versioniert werden
    • Foreign Key Constraints dürfen nach Aktivieren der Versionierung nicht mehr nachträglich erzeugt werden

    Beispiel:
    Tabelle für den Workspace Manager aktivieren:

    BEGIN
    DBMS_WM.EnableVersioning(Table_Name => 'emp');
    END;
    /


    Zwei Workspaces einrichten:

    EXECUTE DBMS_WM.CreateWorkspace('my_workspace_1');


    In den ersten Workspace wechseln:

    EXECUTE DBMS_WM.GotoWorkspace('my_workspace_1');


    Durchführen von Änderungen in der Tabelle emp im Workspace my_workspace_1:

    INSERT INTO emp (empno,ename,deptno) VALUES (8000,'Marco',40);
    DELETE FROM emp WHERE deptno=10;
    UPDATE emp SET sal=sal+1 WHERE deptno=20;


    In den Haupt-Workspace wechseln:

    EXECUTE DBMS_WM.GotoWorkspace('LIVE');

    Dort ist die Tabelle in Ihrem Ursprungszustand zu sehen (ohne die 3 DML Änderungen)

    In welchem Workspace sind wir gerade?

    SELECT DBMS_WM.GetWorkspace FROM dual;


    Die Live Tabelle kann nun auf die Workspace Tabellen-Variante refreshed werden:

    BEGIN
    DBMS_WM.RefreshWorkspace(
    workspace =>'my_workspace_1');
    END;
    /

    Oder die Workspace Variante wird auf Live synchronisiert:

    BEGIN
    DBMS_WM.MergeWorkspace(
    workspace =>'my_workspace_1');
    END;
    /


    Die Tabelle kann wieder aus der Versionsverwaltung herausgenommen werden durch:

    EXEC DBMS_WM.DisableVersioning('SCOTT.EMP');

    Mit der Option FORCE wird das Kommando auch mit geänderten Workspacedaten durchgeführt, sonst erhält man einen Oracle Fehler:

    ORA-20038: cannot disable version a table modified in non-LIVE workspaces

    BEGIN
    DBMS_WM.DisableVersioning('SCOTT.EMP',force=>TRUE);
    END;
    /


    Zum Löschen eines Workspace verwenden Sie:

    BEGIN
    DBMS_WM.RemoveWorkspace('my_workspace_1');
    END;
    /


    Weitere Informationen zum Workspace Manager erhalten Sie in unserem PL/SQL II Kurs.
     



    Weitere Interessente Artikel zum Thema:



    Empfohlene Schulungen zum Thema:


    Oracle 23ai FREE auf Debian (bookworm) Installation

    Bereich:DBA, Version: ab RDBMS 23.1, Letzte Überarbeitung: 13.06.2024

    Keywords:

    Installation von Oracle 23ai FREE auf Debian

    Die folgende Installation wird zwar nicht von Oracle supported, aber das wird die 23ai FREE Edition sowieso nicht, also los geht´s …

    Benutzer anlegen (kann man auch durch das Preinstall Skript durchführen lassen), aber hier ist man flexibler…

    addgroup --system oinstall
    addgroup --system dba
    adduser --system --ingroup oinstall --shell /bin/bash oracle
    usermod -d /home/oracle oracle
    chown -R oracle:oinstall /home/oracle
    adduser -d /home/oracle oracle dba

    Ein paar Packages vorinstallieren:

    apt-get install alien rlwrap

    REM Download der beiden Dateien von der URL: https://www.oracle.com/database/free/download/

    Red Hat 8 / Rocky Linux 8 / Alma Linux 8 / Oracle Linux 8
    wget https://yum.oracle.com/repo/OracleLinux/OL8/appstream/x86_64/getPackage/oracle-database-preinstall-23ai-1.0-2.el8.x86_64.rpm
    wget https://download.oracle.com/otn-pub/otn_software/db-free/oracle-database-free-23ai-1.0-1.el8.x86_64.rpm
    
    Red Hat 9 / Rocky Linux 9 / Alma Linux 9 / Oracle Linux 9
    https://yum.oracle.com/repo/OracleLinux/OL8/appstream/x86_64/getPackage/oracle-database-preinstall-23ai-1.0-2.el9.x86_64.rpm
    wget https://download.oracle.com/otn-pub/otn_software/db-free/oracle-database-free-23ai-1.0-1.el9.x86_64.rpm

    REM Unwandeln der .rpm Dateien in .deb Dateien:

    alien --script oracle-database-preinstall*.rpm
    alien --script oracle-database-free-23ai*.rpm

    ### Achtung dauert laaaaange #### nicht abbrechen #### bei uns fast 1 Stunde ! 
    zur Überbrückung bekommt man dauernd die Warnung:  
    warning: oracle-database-free-23ai-1.0-1.el8.x86_64.rpm: Header V3 RSA/SHA256 Signature, key ID ad986da3: NOKEY  
    oder wenn Sie Langeweile haben, können Sie die Größe des Zielordners "im Auge"  behalten:

    du -hs oracle-database-free-23ai-1.0

    da sollten dann ca. 11 GB rauspurzeln

    Sie können während Sie auf die Beendigung der Umwandlung warten schon mal (in einer zweiten Session) die .bash_profile Datei bearbeiten/anpassen:

    cat << EOF > /home/oracle/.bash_profile
        export TERM=vt220
        export EDITOR=vi
        export PS1="[\u@\h:\w]>"
        export DISPLAY=:0.0
        export ORACLE_BASE=/opt/oracle
        export ORACLE_HOME=\$ORACLE_BASE/product/23ai/dbhomeFree
        export ORACLE_SID=FREE
        export ORACLE_INSTANCE=free
        export ORACLE_TERM=vt220
        export ORA_NLS10=\$ORACLE_HOME/nls/data
        export LD_LIBRARY_PATH=\$LD_LIBRARY_PATH:\$ORACLE_HOME/lib
        export NLS_LANG=GERMAN_GERMANY.WE8MSWIN1252
        export PATH=\$ORACLE_HOME/bin:\$PATH:/usr/sbin:/usr/ccs/bin
        alias cdo="cd \$ORACLE_HOME; pwd"
        alias cdd="cd \$ORACLE_BASE/oradata"
        alias cda="cd \$ORACLE_BASE/diag/rdbms/\$ORACLE_INSTANCE/\$ORACLE_SID/trace; pwd"
        alias cdn="cd \$ORACLE_HOME/network/admin; pwd"
        alias cde="cd \$ORACLE_BASE/admin/\$ORACLE_SID/dpdump"
        alias sp="\$ORACLE_HOME/bin/sqlplus '/ as sysdba'"
        alias l="ls -l"
        alias ll="ls -la"
        alias ipconfig="/sbin/ifconfig | grep Bcast"
        echo Folgende Einstellungen wurden gesetzt:
        env | grep ORA
        ps aux | grep [t]omcat | awk '{print "Tomcat-Prozess:" $2}'
        ps aux | grep [x]e_pmon | awk '{print "Oracle-Prozess:" $2}'
        ps aux | grep [t]ns | awk '{print "Listener-Prozess:" $2}'
        EOF

    Wenn der Umwandlungsprozess fertig ist, geht es weiter mit:

    apt update 
    apt install libaio* 
    apt install ./oracle-database-preinstall-23ai_1.0-1.5_amd64.deb 
    apt install ./oracle-database-free-23ai_1.0-2_amd64.deb 

    dann geht es weiter mit: 

    Hinweis: Bei uns wurde bei der automatischen Installation der Listener Port 1539 verwendet und dann abgebrochen.

    Sie können den Port in folgender Datei selber setzen:

    vi /etc/sysconfig/oracle-free-23ai.conf
    LISTENER_PORT=1521
    /etc/init.d/oracle-free-23ai configure 

    zweimal ein schönes langes Passwort eingeben, z.B. "dasistmeinPasswordganzschoenlangoder"

    /etc/init.d/oracle-free-23ai configure
    Oracle Net Listener configured.
    Specify a password to be used for database accounts. Oracle recommends that the password entered should be at least 8 characters in length, contain at least 1 uppercase character, 1 lower case character and 1 digit [0-9]. Note that the same password will be used for SYS, SYSTEM and PDBADMIN accounts:
    Confirm the password:
    Configuring Oracle Listener.
    Listener configuration succeeded.
    Configuring Oracle Database FREE.
    Enter SYS user password: 
    ***
    Enter SYSTEM user password: 
    ****
    Enter PDBADMIN User Password: 
    *****
    Prepare for db operation
    7% complete
    Copying database files
    29% complete
    Creating and starting Oracle instance
    30% complete
    33% complete
    36% complete
    39% complete
    43% complete
    Completing Database Creation
    47% complete
    49% complete
    50% complete
    Creating Pluggable Databases
    54% complete
    71% complete
    Executing Post Configuration Actions
    93% complete
    Running Custom Scripts
    100% complete
    Database creation complete. For details check the logfiles at:
    /opt/oracle/cfgtoollogs/dbca/FREE.
    Database Information:
    Global Database Name:FREE
    System Identifier(SID):FREE
    Look at the log file "/opt/oracle/cfgtoollogs/dbca/FREE/FREE.log" for further details.
    Connect to Oracle Database using one of the connect strings:
        Pluggable database: pve4/FREEPDB1
        Multitenant container database: pve4
    

    Beim ersten Versuch gab es leider kein schöner Abschluss, wir bekommen den Fehler:

    Listener configuration failed. Check log '/opt/oracle/cfgtoollogs/netca/netca_configure_out.log' for more details

    und da steht drinnen: 

    The information provided for this listener is currently in use by other software on this computer.

    In der listener.ora Datei ($ORACLE_HOME/network/admin) steht ein Port 1539 hmm, kenn ich nicht, Oracle verwendet normalerweise den Port 1521. 
    Oder Sie haben wir oben beschrieben den Port bereits geändert, dann sollte alles durchlaufen.

    So jetzt können Sie mit Ihren SQL / DBA /APEX Kenntnissen loslegen… oder doch erst einen Kurs bei uns besuchen ?
    Kein Problem, bei 44 verschiedenen Kursen ist sicher auch für Sie etwas dabei.

     



    Weitere Interessente Artikel zum Thema:


    Empfohlene Schulungen zum Thema:


    Linux Shell Skripten in Oracle Tabellen speichern

    Bereich:DBA:PL/SQL, Version: ab RDBMS 21.1:RDBMS 23.1, Letzte Überarbeitung: 23.12.2024

    Keywords:Shell Script, Linux,

    Wollten Sie immer schon mal zu Dokumentationszwecken ihre Shell Skripten in Oracle Tabellen speichern, das Ganze auch noch Clientseitig?

    Das Problem sind die vielen Zeilenumbrüche in den Shell Skript Dateien, deswegen wandeln wir die Datei in BASE64 um. 
    Da die Dateien länger als 32767 Zeichen lang werden können und damit die Maximallänge von VARCHAR2 in Tabellen von 4k (bzw. 32K ab 12.1 bei spezieller Einrichtung) übersteigen können,
    wandeln wir es sicherheitshalber in CLOB um.
    Leider hat Oracle eine solche Funktion nicht, dewegen schreiben wir sie uns selbst.

    1. Funktion anlegen:

    CREATE OR REPLACE FUNCTION Decode_Base64(
           p_base64 IN CLOB
        ) RETURN CLOB AS
           v_blob       BLOB;
           v_decoded    CLOB;
           v_length     INT;
           v_warning    INT;
           v_offset     INT:= 1;
           v_context    INT:= dbms_lob.default_lang_ctx;
        BEGIN
           -- Convert the Base64 CLOB into a BLOB
           DBMS_LOB.CREATETEMPORARY(v_blob, TRUE);
           DBMS_LOB.WRITE(v_blob, DBMS_LOB.GETLENGTH(p_base64), 1, UTL_RAW.CAST_TO_RAW(p_base64));
           -- Decode the Base64 BLOB
           v_blob := UTL_ENCODE.BASE64_DECODE(v_blob);
           -- Determine the length of the decoded text
           v_length := DBMS_LOB.GETLENGTH(v_blob);
           -- Convert the BLOB to CLOB
           DBMS_LOB.CREATETEMPORARY(v_decoded, TRUE);
           DBMS_LOB.CONVERTTOCLOB(
               DEST_LOB     => v_decoded,
               SRC_BLOB     => v_blob,
               AMOUNT       => v_length,
               DEST_OFFSET  => v_offset,
               SRC_OFFSET   => v_offset,
               blob_csid    => dbms_lob.default_csid,
               lang_context => v_context,
               warning      => v_warning
           );
            
                -- Free resources
           DBMS_LOB.FREETEMPORARY(v_blob);
           RETURN v_decoded;
        END Decode_Base64;
        /

    2. Ziel-Tabelle erstellen:

    CREATE TABLE script_table (
        id int 
        GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, 
        content CLOB);

    3. Auf Shell-Ebene gewünschte Datei (hier: install.sh) in Base64 codieren und in Variable B64 speichern:

    export B64=`echo -n \`cat install.sh\` | base64`

    4. OS-Variable in Oracle Tabelle eintragen und wieder aus Base64 in CLOB Text konvertieren:

        sqlplus scott/TIGER@172.24.114.237:1522/pdb1  <<EOF
        set echo off termout off 
        DECLARE
         script_content CLOB := q'^${B64}^';
        BEGIN
         INSERT INTO script_table(content)VALUES(decode_base64(script_content));
         COMMIT;
        END;
        /
        EXIT;
        EOF

    So nun steht Ihrer Versionsverwaltung oder dem Audit nichts mehr im Wege…
    Weitere tolle Tipps erhalten Sie in einem unserer 50 Oracle und Postgres Kurse. Wir freuen uns auf Sie …
     



    Weitere Interessente Artikel zum Thema:


    Empfohlene Schulungen zum Thema:


    Tablespace Shrink ab 23ai

    Bereich:DBA, Version: ab RDBMS 23.1, Letzte Überarbeitung: 11.02.2025

    Keywords:

    Seit 30 Jahren warte ich auf dieses Feature, aber wie sagt man besser spät als nie …

    Ab 23ai kann nun endlich ein Tablespace reorganisiert werden. Es gibt da nur ein paar Einschränkungen:

    1. Es muss ein Bigfile Tablespace sein
    2. Es dürfen keine exotischen und schreibgeschützten Objekte dort liegen wie z.B.
      aud$unified

    Zuerst prüfen wir mal, welche Tablespaces sich den lohnen bzgl eines Reorg?

    set serveroutput on linesize 400
    set long 9999999
    WITH function analyze_tbs(tbs_name In VARCHAR2) RETURN CLOB
    IS
    PRAGMA AUTONOMOUS_TRANSACTION;
    v_result CLOB:=empty_clob();
    BEGIN
    dbms_output.put_line('###  '||tbs_name||'  ###:');
    DBMS_SPACE.TABLESPACE_SHRINK(tbs_name, 
    SHRINK_MODE => DBMS_SPACE.TS_MODE_ANALYZE, 
    SHRINK_RESULT => v_result);
    RETURN tbs_name||':'||chr(10)||v_result;
    END;
    select analyze_tbs(tablespace_name)
    from dba_tablespaces
    where bigfile='YES'
    and contents='PERMANENT';

     

    Nun können wir einen Tablespace von Oracle shrinken/reorganisieren …

    BEGIN
    DBMS_SPACE.TABLESPACE_SHRINK('SYSAUX', 
    SHRINK_MODE => dbms_space.ts_mode_shrink, 
    target_size => dbms_space.ts_target_max_shrink);
    END;
    /

    Viel Spaß mit dem neuen gewonnen Speicherplatz. Die Einsparung kann man ja nun in eine Schulung stecken :-)



    Weitere Interessente Artikel zum Thema:


    Empfohlene Schulungen zum Thema:


    Oracle ORDS 24.x Install mit Apache TomCat 10.1.x oder TomCat 11.x

    Bereich:APEX, Version: ab APEX 23.2:APEX 24.1:APEX 24.2:ORDS 23.2, Letzte Überarbeitung: 29.07.2025

    Keywords:

    TomCat Version 10 ist nun einige Jahre auf dem Markt, nur leider unterstützt Oracle diese Version weiterhin nicht. Inwischen gibt es sogar auch schon TomCat 11!
    Wer doch schon mal auf undokumentierten Pfaden wandeln möchte, bekommt hier die Anleitung dazu.

    1. Wenn Sie noch kein Java JDK installiert haben, sollten Sie das jetzt bitte tun (Download Seite Java JDK 17)
    2. Nun brauchen wir die neuste Apache TomCat Version 10 (Download) oder Apache TomCat 11 (Download)
      Link zur Apache Webseite (Apache 10)
      Der TomCat wird ganz normal installiert, sie sollten aber evtl eine alte TomCat Version entfernen oder für beide TomCat Versionen verschiedene Ports (z.B. 8080 und 8081) einrichten
    3. Dann brauchen wir ein Apache Konvertierungsutility, um die ORDS Version in das für Version 10 buw. Version 11 passende Format zu wandeln (Download) Link zur Webseite (Apache Konvertierungstool)
    4. Laden Sie sich die neuste ORDS Version herunter (Webseite)

    Packen Sie nun die jakartaee-migration Dateien aus Schritt 3 z.B. in das Temp Verzeichnis aus (c:\temp für Windows oder /tmp für Linux)

    Packen Sie auch die ORDS Version aus Schritt 4 im Temp Verzeichnis aus

    Kopieren Sie die Datei ords.war in das Verzeichnis C:\Temp\jakartaee-migration-*\lib (Windows) oder /tmp/jakartaee-migration-*/lib (Linux)

    Windows: "C:\Program Files\Java\jdk-17\bin\java.exe" -jar jakartaee-migration-1.0.8.jar ords.war ords_neu.war
    Linux: java -jar jakartaee-migration-1.0.8.jar ords.war ords_neu.war

    Nun wird die ords_neu.war in ords.war umbennant und in das webapps Verzeichnis des TomCat kopiert

    Windows: move C:\Temp\jakartaee-migration-1.0.8\lib\ords_neu.war “C:\Program Files\Apache Software Foundation\Tomcat 10.1\webapps\ords.war”
    bzw. Version 11
    Windows: move C:\Temp\jakartaee-migration-1.0.8\lib\ords_neu.war “C:\Program Files\Apache Software Foundation\Tomcat 11.0\webapps\ords.war”
    Linux: mv /tmp/jakartaee-migration-1.0.8/lib/ords_neu.war /opt/tomcat/latest/webapss

    Ords Einrichtung (Windows)

    set JAVA_HOME="C:\Program Files\Java\jdk-17"
    set PATH=%PATH%;%JAVA_HOME%
    set ORDS_HOME=c:\oracle\ords
    set ORDS_CONFIG=c:\oracle\ords
    set ORDS_LOGS=%ORDS_CONFIG%\logs
    mkdir %ORDS_CONFIG%
    mkdir %ORDS_LOGS%
    dir %JAVA_HOME%
    set DB_PORT=1521
    set DB_SERVICE=apex241
    set SYSDBA_USER=SYS
    set SYSDBA_PASSWORD=sys
    set ORDS_PASSWORD=ords
    set ORA_HOST=172.30.30.2
    #%ORDS_HOME%\bin\ords --config %ORDS_CONFIG% uninstall
    %ORDS_HOME%\bin\ords --config %ORDS_CONFIG% install ^
        --log-folder %ORDS_LOGS% ^
        --admin-user %SYSDBA_USER% ^
        --db-hostname %ORA_HOST% ^
        --db-port %DB_PORT% ^
        --db-servicename %DB_SERVICE% ^
        --feature-db-api true ^
        --feature-rest-enabled-sql true ^
        --feature-sdw true ^
        --gateway-mode proxied ^
        --gateway-user APEX_PUBLIC_USER ^
        --proxy-user 

    ORDS Einrichtung (Linux)

    export ORDS_HOME=/opt/oracle/ords
    mkdir -p $ORDS_HOME
    chown -R tomcat:dba $ORDS_HOME
    export ORA_HOST=172.30.30.2
    export DB_SERVICE=apex241
    export ORDS_CONFIG=/opt/oracle/ords
    cp ords.war $ORDS_HOME
    cp -R bin $ORDS_HOME
    cd $ORDS_HOME
    export SYSDBA_USER=sys
    export DB_PORT=1521
    chmod u+x $ORDS_HOME/bin/ords
    tnsping $ORA_HOST:$DB_PORT/$DB_SERVICE
    lsnrctl status | grep $DB_SERVICE
    # Optional: Deinstallation
    # systemctl stop tomcat
    # $ORDS_HOME/bin/ords --config $ORDS_CONFIG uninstall --admin-user $SYSDBA_USER
    $ORDS_HOME/bin/ords --config $ORDS_CONFIG install \
        --log-folder $ORDS_HOME \
        --admin-user $SYSDBA_USER \
        --db-hostname $ORA_HOST \
        --db-port $DB_PORT \
        --db-servicename $DB_SERVICE \
        --feature-db-api true \
        --feature-rest-enabled-sql true \
        --feature-sdw true \
        --gateway-mode proxied \
        --gateway-user APEX_PUBLIC_USER \
        --proxy-user 

    Auf jeden Fall immer beachten: Der TomCat muss wissen, wo sein Configfile für den ORDS ist.

    Unter Windows trägt man das im Apache Tomcat Config Fenster im Bereich Java ein:

    -Dconfig.url=C:\oracle\ords

    Unter Unix natürlich entsprechend (Datei /etc/systemd/system/tomcat.service):

    Environment="JAVA_OPTS=-Djava.security.egd=file:///dev/urandom -Dconfig.url=/opt/oracle/ords"

    Danach sollte in der der Catalina*.log etwas stehen wie:

    19-May-2025 11:06:32.611 INFORMATION [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dcatalina.home=C:\Program Files\Apache Software Foundation\Tomcat 9.0
    19-May-2025 11:06:32.611 INFORMATION [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dcatalina.base=C:\Program Files\Apache Software Foundation\Tomcat 9.0
    19-May-2025 11:06:32.611 INFORMATION [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.io.tmpdir=C:\Program Files\Apache Software Foundation\Tomcat 9.0\temp
    19-May-2025 11:06:32.611 INFORMATION [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
    19-May-2025 11:06:32.611 INFORMATION [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.util.logging.config.file=C:\Program Files\Apache Software Foundation\Tomcat 9.0\conf\logging.properties
    19-May-2025 11:06:32.611 INFORMATION [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dconfig.url=C:\opt\oracle\ords
    

    So, das war es schon. nur immer daran denken, wenn es ein Problem in dieser nicht unterstützen Konfiguartion gibt, müsste das Ganze erst unter TomCat Version 9 reproduziert werden!

    Viel Spass und bis Bald in einem unserer APEX Kurse …



    Weitere Interessente Artikel zum Thema:


    Empfohlene Schulungen zum Thema:


    Save Linux shell scripts in Oracle tables

    Bereich:DBA:PL/SQL, Version: ab RDBMS 21.1:RDBMS 23.1, Letzte Überarbeitung: 23.12.2024

    Keywords:Shell Script, Linux,

    Have you always wanted to save your shell scripts in Oracle tables for documentation purposes, even on the client side?

    The problem is the many line breaks in the shell script files, so we convert the file to BASE64. 
    Since the files can be longer than 32767 characters and thus exceed the maximum length of VARCHAR2 in tables of 4k (or 32K from 12.1 with special setup),

    1. Create Funktion :

    CREATE OR REPLACE FUNCTION Decode_Base64(
           p_base64 IN CLOB
        ) RETURN CLOB AS
           v_blob       BLOB;
           v_decoded    CLOB;
           v_length     INT;
           v_warning    INT;
           v_offset     INT:= 1;
           v_context    INT:= dbms_lob.default_lang_ctx;
        BEGIN
           -- Convert the Base64 CLOB into a BLOB
           DBMS_LOB.CREATETEMPORARY(v_blob, TRUE);
           DBMS_LOB.WRITE(v_blob, DBMS_LOB.GETLENGTH(p_base64), 1, UTL_RAW.CAST_TO_RAW(p_base64));
           -- Decode the Base64 BLOB
           v_blob := UTL_ENCODE.BASE64_DECODE(v_blob);
           -- Determine the length of the decoded text
           v_length := DBMS_LOB.GETLENGTH(v_blob);
           -- Convert the BLOB to CLOB
           DBMS_LOB.CREATETEMPORARY(v_decoded, TRUE);
           DBMS_LOB.CONVERTTOCLOB(
               DEST_LOB     => v_decoded,
               SRC_BLOB     => v_blob,
               AMOUNT       => v_length,
               DEST_OFFSET  => v_offset,
               SRC_OFFSET   => v_offset,
               blob_csid    => dbms_lob.default_csid,
               lang_context => v_context,
               warning      => v_warning
           );
            
                -- Free resources
           DBMS_LOB.FREETEMPORARY(v_blob);
           RETURN v_decoded;
        END Decode_Base64;
        /

    2. Target-Tabelle:

    CREATE TABLE script_table (
        id int 
        GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, 
        content CLOB);

    3. At shell level, code the desired file (here: install.sh) in Base64 and save it in variable B64:

    export B64=`echo -n \`cat install.sh\` | base64`

    4. Enter OS variable in Oracle table and convert back from Base64 to CLOB text:

        sqlplus scott/TIGER@172.24.114.237:1522/pdb1  <<EOF
        set echo off termout off 
        DECLARE
         script_content CLOB := q'^${B64}^';
        BEGIN
         INSERT INTO script_table(content)VALUES(decode_base64(script_content));
         COMMIT;
        END;
        /
        EXIT;
        EOF

    So now nothing stands in the way of your version management or audit...
    You can get more great tips in one of our 50 Oracle and Postgres courses. We look forward to seeing you ...
     



    Weitere Interessente Artikel zum Thema:


    Empfohlene Schulungen zum Thema:


    Tablespace Shrink available as of Oracle 23ai

    Bereich:DBA, Version: ab RDBMS 23.1, Letzte Überarbeitung: 11.02.2025

    Keywords:

    I've been waiting 30 years for this feature, but as they say – better late than never…

    Starting with Oracle 23ai, it is finally possible to reorganize a tablespace. There are just a few limitations:

    1. It must be a Bigfile Tablespace

    2. It must not contain any exotic or read-only objects such as
      AUD$UNIFIED

    First, let's check which tablespaces are actually worth reorganizing.

    set serveroutput on linesize 400
    set long 9999999
    WITH function analyze_tbs(tbs_name In VARCHAR2) RETURN CLOB
    IS
    PRAGMA AUTONOMOUS_TRANSACTION;
    v_result CLOB:=empty_clob();
    BEGIN
    dbms_output.put_line('###  '||tbs_name||'  ###:');
    DBMS_SPACE.TABLESPACE_SHRINK(tbs_name, 
    SHRINK_MODE => DBMS_SPACE.TS_MODE_ANALYZE, 
    SHRINK_RESULT => v_result);
    RETURN tbs_name||':'||chr(10)||v_result;
    END;
    select analyze_tbs(tablespace_name)
    from dba_tablespaces
    where bigfile='YES'
    and contents='PERMANENT';

     

    Now, we are finally able to shrink or reorganize a tablespace in Oracle...

    BEGIN
    DBMS_SPACE.TABLESPACE_SHRINK('SYSAUX', 
    SHRINK_MODE => dbms_space.ts_mode_shrink, 
    target_size => dbms_space.ts_target_max_shrink);
    END;
    /

    Have fun with the newly reclaimed space. The savings can now be invested in a training course. :-)



    Weitere Interessente Artikel zum Thema:


    Empfohlene Schulungen zum Thema:


    PDF display from table in APEX

    Bereich:APEX, Version: ab APEX 22.2:APEX 23.1:APEX 23.2:APEX 24.1:APEX 24.2:RDBMS 19.1, Letzte Überarbeitung: 05.04.2025

    Keywords:APEX, PDF, Tabelle

    The question of how to display a PDF in a page comes up very often in our APEX courses. In our case, the PDF comes from a table in the Oracle database.

    Preparation:

    1. Create Table
      Example-Table: emp_lob
       
    2. CREATE TABLE emp_lob (
      EMPNO        NUMBER(4,0),
      ENAME        VARCHAR2(10 BYTE),
      DATEINAME    VARCHAR2(512 BYTE),
      BESCHREIBUNG VARCHAR2(4000 BYTE),
      DATEITYP     VARCHAR2(100 BYTE),
      L_UPDATE     DATE,
      BILD         BLOB)
    3. Place the form with report on this table. You can then use this to load the PDF into your table
    4. Create a new application item (Shared Components / Application Logic / Application Item) in APEX (name: EMPNO) and set this item to unrestricted.
    5. Create an application process (Shared Components / Application Logic / Application Process) in APEX: Name getPDF and define as Ajax Processes

      The process has the following code:

      begin
         for file in (select * from emp_lob
                     where empno = :EMPNO
           and dateityp='application/pdf') loop
             sys.htp.init;
             sys.owa_util.mime_header( file.dateityp, FALSE );
             sys.htp.p('Content-length: ' || sys.dbms_lob.getlength( file.bild));
             sys.htp.p('Content-Disposition: inline; filename="' || file.fname || '"' );
             sys.htp.p('Cache-Control: max-age=3600');  -- tell the browser to cache for one hour, adjust as necessary
             sys.owa_util.http_header_close;
             sys.wpg_docload.download_file( file.bild );
          
             apex_application.stop_apex_engine;
         end loop;
      end;
    6.  Create a new page with a static content region:
      Please adjust the IP address and possibly the port on your server
    7. In this region (we are now starting from page 1) there should be an item with the name P1_EMPNO. The corresponding line in which the PDF is located can then be selected there.
      The page should then have a submit button (e.g. via a button)
    8. <object 
      data="http://127.0.0.1:8080/ords/f?p=100:0:&SESSION.:APPLICATION_PROCESS=getPDF:::EMPNO:&P1_EMPNO." style="width:100%;height:700px">
      <a href="http://127.0.0.1:8080/ords/f?p=100:0:&SESSION.:APPLICATION_PROCESS=getPDF:::EMPNO:&P1_EMPNO.">PDF laden</a>
      </object>

    And now have fun displaying all your PDFs. We use it to display our PDF invoices in APEX :-)
    See you (hopefully) soon in one of our courses ...



    Weitere Interessente Artikel zum Thema:


    Empfohlene Schulungen zum Thema:


    Oracle APEX Interactive Grid Javascript Snippets

    Bereich:APEX, Version: ab APEX 22.2:APEX 23.1, Letzte Überarbeitung: 20.09.2023

    Keywords:APEX Javascript, Grid , Interactive Grid

    In our training sessions, we frequently get interesting questions about the Interactive Grid. Here's a small selection:

    Requirements:

    1. There is a grid on the Scott.emp table with the static ID EMP

    2. There is a select list named P1_JOB

     

    1. In the grid for the EMP table, the job for all employees should be set to a single unified value, which is defined in an item outside of the grid.
    var model = apex.region("EMP").widget().interactiveGrid("getViews", "grid").model;
        model.forEach(function(r) {
           model.setValue(r, "JOB", $v("P1_JOB" )); // Setzen des Jobs pro Zeile
        })

    2. Only the entries selected using the checkbox (column 1) in the grid should be updated.

    var grid = apex.region("EMP").widget().interactiveGrid("getViews","grid");
    var model   = grid.model;
    var selectedRecords = grid.getSelectedRecords();
    //console.log("Records" +selectedRecords.length);
    for (idx = 0; idx < selectedRecords.length; idx++) {
       record = model.getRecord(selectedRecords[idx][0]); 
         model.setValue(record, "JOB", $v( "P1_JOB" ));
        //console.log(idx+ " " + selectedRecords[idx][1] ); //Felder [1] EMPNO [2] ENAME, [3] JOB ...
    }

    3. ou want to handle writing back to the database yourself? No problem. Simply replace the process with:

    IF :APEX$ROW_STATUS='C' THEN
        INSERT INTO emp (empno,ename,job,mgr,hiredate,sal,comm,deptno)
        VALUES (:EMPNO,:ENAME,:JOB,:MGR,:HIREDATE,:SAL,:COMM,:DEPTNO);
    ELSIF :APEX$ROW_STATUS='U' THEN
        UPDATE emp SET
        ename=:ENAME,job=:JOB, mgr=:MGR,hiredate=:HIREDATE ,sal=:SAL,comm=:COMM, deptno=:DEPTNO
        WHERE empno=:EMPNO ;
    ELSIF :APEX$ROW_STATUS='D' THEN
        DELETE FROM emp
        WHERE empno=:EMPNO;
    END IF;

    That was a small excerpt from the many questions we get in our APEX courses. Do you have questions too? Then join one of our courses… we look forward to seeing you!

     


    Weitere Interessente Artikel zum Thema:


    Empfohlene Schulungen zum Thema:


    Regular Expression in Oracle (Working Examples)

    Bereich:SQL, Version: ab RDBMS 12.x, Letzte Überarbeitung: 29.02.2024

    Keywords:

    In today's tip, we will look at a few practical examples of regular expressions in Oracle.

    Example 1: Credit card numbers usually have the format: 1234-1234-1234-1234
    We look for two blocks of 4 in a string with numbers and a minus in between and replace each block with: xxxx-xxxx:

    • [[:digit:]] stands for numbers only
    • [4} 4 Numbers
    select regexp_replace(
    '1234-5678-1111-2222',
    '[[:digit:]]{4}-[[:digit:]]{4}','xxxx-xxxx') from dual;

    Result: xxxx-xxxx-xxxx-xxxx

    Or we look for four blocks of 4 with numbers and replace this with: XXXX:

    select regexp_replace(
    '1234-5678-1111-2222',
    '[[:digit:]]{4}','xxxx') 
    from dual;

    Result: xxxx-xxxx-xxxx-xxxx

    Only the last block of 4 is to be issued:

    select regexp_replace(
    '1234-5678-1111-2222',
    '([[:digit:]]{4})(-)([[:digit:]]{4})(-)([[:digit:]]{4})(-)([[:digit:]]{4})','XXXX-XXXX-XXXX-\7') from dual;

    Result:

    XXXX-XXXX-XXXX-2222

    Alternatively, instead of [[:digit:]] with \d replace

    select regexp_replace(
    '1234-5678-1111-2222',
    '\d{4}','x') 
    from dual;

    Result: x-x-x-x

    Beispiel 2: Wir löschen einige HTML Tags in einem String:

    • \/?div finds div, 
    • \/?p finds p and 
    • \/?strong findsstrong Tags 
    • (/ is a special Character and have to be \ escaped)
    select regexp_replace(
    '<div>Hallo</div><strong> Kurs</strong>',
    '(<\/?div>|<\/?p>|<\/?strong>)','') 
    from dual;

    or we delete all HTML tags in the string

    select regexp_replace(
    '<div>Hallo</div><strong> Training</strong>',
    '<\/?[^>]*>,'') 
    from dual;

    This was a small selection of examples, which we are constantly expanding. Or you can come to the PL/SQL or PL/SQL II course, for example, where these topics are also discussed in detail.



    Weitere Interessente Artikel zum Thema:


    Empfohlene Schulungen zum Thema:


    Oracle Attention.log via Pipelined Table Function read / analyze

    Bereich:DBA:PL/SQL, Version: ab RDBMS 21.1:RDBMS 23.1, Letzte Überarbeitung: 15.11.2023

    Keywords:

    Starting with Oracle version 21c, in addition to the alert.log, a new file is delivered: the Attention.log. This file is intended to contain important information as a summary of the alert.log.

    Well, I hope this gets improved a bit, but still, it would be useful to read the file using a SELECT statement. I was hoping someone had already done the work (Google, where are you when you need it...), but unfortunately, I found nothing.

    Okay, then I’ll do it myself…

    We give the objects to the SYSTEM user, but of course, you can also create another user for this purpose.

    ALTER SESSION SET current_schema=system;
    col adir new_value adir
    col afile new_value afile
    with diag as (select sys_context('userenv','PLATFORM_SLASH') as ps,value from v$diag_info
    where name='Attention Log')
    select 
    substr(value,1,instr(value,ps,-1)-1) as adir,
    substr(value,instr(value,ps,-1)+1) as afile
    from diag;
    CREATE OR REPLACE DIRECTORY attention_dir as '&adir.';
    GRANT READ ON DIRECTORY attention_dir to system;

     

    CREATE OR REPLACE TYPE attention_type 
    AS OBJECT (
       NOTIFICATION    VARCHAR2(4000), 
       ERROR           VARCHAR2(4000),
       URGENCY         VARCHAR2(4000),
       INFO            VARCHAR2(4000),
       CAUSE           VARCHAR2(4000),
       ACTION          VARCHAR2(4000),
       CLASS           VARCHAR2(4000),
       TIME            TIMESTAMP WITH TIME ZONE    
    );
    /
    CREATE OR REPLACE TYPE attention_tab_type AS TABLE OF attention_type;
    /
    CREATE OR REPLACE FUNCTION read_attention_log 
    RETURN attention_tab_type PIPELINED
    IS
     f_handle  utl_file.file_type:=utl_file.fopen(
     location=>'ATTENTION_DIR',
     filename=>'&afile.',
     open_mode=>'r',
     max_linesize=>32767);
     text         varchar2(32767);
     v_a_log attention_type:=attention_type(null,null,null,null,null,null,null,null);
     v_i INT:=1;
    BEGIN
     LOOP 
         BEGIN
         utl_file.get_line(f_handle,text); -- Neue Zeile lesen
       IF substr(text,1,16)='  "NOTIFICATION"' THEN
           v_a_log.NOTIFICATION:=rtrim(rtrim(substr(text,21),','),'"');
       END IF;
       IF substr(text,1,9)='  "ERROR"' THEN 
           v_a_log.ERROR:=rtrim(rtrim(substr(text,21),','),'"');
       END IF;
       IF substr(text,1,11)='  "URGENCY"' THEN    
           v_a_log.URGENCY:=rtrim(rtrim(substr(text,21),','),'"');
       END IF;
       IF substr(text,1,8)='  "INFO"' THEN    
           v_a_log.INFO:=rtrim(rtrim(substr(text,21),','),'"');
       END IF;  
       IF substr(text,1,9)='  "CAUSE"' THEN
           v_a_log.CAUSE:=rtrim(rtrim(substr(text,21),','),'"');
       END IF;
       IF substr(text,1,10)='  "ACTION"' THEN
           v_a_log.ACTION:=rtrim(rtrim(substr(text,21),','),'"');
       END IF;
        IF substr(text,1,9)='  "CLASS"' THEN
           v_a_log.CLASS:=rtrim(rtrim(substr(text,21),','),'"');
       END IF;
       IF substr(text,1,8)='  "TIME"' THEN
           v_a_log.TIME:=to_timestamp_tz(rtrim(substr(text,21),'"'),'YYYY-MM-DD"T"HH24:MI:SS.FFTZH:TZM');
           PIPE ROW (v_a_log);
           v_a_log :=attention_type(null,null,null,null,null,null,null,null);
       END IF;
         EXCEPTION 
        WHEN NO_DATA_FOUND THEN EXIT; -- Keine Zeile im Attention.log mehr gefunden oder Fehler=> Schleife verlassen
        WHEN OTHERS THEN RAISE;
         END;
     END LOOP; 
     utl_file.fclose(f_handle);
    END;
    /

    And now you can read the attention.log via SQL...

    SELECT * FROM table(read_attention_log);


    Weitere Interessente Artikel zum Thema:


    Empfohlene Schulungen zum Thema:


    Oracle 23ai FREE on Debian (Bookworm) Installation

    Bereich:DBA, Version: ab RDBMS 23.1, Letzte Überarbeitung: 13.06.2024

    Keywords:

    Installation of Oracle 23ai FREE on Debian

    The following installation is not supported by Oracle, but since the 23ai FREE Edition won’t be supported anyway, let's get started...

    Create a user (can also be done through the preinstall script), but here you have more flexibility...

    addgroup --system oinstall
    addgroup --system dba
    adduser --system --ingroup oinstall --shell /bin/bash oracle
    usermod -d /home/oracle oracle
    chown -R oracle:oinstall /home/oracle
    adduser -d /home/oracle oracle dba

    Preinstall a few packages:

    apt-get install alien rlwrap

    REM Download the two files from the URL: https://www.oracle.com/database/free/download/

    Red Hat 8 / Rocky Linux 8 / Alma Linux 8 / Oracle Linux 8
    wget https://yum.oracle.com/repo/OracleLinux/OL8/appstream/x86_64/getPackage/oracle-database-preinstall-23ai-1.0-2.el8.x86_64.rpm
    wget https://download.oracle.com/otn-pub/otn_software/db-free/oracle-database-free-23ai-1.0-1.el8.x86_64.rpm
    
    Red Hat 9 / Rocky Linux 9 / Alma Linux 9 / Oracle Linux 9
    https://yum.oracle.com/repo/OracleLinux/OL8/appstream/x86_64/getPackage/oracle-database-preinstall-23ai-1.0-2.el9.x86_64.rpm
    wget https://download.oracle.com/otn-pub/otn_software/db-free/oracle-database-free-23ai-1.0-1.el9.x86_64.rpm

    REM Convert the .rpm files to .deb files:

    alien --script oracle-database-preinstall*.rpm
    alien --script oracle-database-free-23ai*.rpm

    Attention, it takes a looooong time #### do not interrupt ####
    It took almost 1 hour for us!
    During this time, you will constantly get the warning:
    warning: oracle-database-free-23ai-1.0-1.el8.x86_64.rpm: Header V3 RSA/SHA256 Signature, key ID ad986da3: NOKEY
    Or, if you're bored, you can keep an eye on the size of the target folder:

    du -hs oracle-database-free-23ai-1.0

    There should be about 11 GB as a result.

    While waiting for the conversion to finish, you can already (in a second session) edit/adjust the .bash_profile file:

    cat << EOF > /home/oracle/.bash_profile
        export TERM=vt220
        export EDITOR=vi
        export PS1="[\u@\h:\w]>"
        export DISPLAY=:0.0
        export ORACLE_BASE=/opt/oracle
        export ORACLE_HOME=\$ORACLE_BASE/product/23ai/dbhomeFree
        export ORACLE_SID=FREE
        export ORACLE_INSTANCE=free
        export ORACLE_TERM=vt220
        export ORA_NLS10=\$ORACLE_HOME/nls/data
        export LD_LIBRARY_PATH=\$LD_LIBRARY_PATH:\$ORACLE_HOME/lib
        export NLS_LANG=GERMAN_GERMANY.WE8MSWIN1252
        export PATH=\$ORACLE_HOME/bin:\$PATH:/usr/sbin:/usr/ccs/bin
        alias cdo="cd \$ORACLE_HOME; pwd"
        alias cdd="cd \$ORACLE_BASE/oradata"
        alias cda="cd \$ORACLE_BASE/diag/rdbms/\$ORACLE_INSTANCE/\$ORACLE_SID/trace; pwd"
        alias cdn="cd \$ORACLE_HOME/network/admin; pwd"
        alias cde="cd \$ORACLE_BASE/admin/\$ORACLE_SID/dpdump"
        alias sp="\$ORACLE_HOME/bin/sqlplus '/ as sysdba'"
        alias l="ls -l"
        alias ll="ls -la"
        alias ipconfig="/sbin/ifconfig | grep Bcast"
        echo Folgende Einstellungen wurden gesetzt:
        env | grep ORA
        ps aux | grep [t]omcat | awk '{print "Tomcat-Prozess:" $2}'
        ps aux | grep [x]e_pmon | awk '{print "Oracle-Prozess:" $2}'
        ps aux | grep [t]ns | awk '{print "Listener-Prozess:" $2}'
        EOF

    When the conversion process is complete, proceed with:

    apt update 
    apt install libaio* 
    apt install ./oracle-database-preinstall-23ai_1.0-1.5_amd64.deb 
    apt install ./oracle-database-free-23ai_1.0-2_amd64.deb 

    Then continue with:

    Note: During the automatic installation, the listener port 1539 was used and then the process was interrupted.

    You can set the port yourself in the following file:

    vi /etc/sysconfig/oracle-free-23ai.conf
    LISTENER_PORT=1521
    /etc/init.d/oracle-free-23ai configure 

    Enter a nice long password twice, for example, "SuperSecretPassword123!WithUnicornAndPizza!"

     
    /etc/init.d/oracle-free-23ai configure
    Oracle Net Listener configured.
    Specify a password to be used for database accounts. Oracle recommends that the password entered should be at least 8 characters in length, contain at least 1 uppercase character, 1 lower case character and 1 digit [0-9]. Note that the same password will be used for SYS, SYSTEM and PDBADMIN accounts:
    Confirm the password:
    Configuring Oracle Listener.
    Listener configuration succeeded.
    Configuring Oracle Database FREE.
    Enter SYS user password: 
    ***
    Enter SYSTEM user password: 
    ****
    Enter PDBADMIN User Password: 
    *****
    Prepare for db operation
    7% complete
    Copying database files
    29% complete
    Creating and starting Oracle instance
    30% complete
    33% complete
    36% complete
    39% complete
    43% complete
    Completing Database Creation
    47% complete
    49% complete
    50% complete
    Creating Pluggable Databases
    54% complete
    71% complete
    Executing Post Configuration Actions
    93% complete
    Running Custom Scripts
    100% complete
    Database creation complete. For details check the logfiles at:
    /opt/oracle/cfgtoollogs/dbca/FREE.
    Database Information:
    Global Database Name:FREE
    System Identifier(SID):FREE
    Look at the log file "/opt/oracle/cfgtoollogs/dbca/FREE/FREE.log" for further details.
    Connect to Oracle Database using one of the connect strings:
        Pluggable database: pve4/FREEPDB1
        Multitenant container database: pve4
    

    On the first attempt, there was unfortunately no successful completion, and we received the error:

    Listener configuration failed. Check log '/opt/oracle/cfgtoollogs/netca/netca_configure_out.log' for more details.

    And it says inside:

    The information provided for this listener is currently in use by other software on this computer.

    In the listener.ora file ($ORACLE_HOME/network/admin), there is a port 1539, hmm, I don't recognize that, Oracle usually uses port 1521.
    Or, as described above, you may have already changed the port, in which case everything should work fine.

    Now, you can start using your SQL / DBA / APEX skills... or maybe first take a course with us?
    No problem, with 44 different courses, there is certainly something for you!

     



    Weitere Interessente Artikel zum Thema:


    Empfohlene Schulungen zum Thema:


    20 CREATE TABLE Examples for Oracle (Version 10.2 bis 21c)

    Bereich:SQL, Version: ab RDBMS 12.x:RDBMS 20.1, Letzte Überarbeitung: 08.04.2025

    Keywords:CREATE Table, Tabellenerstellung, External Table, Collation, Primary Key, Foreign Key, Temporäre Tabelle

    Today's tip is dedicated to the most important concept of a database: the table!

    Since I keep having to search for examples of special CREATE TABLE commands for Oracle databases, it was time to create my own summary.

    Case 1: A table is to be created with the most common numeric data types:

    CREATE TABLE t (
      c1 BINARY_DOUBLE,
      c2 BINARY_FLOAT,
      c3 DEC,
      c4 DECIMAL,
      c5 FLOAT,
      c6 INT,
      c7 INTEGER,
      c8 NUMBER(9,2),
      c9 NUMERIC );


    Case 2: Next, we take the text and other data types:

    CREATE TABLE t (
      c1 char(1),
      c2 char,
      c3 varchar2(4000),
      c4 varchar(4000),
      c5 BLOB,
      c6 CLOB,
      c7 BFILE,
      c8 RAW(2000),
      c9 date,
      c10 timestamp,
      c11 rowid );


    Case 3: The table is to be created with the new LOB storage technology (from 11.1), the secure files:

    CREATE TABLE t (
      c1 NUMBER,
      c2 CLOB,
      CONSTRAINT t_pk PRIMARY KEY (c1) )
      LOB(c2) STORE AS SECUREFILE my_lob_tbs;


    Case 4: The table should only be physically created once the first line has been entered:

    CREATE TABLE t (
      c1 NUMBER )
      SEGMENT CREATION DEFERRED;


    Case 5: The table is to be created with the NOLOGGING attribute:

    CREATE TABLE t (
      c1 INT,
      c2 VARCHAR2(4000) )  -- AB 12.2 bis 32767 möglich
      NOLOGGING;


    The table can subsequently be changed back to LOGGING or NOLOGGING:

    ALTER TABLE t LOGGING;
    ALTER TABLE t NOLOGGING;


    Case 6: You want to specify a special tablespace for the table:

    CREATE TABLE t (
      c1 INT,
      c2 VARCHAR2(4000) )
      TABLESPACE user_tbs;


    Case 7: The table should have a primary key:

    CREATE TABLE t (
      c1 INT PRIMARY KEY );


    Case 8: The table should have a foreign key:

    CREATE TABLE t (
      c1 INT,
      c2 INT,
      c3 INT,
      CONSTRAINT fk_cons FOREIGN KEY (c1, c2,c3) REFERENCES t2(c1, c2,c3);


    Case 9: The table should have a special sorting for a column. This function is only available from 12.2 onwards. In addition, the parameter max_string_size= EXTENDED must be set.

    CREATE TABLE t (
      c1 NUMBER,
      c2 VARCHAR2(2000 CHAR) COLLATE GERMAN_CI );


    Case 10: The table should only store the data temporarily (until the end of the session):

    CREATE GLOBAL TEMPORARY TABLE t (
      c1 INT,
      c2 VARCHAR2(4000) )
      ON COMMIT PRESERVE ROWS;


    Case 11: The table should only store the data temporarily (until the end of the transaction):

    CREATE GLOBAL TEMPORARY TABLE t (
      c1 INT,
      c2 VARCHAR2(4000) )
      ON COMMIT DELETE ROWS;


    Case 12: From version 18, a temporary table can be created which is deleted at the end of the transaction together with its definition.
    Note: It must have a prefix (ora$ptt) in the name. However, this can be changed via the initialization parameter PRIVATE_TEMP_TABLE_PREFIX.

    CREATE PRIVATE TEMPORARY TABLE ora$ptt_t (
      c1 NUMBER,
      c2  VARCHAR2(20) )
      ON COMMIT DROP DEFINITION;


    Case 13: Also only available from version 18, this is the second variant of the private temporary table, which is only deleted at the end of the session:

    CREATE PRIVATE TEMPORARY TABLE ora$ptt_t (
      c1 NUMBER,
      c2  VARCHAR2(20) )
      ON COMMIT PRESERVE DEFINITION;


    Case 14: The table should remember the change SCN per line and not just per block:

    CREATE TABLE t (
      c1 INT,
      c2 VARCHAR2(4000) )
      ROWDEPENDENCIES;


    Case 15: The table is to be based on a file that is outside the database:

    Als Benutzer mit DBA Rechten ausführen:

    CREATE DIRECTORY utl_dir as 'C:\temp';
    GRANT read,write ON DIRECTORY utl_dir TO scott;


    do it as SCOTT User:

    CREATE TABLE scott.emp_ext (
      EMPNO     NUMBER(4),
      ENAME     VARCHAR2(10),
      JOB       VARCHAR2(9),
      MGR       NUMBER(4),
      HIREDATE  DATE,
      SAL       NUMBER(7,2),
      COMM      NUMBER(7,2),
      DEPTNO    NUMBER(2) )
    ORGANIZATION EXTERNAL
      (
        TYPE ORACLE_LOADER
        DEFAULT DIRECTORY UTL_DIR
        ACCESS PARAMETERS
          (
            RECORDS DELIMITED BY NEWLINE
            FIELDS TERMINATED BY ','
            MISSING FIELD VALUES ARE NULL
              (
                empno,
                ename,
                job,
                mgr,
                hiredate   CHAR(21) DATE_FORMAT DATE MASK "DD-MON-YYYY HH24:MI:SS",
                sal,
                comm,
                deptno
              )
           )
         LOCATION ('emp.txt')
       )
      PARALLEL 1
      REJECT LIMIT UNLIMITED;

     

    Case 16: Blockchain table (only from version 21c)

    CREATE BLOCKCHAIN TABLE bc_tab1 ( 
    bank VARCHAR2(128), 
    d_date DATE,   
    d_amount NUMBER )
     NO DROP UNTIL 25 DAYS IDLE
     NO DELETE UNTIL 31 DAYS AFTER INSERT
     HASHING USING "SHA2_512" VERSION v1;


    Case 17: Interval range partitioned table (1 month interval)

    CREATE TABLE t
    (id        NUMBER(6)    NOT NULL,
     datum     DATE         NOT NULL)
    PARTITION BY RANGE (datum)
    INTERVAL (numtoyminterval(1,'MONTH'))
    ( PARTITION p2021 VALUES LESS THAN (to_date('01.01.2021','dd.mm.yyyy')));


    Case 18: Interval Range-Partitioned Table (1 day interval)

    CREATE TABLE t
    (id        NUMBER(6)    NOT NULL,
     datum     DATE         NOT NULL)
    PARTITION BY RANGE (datum)
    INTERVAL (numtodsinterval(1,'DAY'))
    ( PARTITION p2021 VALUES LESS THAN (to_date('01.01.2021','dd.mm.yyyy')));


    Case 19: Interval Range-Partitioned Table (1 year interval)

    CREATE TABLE t
    (id        NUMBER(6)    NOT NULL,
     datum     DATE         NOT NULL)
    PARTITION BY RANGE (datum)
    INTERVAL (numtoyminterval(1,'YEAR'))
    ( PARTITION p2021 VALUES LESS THAN (to_date('01.01.2021','dd.mm.yyyy')));


    Case 20: Range partitioning with own interval

    CREATE TABLE scott.emp_part (
    empno         NUMBER(4),
    ename         VARCHAR2(20),
    hiredate      DATE)
    PARTITION BY RANGE (hiredate)(
    partition year2019 VALUES LESS THAN (to_date('01.01.2020','DD.MM.YYYY')),
    partition year2020 VALUES LESS THAN (to_date('01.01.2021','DD.MM.YYYY')),
    partition year2021 VALUES LESS THAN (to_date('01.01.2022','DD.MM.YYYY'))
    );


    Weitere Interessente Artikel zum Thema:


    Empfohlene Schulungen zum Thema: