Im heutigen Tipp widmen wir uns ein paar Praxisbeispielen zu Regulär Expression (Reguläre Ausdrücke) in Oracle.
Beispiel 1: Kreditkartennummern haben meist das Format 1234-1234-1234-1234Wir suchen in einem String zwei 4er Blöcke mit Zahlen un einem Minus dazwischen und ersetzen jeden Block durch xxxx-xxxx:
select regexp_replace( '1234-5678-1111-2222', '[[:digit:]]{4}-[[:digit:]]{4}','xxxx-xxxx') from dual;
Ergebnis: xxxx-xxxx-xxxx-xxxx
Oder wir suchen vier 4er Blöcke mit Zahlen und ersetzen das mit XXXX:
select regexp_replace( '1234-5678-1111-2222', '[[:digit:]]{4}','xxxx') from dual;
Nur der letzte 4er Block soll ausgegeben werden
select regexp_replace( '1234-5678-1111-2222', '([[:digit:]]{4})(-)([[:digit:]]{4})(-)([[:digit:]]{4})(-)([[:digit:]]{4})','XXXX-XXXX-XXXX-\7') from dual;
Ergebnis:
XXXX-XXXX-XXXX-2222
Alternativ können sie anstatt [[:digit:]] durch \d ersetzen
select regexp_replace( '1234-5678-1111-2222', '\d{4}','x') from dual;
Ergebnis: x-x-x-x
Beispiel 2: Wir löschen einige HTML Tags in einem String:
select regexp_replace( '<div>Hallo</div><strong> Kurs</strong>', '(<\/?div>|<\/?p>|<\/?strong>)','') from dual;
oder wir löschen alle HTML Tags im String
select regexp_replace( '<div>Hallo</div><strong> Kurs</strong>', '<\/?[^>]*>,'') from dual;
Das war eine kleine Beispielauswahl, die von uns ständig erweitert wird. Oder Sie kommen z.B. in den PL/SQL oder PL/SQL II Kurs, da werden diese Themen auch ausführlich besprochen.
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:
Constraint wieder löschen:
Nachträglich einen Primärschlüssel anlegen (Achtung es darf keiner bisher existieren):
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.
Foreign Key Constraint anlegen. (Dieser hat einen Bezug zu einer lokalen Spalte der Tabelle und eine Verbindung zu einer Spalte einer anderen Tabelle)
Der Foreign Key Constraint hat aber noch eine weitere nette Option: On Delete CascadeWenn 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
Alternativ kann auch nur die abhängige Spalte auf NULL gesetzt werden
SonderfälleConstraints 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.
oder wieder zurück auf Default:
Mit einem ALTER TABLE kann der Zustand der verzögerten Constraints verändert werden:
ENABLE VALIDATE
ENABLE
ENABLE NOVALIDATE
DISABLE NOVALIDATE
DISABLE
DISABLE VALIDATE
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:
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:
Hier lohnt sich ein Blick ins Data Dictionary:
Die column_id ist also bei unsichtbaren Spalten nicht gefüllt. Interessanter noch ist die etwas weniger bekannte user_tab_cols:
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:
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:
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?
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.
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:
Dann schauen wir uns die Struktur der Tabelle einmal genauer an:
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:
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.
Zum warmwerden, tragen wir ein paar Texte ein (Ein bisschen Schleichwerbung muss schon sein :-) )
Und testen das mit:
Nun legen wir richtig los:
Wie wäre es mit allen undokumentierten Parametern mit Beschreibung (in zwei Zeilen wegen der Länge):
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:
Das testen wir z. B. mit:
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.
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 SpaltenDer 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:
Oder etwas aufwendiger mit Startwert 100 und Schrittweite 10 (aber ohne Primärschlüssel):
Möchten Sie eine bestehende Identity löschen ?
Auch ein nachträgliches Setzen der Identity ist möglich, mit neuer Spalte:
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:
Oder wenn Sie wissen wollen, welche Spalte vom Typ Identity ist:
Ausführliche Optionssyntax:
Komplexes Beispiel:
>100 weitere neue Funktionen der Oracle 12c Version lernen Sie in unserem Oracle 12c Neuerungen Kurs kennen.
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 UmstiegsSeitdem 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 einemzentralen DBMS zu konsolidieren und sich damit die Verwaltung zu vereinfachen, für die Anderen aus den verschiedenstenGrü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.2ist (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:
Also wollen wir uns in diesem Monatstipp einmal ansehen, wie der eigentliche Migrations- und Upgrade-Vorgang aussehenkönnte. An der Stelle sei darauf hingewiesen, dass es verschiedene Alternativen gibt, um eine Migration von Non-CDB in CDBdurchzufü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 (hiermit 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 hineinwird 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ächsteinmal, was sich allerdings sehr bald rächen soll ...Preupgrade PrüfungFü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üftwerden.Durchführung des Upgrades und FehlerbehandlungIm Internet findet man etliche Anleitungen für das Upgrade, allerdings habe ich gemerkt, dass keine davon zusammenfassendalle Probleme behandelt, in die ich gelaufen bin, geschweige denn Lösungen dafür anbietet. Der folgende Tipp bietet hoffentlicheine hilfreiche Liste der möglichen Schritte und Hindernisse, erhebt jedoch ebenfalls keinen Anspruch auf Darstellung allermöglichen Fehler.Als Grundlage für den Upgrade, soll an dieser Stelle eine Variante gewählt werden, die zwar in der Doku enthalten, aber nachmeiner 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. AchtenSie 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 PLUGGABLEDATABASE 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 (bitteReadme dazu lesen).
…
$ cd /tmp/18633374
$ opatch apply
## CODE ##
Danach der zweite Versuch:
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 neuenORACLE_HOME eingespielt (Achtung Readme dazu).
$ cd /tmp/27105253
SQL> startup
SQL> ALTER PLUGGABLE DATABASE ALL OPEN;
$ cd $ORACLE_HOME/OPatch
$ ./datapatch -verbose
Und der dritte Versuch:
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;
Aber jetzt, der vierte Versuch - na geht doch:
-- Pluggable database created.
In jedem Fall sollte man sich die PDB_PLUG_IN_VIOLATIONS View ansehen, um über WARNINGs und ERRORs informiert zuwerden.
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.
Run noncdb_to_pdb.sql.
Also muss die Pluggable Database PDB_O12C zunächst upgegradet werden. Dies erfolgt über den Aufruf des Perl-SkriptsCATCTL.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 =
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
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 OPEN;
Zu überprüfen ist, ob der STATUS bei allen Meldungen von PENDING auf RESOLVED geändert wurde. Falls nicht, gibt die SpalteACTION 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> ALTER DATABASE LOCAL UNDO ON;
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
FAZITSie haben nun einen Eindruck davon bekommen, was auf Sie zukommen kann, wenn Sie auf die Container-Architektur umstellenund 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, ASModer andere Oracle Optionen berücksichtigt. Deshalb kann nicht vorhergesagt werden, welche zusätzlichen Hürden sich Ihnenin den Weg stellen …
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_serializeJSON_SCALAR wandelt einen Text/ ein Datum oder eine Anzahl in eine interne BLOB Repräsentanz.
Wenn wir das zurückwandeln passiert folgendes:
Da fallen einem gleich zwei Problemzonen ins Auge:1. "" am Anfang / Ende2. ein T zwischen Datum und UhrzeitDer erste Versuch scheitert deswegen auch:
Das liegt an der etwas eigenartigen Behandlung von Gänsefüßen bei Oracle Strings
oder auch
auch das geht schief:
Deshalb lösen wir das Problem mit zwei Funktionen:1. Wir ersetzen "" durch nix2. Wir konvertieren den String mit Hilfe der Funktion to_date und dem Format String 'YYYY-MM-DD"T"HH24:MI:SS'
Weitere Tipps erhalten Sie in einem unserer Oracle Kurse...
Wie fügt man eine Zeile in die Oracle Datenbank ein?
Zuerst legen wir uns mal eine Tabelle an:
1. Single Line Insert (fügt nur eine Zeile ein), kurze Variante:
Der Datensatz ist noch persistent in der DB gespeichert, im Gegensatz zu Postgres Datenbanken !Erst ein Commit, schreibt die Daten permanent in die Tabelle
Bei Tabellen mit vielen Spalten wird diese Variante schnell unübersichtlich. Deswegen empfiehlt sich
2. Single Line Insert, lange Variante
Bei dieser Variante wurden die Spalten mit angegeben. Das hat mehrere Vorteile:
3. Multi Line Insert (hier werden eine oder mehrere Zeilen aus einer anderen Tabelle kopiert)
Voraussetzung ist, dass es eine Tabelle mit Namen mitarbeiter_muenchen gibt, die über die gleiche Spaltenzahl und Spaltentypen verfügt.
4. Die Zieltabelle kann auch auf einer entfernt liegenden Oracle Datenbank liegen. Dazu muss nur ein [Public] Database Link vorhanden sein. Dieser meldet sich bei der Datenbank rcl auf dem Server 162.20.10.1 via Port 1521 als Benutzer SCOTT mit Passwort tiger an.
Oder die Daten sollen lokal in einer Tabelle gespeichert werden, die Ursprungsdaten liegen aber Remote:
SQL lernen ist nicht schwer, kommen Sie einfach zu uns her:-)
Wir haben auch für Sie die passende Schulung im Programm.
Wer hat nicht schon mal vor dem Problem einer Datumskonvertierung bei Oracle Datenbanken gestanden?Da kann sehr viel Zeit verstreichen, bis man eine Lösung gefunden hat. Wir wollen Ihnen hier ein Paar Fallstricke erklären.
Das hängt vom Parameter nls_date_format ab, der auf Clientseite im Betriebssystem gesetzt wird.
z.B. können Sie unter DOS in einem Fenster angeben:
Für Linux:
Im SQL*Developer kann stattdessen auch unter Extras/Voreinstellungen/Datenbank/NLS das Datumsformat eingestellt werden.
Wenn Sie sich in einzelnen Befehlen nicht an den Default halten möchten, können Sie eine Ausgabeformatierung verwenden:
Achten Sie darauf, dass die Minuten mit "MI" gekennzeichnet sind. Für Oracle wäre auch folgendes Format OK:
Nur leider ist das Format falsch, weil im String "HH:MM:SS" Stunden:Monat:Sekunden ausgegeben werden, und das stimmt nur ganz selten mit der tatsächlichen Uhrzeit überein (eine Minute pro Stunde :-)
Es gibt eine riesige Liste an möglichen Datumskonvertierungen die hier nachgelesen werden können.
Nur ein paar Beispiele für Oracle Datumsformate:
Wenn Sie ein Datum in die Oracle Datenbank einpflegen möchten, sollten Sie das idealerweise auch mit einer expliziten Datums-Konvertierung tun.
Beispiel:
Hier werden Stunden, Minuten und Sekunden auf 0 gesetzt. Das spart zwar keinen Speicherplatz, erleichtert aber später die Suche nach einem Einstellungstag. Wenn Sie stattdessen schreiben:
Dann wird die aktuelle Uhrzeit im Datum mit gespeichert. Jetzt versuchen Sie mal nach Mitarbeitern zu suchen, die heute eingestellt wurden:
Nur sollte es einen normalen Index auf der Spalte hiredate geben, wird er nicht benutzt werden. Deswegen könnte man folgende Variante benutzen:
Hier suchen Sie nach allen Mitarbeitern, die zwischen 00:00.00 und 23:59:59 des aktuellen Tages eingestellt wurden.
Für Freunde des JSON Datentyps: JSON verwendet gerne folgendes Datumsformat: 01.08.2022T12:30:39ZMan beachte das T in der Mitte und das Z am Ende.Das können wir wieder in ein Oracle Datumsformat zurückwandeln:
Hat das Ihre Lust auf noch mehr SQL Tipps geweckt? Wir haben bestimmt auch die passende Schulung für Sie!
Mit der SAMPLE-Klausel kann die SQL-Ergebnismenge von 0,000001% bis 100% der ursprünglichen reduziert werden. Oracle greift einfach zufällig in die Ergebnismenge und holt (wählt) einen gewissen Prozentsatz der Daten heraus.In der Schule nannte man das Experiment: Ziehen mit zurücklegen :-)
Schauen wir uns ein paar Beispiele an:
Man sieht also, die Rückgabemenge kann durch statistische Abweichungen mal mehr, mal weniger Zeilen zurückliefern.
Mit dem SEED-Parameter kann die Ergebnismenge auf einer Speicherplatznummer gespeichert werden (hier: Position 1)
Wenn Sie die erste Ergebnismenge nochmal benötigen, setzen Sie einfach das Statement mit SEED(1) nochmal ab:
Wir konnten bei unseren Tests bis Speicheradresse SEED(4294967295) gehen. Nach dieser Speicheradresse wurden die Ergebnismengen nicht mehr geändert. Also egal welche SEED-Adresse darüber hinaus Sie verwenden, Sie bekommen immer die Daten der Nummer SEED(4294967295) zu sehen.
Sie können in das Statement auch eine WHERE-Klausel einbauen:
Auch ein Alias Name für die Tabelle ist möglich (wenn auch an eigenartiger Stelle):
Auch bei Joins steht die Sample-Klausel direkt hinter dem Tabellennamen:
Hier endet auch leider der Nutzen der SAMPLE-Klausel, denn sobald Sie komplexere Statements haben, funktioniert sie nicht mehr!
Gut, dann verwenden wir wieder die altbewährte ROWNUM-Klausel. Wenn Sie verschiedene Varianten eines SQL-Statements austesten möchten und dabei feststellen, jeder Versuch dauert mehr als 3 Stunden, werden Sie diese Möglichkeit zu schätzen wissen.
Wenn das Statement also lautet:
Machen wir daraus:
Und Statements mit WITH-Klausel?
Ab Oracle 12.2:
Oder alternativ:
Wenn die Ergebnismenge sehr groß ist, kann man auch nur einen COUNT(*) verwenden.
Dieser muss auch die ganze Ergebnismenge durchgehen, sich aber nicht mit der (lästigen und langwierigen) Zeilenausgabe beschäftigen:
Da wir immer wieder nach der Syntax zu (Outer) Joins gefragt werden, beschäftigen wir uns in diesem Tipp einmal mit den Neuerungen in Version 12c und den Einschränkungen in den vorangegangenen Versionen.
Vorausgesetzt wird, dass der Leser bereits Erfahrungen mit der Formulierung von Joins gemacht hat und ihm auch die in Oracle 9i eingeführte ANSI-konforme Join Syntax nicht unbekannt ist.
Beginnen wir mit einer Einschränkung, mit der Entwickler und Anwender bis einschließlich Version 11gR2 konfrontiert wurden: Wer über die Oracle-eigene Syntax zwei oder mehr Tabellen mit einer anderen Tabelle über Outer Join verknüpfen wollte, bekam in der Regel einen ORA-01417 Fehler.
Anhand der Tabellen EMP, DEPT und SALGRADE des Benutzers SCOTT soll dies im weiteren Verlauf demonstriert werden. Dabei wurde die Tabelle SALGRADE um eine weitere Zeile ergänzt, die keine Entsprechung in der Tabelle EMP findet.
Die beiden Tabellen DEPT und SALGRADE sollen jeweils durch einen Outer Join mit EMP verbunden werden, was auf folgende Weise bisher noch nicht möglich war:
Mit der Version 12c ist diese Einschränkung aufgehoben worden. Macht man sich anhand der Ergebnismenge allerdings klar, welche Logik hinter dieser Syntax steht, stellt sich die Frage, ob und wann dies wirklich gewünscht ist.
Offensichtlich erzeugt Oracle ein Kreuzprodukt zwischen den beiden Outer-Tabellen DEPT und SALGRADE und gibt davon alle Zeilen aus, die keine direkte Entsprechung zu EMP haben. Bereits vor Version 12c hätte man das selbe Ergebnis über den Trick einer Inline View erhalten können:
Bezogen auf die drei Tabellen von SCOTT, möchte man - sinnvollerweise - zu den 14 Mitarbeitern lediglich noch die Abteilungen aus DEPT sehen, in denen keine Mitarbeiter sind (hier: 40) und die Gehaltsstufen aus SALGRADE in denen sich kein Gehalt aus EMP befindet (hier: 6).
Mittels ANSI-Join-Syntax lautet die - recht übersichtliche - Lösung dazu bereits seit Version 9i:
Über die Oracle-eigene Syntax sieht das schon wieder wesentlich komplizierter aus (und mit zunehmender Tabellenanzahl wird dies nicht besser ...):
Mit Version 12c hat Oracle drei neue Varianten im Bereich der ANSI-Join Syntax aufgenommen.
Das Interessante an allen drei Neuerungen ist die Unterstützung der sog. Left Correlation. Dabei kann z. B. aus der FROM-Klausel innerhalb einer Inline View auf Werte aus einer anderen Tabelle der äußeren FROM-Klausel Bezug genommen werden. Diese andere Tabelle muss sich links von den neuen Schlüsselwörtern APPLY oder LATERAL befinden. Daher der Name Left Correlation.
Der CROSS APPLY ist eine Erweiterung des CROSS JOIN und bildet in der einfachsten Form ein Kreuzprodukt der beteiligten Tabellen. Beispiel:
Verwendet man den CROSS APPLY jedoch in Zusammenhang mit einer Inline View, ergibt sich eine wesentlich reizvollere Alternative. Dazu sollen im nächsten Beispiel ausschließlich Zeilen der (linken) Tabelle DEPT zurückkommen, die auch eine Entsprechung innerhalb der Inline View besitzen. Relevant sind nur die Abteilungen RESEARCH, SALES und OPERATIONS.
Die Abteilung OPERATIONS kommt nicht zurück, da es über die Inline View keinen Treffer zur Abteilungsnummer 40 gibt.
Dabei handelt es sich um eine Variante des LEFT OUTER JOIN. Alle Zeilen der linken Tabelle sollen ausgegeben werden, unabhängig davon, ob es dazu einen Treffer innerhalb der Inline View gibt oder nicht.
Im Gegensatz zum CROSS APPLY-Beispiel erhält man nun auch die Abteilung 40, auch wenn sie keinen Mitarbeiter besitzt.
LATERAL bietet eine Alternative zum Equi- oder Inner Join. Dabei kann auch hier über eine Inline View ein Bezug zur äußeren (linken) Tabelle der FROM-Klausel her
1. Fangen wir mal mit der Substitutionsvariable an:
Wir definieren uns eine Textvariable (etwas anderes gibt es auch nicht)
SQL> DEFINE my_ename='KING'
Diese können wir nun in einem SELECT einsetzen:
SQL> SELECT sal FROM empWHERE ename='&my_ename.';alt 2: WHERE ename='&my_ename.'neu 2: WHERE ename='KING'
SAL---------- 5000
Die alt und neu ... stört etwas, aber die bekommen wir weg:
SET VERIFY OFF
SELECT sal FROM empWHERE ename='&my_ename.';
Mit DEFINE bekommen sie alle gesetzten Variablen angezeigt:
SQL> defineDEFINE _DATE = "11.10.2018 16:18:05" (CHAR)DEFINE _CONNECT_IDENTIFIER = "o18c" (CHAR)DEFINE _USER = "SCOTT" (CHAR)DEFINE _PRIVILEGE = "" (CHAR)DEFINE _SQLPLUS_RELEASE = "1803000000" (CHAR)DEFINE _EDITOR = "Notepad" (CHAR)DEFINE _O_VERSION = "Oracle Database 18c Enterprise Edition Release 18.0.0.0.0 - ProductionVersion 18.3.0.0.0" (CHAR)DEFINE _O_RELEASE = "1803000000" (CHAR)DEFINE MY_ENAME = "KING" (CHAR)
Die Variable bleibt nun bis zum Sessionende am Leben, es sei denn sie löschen sie wieder:
SQL> UNDEFINE my_ename
Was machen wir, wenn wir denn Wert der Variablen vom Benutzer erfragen möchten:
Lösung 1:
SQL> SELECT sal FROM empWHERE ename='&my_ename.';Geben Sie einen Wert f³r my_ename ein: KING
Der Inhalt der Variablen wird nach dem Einsatz gelöscht:
SQL> define my_enameSP2-0135: Symbol my_ename ist nicht definiert (UNDEFINED)
Lösung 2:
SQL> SELECT sal FROM empWHERE ename='&&my_ename.';Geben Sie einen Wert f³r my_ename ein: KING
Hier wird der Inhalt der Variablen bis zum Sessionende (oder UNDEFINE) aufgehoben:
SQL> define my_enameDEFINE MY_ENAME = "KING" (CHAR)
Lösung 3: Der Benutzer soll höflich nach dem Wert gefragt werden:
accept my_ename DEFAULT 'KING' PROMPT "Hätten Sie die Güte, den Namen rauszurücken? "
Hinweis:1. der DEFAULT Wert ist Optional.2. wenn hinter das Statement noch HIDE geschrieben wird, ist die Eingabe verdeckt möglich.
Was machen wir, wenn der Wert der Variablen aus einem Ergebnis einer Query zurückkommt?
SQL> COL my_ename NEW_VALUE my_enameSQL> SELECT ename As my_ename FROM empWHERE empno=7839;
MY_ENAME----------KING
Und nun zu den Bindvariablen:Diese können nur im Filter einer Query eingesetzt werden, sind dafür aber auch in PL/SQL nutzbar.
Wir definieren die Bindvariable mit einem Datentyp:
SQL> VARIABLE my_ename VARCHAR2(100)
Danach füllen wir die Variable mit einem Wert:
SQL> EXEC :MY_ENAME:='KING';
Wir setzen den Bindvariablen-Wert in einer Query ein:
SQL> SELECT sal FROM emp WHERE ename=:MY_ENAMESQL> /
Oder befüllen die Variable direkt aus einer Query
SQL> EXEC SELECT ename INTO :my_ename FROM emp WHERE sal=5000;
Als DBA steht man oft vor dem Problem der Rechtevergabe an die Benutzer. Zu viele Rechte stellen ein Sicherheitsrisiko dar, zu wenige verhindern möglicherweise das Abfragen wichtiger Daten. Damit normale Benutzer Informationen über die genutzte Instanz abfragen können, ohne dass ihnen Rechte an den nötigen v$-Views zugewiesen werden müssen, gibt es eine Funktion namens SYS_CONTEXT.
In diesem Tipp werden von rund 60 Parametern nur die nützlichsten kurz erläutert und die seit Version 12c neu hinzugefügten Parameter aufgezeigt. Diese Parameter sind sehr hilfreich z. B. bei der Erstellung von Automatismen über Skripte.
Mit der Funktion SYS_CONTEXT ist es möglich, Werte verschiedener Parameter der aktuellen Instanz abzufragen. Die Parameter sind verbunden mit einem bestimmten Namensraum. Oracle hat standardmäßig zwei Namensräume integriert, nämlich USERENV, der die aktuelle Session beschreibt, und SYS_SESSION_ROLES (verfügbar ab 11.2.0.4), der anzeigt, ob eine bestimmte Rolle für die aktuelle Session aktiv ist.
Ein Beispiel für eine Abfrage mit USERENV, ausgegeben wird der Name der Datenbank:
Die folgende Abfrage zeigt mithilfe von SYS_SESSION_ROLES, ob der angemeldete Benutzer die DBA-Rolle innehat:
Nachfolgend eine Liste von Parametern für den Namensraum USERENV, die für Skripte wohl am gebräuchlichsten sind:
CURRENT_USERID: Die ID des Benutzers, dessen Rechte momentan aktiv sind.
DATABASE_ROLE: Die Rolle der Datenbank ist entweder PRIMARY, PHYSICAL STANDBY, LOGICAL STANDBY oder SNAPSHOT STANDBY.
DB_UNIQUE_NAME: Name der Datenbank, wie er im Initialisierungsparameter angegeben ist.
HOST: Gibt den Computernamen (mit Domäne) aus.
INSTANCE: Gibt die ID der Instanz aus, zu der die Session verbunden ist.
INSTANCE_NAME: Name der Instanz, zu der die Session verbunden ist.
NLS-Parameter
SERVER_HOST: Gibt den Hostnamen der Maschine aus, auf der die Instanz läuft.
SERVICE_NAME: Name des Dienstes, zu der eine Session verbunden ist.
TERMINAL: Gibt den Computernamen (ohne Domäne) aus.
Seit Version 12.1 gibt es drei neue Parameter:
CDB_NAME: Gibt den Namen der Container-Datenbank aus:
CON_ID: Gibt den Identifier des Containers aus:
CON_NAME: Gibt den Namen des Containers aus:
Die Funktion SYS_CONTEXT bietet den Vorteil, dass man immer die gleiche Syntax verwendet und lediglich die Parameter austauschen muss. Auch wenn es für manche Parameter Alternativen gibt, wie beispielsweise für den Parameter "SESSION_USER", der dieselbe Information wie die SQL-Abfrage "show USER" ausgibt, ist es leichter mit einer Syntax zu arbeiten, als sich viele Views zu merken, auf die man evtl. gar keine Rechte hat.
Sollten Sie Interesse an weiteren neuen Funktionen der Version 12c haben, so besuchen Sie doch unseren Kurs Oracle Neuerungen 12c!
Indizes sind ein spannendes Thema, manchmal hat man zu viele, oder zu wenige. Wir wollen uns in diesem Tipp mal anschauen, wie man diese Probleme löst.
Der nachfolgende SELECT ermittelt, welche Spalten am häufigsten im Einsatz waren, aber nicht indiziert worden sind. Dabei werden folgende Spalten ausgegeben:
Es wird sortiert nach der Benutzungshäufigkeit. Sie können nun entscheiden, ob Sie einen Index auf die Spalte legen möchten.
Bedenken Sie aber, dass ein Index auch Wartungszeit für Inserts/Updates und Deletes benötigt
/*****************************************************************************//* Oracle Index Vorschläge *//*****************************************************************************/
SELECT * FROM (SELECT tc.owner||'.'||o.object_name as segment_name,tc.column_name,round(s.bytes/1024/1024) Size_M,cu.equality_preds "=",cu.equijoin_preds "E-JOIN",cu.nonequijoin_preds "NE-JOIN",cu.range_preds range,cu.like_preds "LIKE", cu.null_preds "NULL",cu.equality_preds+cu.equijoin_preds+cu.nonequijoin_preds+cu.range_preds+cu.like_preds+cu.null_preds as summe,dtm.inserts,dtm.updates,dtm.deletesFROM sys.col_usage$ cu, dba_objects o,dba_tab_columns tc, dba_ind_columns ic, dba_segments s, dba_tab_modifications dtmWHERE o.object_id=cu.obj#AND o.object_name=tc.table_nameAND o.owner=tc.ownerAND cu.equality_preds>0AND cu.intcol#= tc.column_idAND tc.owner=ic.table_owner(+)AND tc.table_name=ic.table_name(+)AND tc.column_name=ic.column_name(+)AND ic.index_name is NULLAND o.owner=s.owner(+)AND o.object_name=s.segment_name(+)AND o.owner=dtm.table_ownerAND o.object_name=dtm.table_nameAND o.owner IN (select username from dba_users where oracle_maintained='N' ) -- alternativ für Oracle <12.1: o.owner IN ('SCOTT','X')ORDER BY 10 desc,1,2)WHERE rownum <30/
oder anders herum betrachtet: Welche Indizes sind evtl unnötig, weil eine Spalte durch mehrere Indizes abgedeckt wird.
REM ###################################################################REM Welche Spalten haben einen Index (alle Indizes in einer Spalte ausgeben)REM ################################################################### SELECT owner,table_name,column_name, index_names FROM (SELECT owner,table_name,column_name, index_names,RANK() OVER (PARTITION BY owner,table_name,column_name ORDER BY len DESC NULLS LAST) AS col_RankFROM(SELECT owner,table_name,column_name,rtrim(index_name||','||i2||','||i3||','||i4||','||i5||','||i6||',',',') index_names,length(index_name||i2||i3||i4||i5||i6) lenFROM (select c.owner,c.table_name,c.column_name,i.index_name||'('|| i.column_position||')' index_name,CASE WHENlead(c.owner||'#'||c.table_name||'#'||i.column_name,1) OVER (order by c.owner,c.table_name,c.column_name)=c.owner||'#'||c.table_name||'#'||c.column_nameTHEN lead(i.index_name||'('|| i.column_position||')',1) OVER (order by c.owner,c.table_name,c.column_name) END i2,CASE WHENlead(c.owner||'#'||c.table_name||'#'||i.column_name,2) OVER (order by c.owner,c.table_name,c.column_name)=c.owner||'#'||c.table_name||'#'||c.column_nameTHEN lead(i.index_name||'('|| i.column_position||')',2) OVER (order by c.owner,c.table_name,c.column_name) END i3,CASE WHENlead(c.owner||'#'||c.table_name||'#'||i.column_name,3) OVER (order by c.owner,c.table_name,c.column_name)=c.owner||'#'||c.table_name||'#'||c.column_nameTHEN lead(i.index_name||'('|| i.column_position||')',3) OVER (order by c.owner,c.table_name,c.column_name) END i4,CASE WHENlead(c.owner||'#'||c.table_name||'#'||i.column_name,4) OVER (order by c.owner,c.table_name,c.column_name)=c.owner||'#'||c.table_name||'#'||c.column_nameTHEN lead(i.index_name||'('|| i.column_position||')',4) OVER (order by c.owner,c.table_name,c.column_name) END i5,CASE WHENlead(c.owner||'#'||c.table_name||'#'||i.column_name,5) OVER (order by c.owner,c.table_name,c.column_name)=c.owner||'#'||c.table_name||'#'||c.column_nameTHEN lead(i.index_name||'('|| i.column_position||')',5) OVER (order by c.owner,c.table_name,c.column_name) END i6FROM all_tab_columns c,all_ind_columns iWHERE c.owner=i.table_ownerAND c.table_name= i.table_nameAND c.column_name= i.column_nameAND c.owner='SCOTT' )))WHERE col_rank=1AND index_names like '%,%'; /* Die letzte Klausel gibt nur Spalten mit mehr als 2 Indizes aus */
Man sollte jetzt nicht gleich alle Indizes löschen, nur weil Sie eine Spalte mehrfach indiziert haben. Evtl. hatte der Index genau eine spezielle Funktion, die ohne ihn nicht mehr funktioniert.Aber Sie haben durch die beiden Select´s eine Diskussionsgrundlage.
Weitere tolle Tipps erhalten Sie in einem unserer Oracle Kurse. Ich hoffe wir sehen uns bald dort :-)
Haben Sie sich vielleicht schon einmal gewundert, warum Sie nach dem Löschen einer Tabelle keinen freien Speicherplatz gewonnen haben, aber dafür plötzlich Objekte mit Namen BIN$c7WXyzMkT1q9DglxITmbRw==$0 (oder so ähnlich) vorhanden sind
Das liegt an einem Feature names RECYCLE BIN.
Durch einen (alten) DROP TABLE-Befehl wird eine Tabelle nicht mehr - wie in den vorangegangenen Versionen - endgültig gelöscht und der Speicherplatz sofort freigegeben, sondern die Tabelle und alle darauf liegenden Indizes (inkl. Constraints) werden umbenannt und dem Recycle Bin zugeordnet. Der Inhalt des Recycle Bin wird somit weiter zur Quota des Benutzers gezählt.
Folgende Befehle zeigen den Inhalt des eigenen "Papierkorbs" an:
SELECT * FROM recyclebin;-- oderSELECT * FROM user_recyclebin;-- oderSHOW RECYCLEBIN
Und über folgenden SELECT kann sich der Administrator den Recycle Bin der gesamten Datenbank anzeigen lassen:
SELECT * FROM dba_recyclebin;
Im ersten Beispiel soll die Tabelle EMP gelöscht und anschließend wieder hergestellt werden.
DROP TABLE emp;SELECT object_name, object_type FROM user_objects WHERE object_name LIKE 'BIN$%';OBJECT_NAME OBJECT_TYPE------------------------------ -------------------BIN$c7WXyzMkT1q9DglxITmbRw==$0 INDEXBIN$vvz7kZGhSoqCVWbdTtoSbQ==$0 TABLE
Hinweis:
Die gelöschten Objekte werden nicht mehr in der USER_OBJECTS-View angezeigt. Gesehen werden sie nur noch in der View USER_RECYCLEBIN oder über:
SHOW RECYCLEBINORIGNAME_PLUS_SHOW_RECYC OBJECTNAME_PLUS_SHOW_RECYC OBJTYPE_PLUS_SHOW_RECYC DROPTIME_PLUS_SHOW_------------------------ ------------------------------ ----------------------- -------------------EMP BIN$vvz7kZGhSoqCVWbdTtoSbQ==$0 TABLE 2018-05-26:09:42:33FLASHBACK TABLE emp TO BEFORE DROP; -- die Tabelle wird unter ihrem alten Namen wieder hergestellt.
Indizes und Constraints bekommen beim Flashback ihren alten Namen nicht automatisch wieder. Sie müssen extra umbenannt werden:
ALTER INDEX "BIN$c7WXyzMkT1q9DglxITmbRw==$0" RENAME TO pk_emp;ALTER TABLE emp RENAME CONSTRAINT "BIN$c7WXyzMkT1q9DglxITmbRw==$0" TO pk_emp;
Im zweiten Beispiel wird die Tabelle EMP endgültig gelöscht und der Speicherplatz freigegeben.
DROP TABLE emp PURGE;-- alternativDROP TABLE emp;PURGE TABLE emp;-- oderPURGE RECYCLEBIN; -- alle Objekte aus dem Benutzer-Papierkorb werden endgültig gelöscht
Vielleicht erscheint dem ein oder anderen Benutzer das Recycle Bin-Konzept überflüssig und er möchte gerne auf dieses Feature verzichten. Da aus Gewohnheit die PURGE-Option beim Löschen aber vergessen wird und der "Papierkorb" somit noch einmal separat geleert werden muss, kann dieses Verhalten auch ausgeschaltet werden.
SHOW PARAMETER recyclebinALTER SESSION SET recyclebin = OFF; -- oderALTER SYSTEM SET recyclebin = OFF;
Weitere Tipps zu Recycle Bin
Tabellen der Benutzer SYS und SYSTEM werden nicht in den Recycle Bin verschoben."
Beim Löschen von Tablespaces oder Benutzern werden die Tabellen nicht in den Recycle Bin verschoben."
SELECT-Zugriffe sind auf die Tabellen des Recycle Bin unter dem vom System-generierten Namen möglich. DDL- und DML-Anweisungen sind nicht zulässig und ergeben den Fehler ORA-38301."
Die Objekte im Recycle Bin werden automatisch gelöscht, wenn die Quota eines Benutzers ausgeschöpft wäre oder ein Tablespace erweitert werden müsste, weil kein Platz mehr verfügbar ist."
Beim Wiederherstellen einer Tabelle müssen eventuelle Fremdschlüssel manuell erzeugt werden."
In unserer täglichen Praxis erleben wir häufig, dass Indizes beim Kunden sehr groß geworden sind. In vielen Fällen wurden in der zum Index gehörenden Tabelle viele Einträge gelöscht. Wenn nicht die Tabelle die gleichen Einträge wieder auffüllt, können Lücken im Index entstehen, die dazu führen, dass der Index sich unnötigerweise aufbläht.
Beispiel: Herr Huber wird aus der Kundentabelle gelöscht, Herr Meyerhuber kommt neu dazu.
Ein Index auf der Namensspalte fragmentiert dadurch. Wie kann man nun herausfinden, ob der Index ein Kandidat für einen Neuaufbau ist? Führen Sie dazu eine ausführliche Analyse des Index durch mit:
ANALYZE INDEX scott.emp_ind VALIDATE STRUCTURE;
Dadurch wird eine weitere Tabelle (index_stats) mit Informationen gefüllt:
SELECT DEL_LF_ROWS*100/decode(LF_ROWS, 0, 1, LF_ROWS) PROZ_GELOESCHT, (LF_ROWS-DISTINCT_KEYS)*100/ decode(LF_ROWS,0,1,LF_ROWS) DISTINCTIVENESS FROM index_stats;
PROZ_GELOESCHT gibt an, wieviel Prozent der Indexeinträge gelöscht und nicht wieder gefüllt wurden. Je höher dieser Wert ist, desto schlechter ist der Indexbaum ausbalanciert. Ab 10 % kann und ab 20 % sollte reorganisiert werden.
DISTINCTIVENESS gibt an, wie häufig ein Indexwert wiederholt vorkommt (<Ges. Anzahl>/ <anz. verschiedener Werte>) *100 / <Ges. Anzahl>. Bei häufiger Wiederholung (z.B. bei 90% sollte ein Bitmap-Index in Betracht gezogen werden).
Ein Index Rebuild können Sie wie folgt vornehmen:
ALTER INDEX <owner>.<index_name> REBUILD; /* Tabelle zum Schreiben gesperrt, für Standard Edition */ALTER INDEX <owner>.<index_name> REBUILD ONLINE; /* Tabelle zum Schreiben NICHT gesperrt, für Enterprise, Express und Personal Edition */
oder mit Speicherparametern:
ALTER INDEX <owner>.<index_name> REBUILD ONLINETABLESPACE <indx_tbs> STORAGE (INITIAL <n>M NEXT <n>M);
Das soll sich ändern, wir haben hier nun die wichtigsten Punkte zu Oracle LONG Spalten zusammengefasst:LONG Spalten wurden in Oracle 7.3 eingeführt und sollten die Beschränkung von VARCHAR2 Spalten die nur 4000 Zeichen aufnehmen können, beheben.Deswegen wurden zwei neue Datentypen geschaffen LONG (kann bis 2 GB an Textdaten aufnehmen) und LONG RAW (kann bis zu 2 GB an Binärdaten speichern).
Leider hat man keine Routinen dazu entwickelt, die mit dieser Speichermenge umgehen konnten. Man hat deshalb mit 2 Speicherbegrenzungen zu kämpfen:
SQL Funktionen können (wenn überhaupt) nur die ersten 4000 Zeichen einer LONG-Spalte nutzen.In PL/SQL kann man die ersten 32760 Zeichen lesen/schreiben. Mit einem Trick (dazu später mehr) lesen wir aber auch darüber hinaus.Wo kann man LONG Spalten verwenden?
1.) Beschränkungen von LONG Spalten
Das schauen wir uns mal genauer an ...
CREATE TABLE t ( id NUMBER, text LONG, bild LONG RAW);
FEHLER in Zeile 4:ORA-01754: Eine Tabelle darf nur eine einzige Spalte des Typs LONG aufweisen
Zweiter Versuch funktioniert, da nur eine LONG Spalte verwendet wird:
CREATE TABLE t ( id NUMBER, text LONG);
Das gleiche kann man auch mit einer LONG RAW Spalte machen:
CREATE TABLE test_LONG_raw ( id NUMBER, long_col_raw LONG RAW);
2.) Constraints auf LONG-Spalten
Nur NOT NULL Constraints sind erlaubt Die Constraint-Typen P (Primary Key), U (Unique Key), C (Check) können nicht verwendet werden.
CREATE TABLE t ( id NUMBER, l LONG NOT NULL);
... alle anderen Constraints sind nicht erlaubt
CREATE TABLE t ( id NUMBER, l LONG PRIMARY KEY);
ORA-02269:Schlüsselspalte kann nicht vom Datentyp LONG sein02269: "key column cannot be of LONG datatype"*Cause: Self-evident.*Action: Change the datatype of the column, or remove the column from the key.
3.) Indizierung von LONG-Spalten
Ein normaler Index kann nicht auf eine LONG-Spalte gesetzt werden, aber ein Text-Index schon.
CREATE INDEX i ON t(text);Error: ORA-00997: Unzulässige Verwendung des Datentyps LONG
Workaround: Text-Index setzen
CREATE INDEX i ON t(text) INDEXTYPE IS ctxsys.context;SELECT * FROM t WHERE CONTAINS( text, 'Muniqsoft GmbH' ) > 0;
4.) LONG Spalte füllen (bis zu 4000 Zeichen)
INSERT INTO t VALUES (1,lpad('#',4000,'#'));
LONG RAW Spalte füllen
INSERT INTO test_LONG_raw (id,LONG_col_raw) VALUES(1,utl_raw.cast_to_raw('abcdefghijklmnopqrstuvwxyz'));
5.) Füllung einer LONG-Spalte mit 32760 Bytes in PL/SQL
DECLARE v_l LONG:=lpad('#',32760,'#');BEGIN INSERT INTO t VALUES (1,v_l); COMMIT;END;/
Update eines LONG-Datentyps mit bis zu 32760 Bytes
DECLARE v_l LONG; CURSOR c IS SELECT text FROM t WHERE id=2 FOR UPDATE;BEGIN FOR r IN c LOOP v_l:=r.text || ' Neuer Text'; UPDATE t SET text = v_l WHERE CURRENT OF c; END LOOP; COMMIT;END;/
6.) Die Funktionen INSTR, SUBSTR und LENGTH..
...können nicht auf LONG-Spalten angewendet werden
SELECT LENGTH(text) FROM t WHERE id=1;
FEHLER in Zeile 1:ORA-00932: Inkonsistente Datentypen: NUMBER erwartet, LONG erhalten
SELECT SUBSTR(text,1,5) FROM t;
FEHLER in Zeile 1:ORA-00932: Inkonsistente Datentypen: NUMBER erwartet, LONG erhalt
SELECT INSTR(text,1,'#') FROM t;
Aber mit einem kleinen Trick bekommen wir das doch noch hinWenn Sie z.B. auf die Spalte view_text aus DBA_VIEWS zugreifen möchten, gehen Sie wie folgt vor:1. rufen Sie die Funktion sys_dburigen mit den Parametern: Eigentümer der Tabelle/View, Name der Tabelle/View und Spaltenname auf:
SELECT sys_dburigen( owner,view_name, text, 'text()').getclob()FROM dba_viewsWHERE owner='SCOTT';
2. verwenden Sie nund ie gewünschte Funktion aus 6. (z.B. substr)
SELECT substr(sys_dburigen( owner,view_name, text, 'text()').getclob(),1,100)FROM dba_viewsWHERE owner='SCOTT';
Das Ganze geht natürlich mit der Long Spalte:TRIGGER_BODY aus Tabelle DBA_TRIGGERSSEARCH_CONDITION aus Tabelle DBA_CONSTRAINTSu.v. weiteren LONG SpaltenAchtung: Die 3 Parameter müssen genau eine Zeile zurückgeben, sonst erhält man den Fehler:ORA-19003 XML document must have a top level elementDeswegen funktioniert der TRcik auch leider nicht bei:
SELECT sys_dburigen( table_owner,partition_name,HIGH_VALUE, 'text()').getxml() FROM all_tab_partitionsWHERE table_owner='AUDSYS' AND table_name='AUD$UNIFIED' ;
7.) Trigger auf LONG Spalten
Ein SQL Statement innerhalb eines Trigger kann Daten in eine LONG oder LONG RAW Spalte einfügen.Die maximale Länge einer Text-Variable (VARCHAR2 oder LONG) in einem Trigger beträgt 32000.:NEW und :OLD dürfen nicht auf LONG oder LONG RAW Spalten gesetzt werden.
LONG oder LONG RAW Spalten dürfen nur referenziert werden, wenn sie in CHAR oder VARCHAR2 konvertierbar sind.
CREATE OR REPLACE TRIGGER tr BEFORE UPDATE OR INSERT OR DELETE ON t FOR EACH ROWBEGIN :new.text:='A';END;/Error: ORA-04093: Referenzen auf Spalten von Typ LONG sind in Triggern nicht zulässig.
8.) Ausgabelänge für Oracle LONG-Spalten in SQL*Plus auf 2 Mio Zeichen setzen (Default= 80)
Wenn Sie diesen Parameter nicht umsetzen, werden bei einer Ausgabe in SQL*Plus nur die ersten 80 Zeichen einer LONG Spalte ausgegeben.
SET LONG 200000
9.) Tabellen mit LONG Spalten kopieren
Eine Tabelle kann mit einem CREATE TABLE AS SELECT ... nicht kopiert werden, wenn LONG Spalten enthalten sind.
CREATE TABLE t2 AS SELECT * FROM t;
FEHLER in Zeile 1:ORA-00997: Unzulässige Verwendung des Datentyps LONG
Workaround mit Konvertierung in einen CLOB Datentypen
CREATE TABLE t2 AS SELECT to_lob(text) as text FROM t;
Tabelle wurde erstellt.
10.) LONG Spalten konvertieren
Oracle empfiehlt, möglichst zeitnah auf die Datentypen CLOB, BLOB und NCLOB zu konvertieren. Diese stehen ab Version 8.0 zur Verfügung.
Beispiel: Umwandeln von LONG in CLOB und LONG Raw in BLOB
ALTER TABLE test_LONG MODIFY (LONG_col CLOB);ALTER TABLE test_LONG_raw MODIFY (LONG_col_raw BLOB);
11.) LONG Spalten in PL/SQL
In PL/SQL wird ein LONG-Datentyp als VARCHAR2(32760) abgebildet. Das heißt folgende beiden Deklarationen sind identisch:
DECLARE v_a LONG; v_b VARCHAR2(32760);BEGIN NULL;END;/
12.) PL/SQL Package zum Berechnen der LONG Spaltenlänge und zum Auslesen von Daten
Das nachfolgende kleine PL/SQL Beispiel zeigt, dass man mit einem Spezial-Package (dbms_sql) auch an die Daten einer LONG Spalte herankommt, die hinter der magischen Grenze von 32760 liegen.
Das Package LLONG bietet Ihnen die folgenden Möglichkeiten:
Berechnung der Länge eines LONG Feldes. Damit können Sie feststellen, ob Sie die Daten auch in VARCHAR2(4000) bekommen könnten.
Der Aufruf dazu lautet:
LLONG.LEN(<rowid>)
SELECT LLONG.LEN(rowid) FROM t;
Herausschneiden von beliebigen 400 Zeichen in der LONG-Spalte
Der Aufruf lautet:
LLONG.SUB_STR(<rowid>, <start_pos>, <end_pos>)
Der Default für die Startposiion ist 1 und der Default für die Endposition ist 4000.
Beispiele:
SELECT LLONG.SUB_STR(rowid) FROM t; SELECT LLONG.SUB_STR(rowid, 400000,404000) FROM t;
Das Suchen innerhalb einer Long Spalte mit Like
LLONG.LIKE2(<rowid>,'<such_str>')
SELECT * FROM t WHERE LLONG.LIKE2(rowid,'MARCO%') >0
Das Anhängen von Daten an eine bestimmte Zeile
LLONG.APPEND(<rowid>,'<TEXT>')
EXEC LLONG.APPEND('AAAUfXAAEAAAAEAAAC',' Anhänger')
Das Anzeigen der Position eines Suchstring:
LLONG.IN_STR(<rowed>,'<TEXT>',<pos>)
SELECT LLONG.IN_STR(rowid,'MARCO',1) FROM t;
Und so sieht das komplette Package aus:
CREATE OR REPLACE PACKAGE LLONG AS FUNCTION sub_str( p_rowid in rowid, p_startpos IN NUMBER DEFAULT 1, p_endpos IN NUMBER DEFAULT 4000) RETURN CLOB; FUNCTION len( p_rowid in rowid) RETURN VARCHAR2; PROCEDURE append ( p_rowid IN ROWID, p_value IN VARCHAR2); FUNCTION like2 ( p_rowid IN ROWID, Like_str In VARCHAR2) RETURN NUMBER; FUNCTION in_str ( p_rowid IN ROWID, search_char In VARCHAR2, position IN NUMBER DEFAULT 1) RETURN NUMBER;
END;/SHOW ERRORS
CREATE OR REPLACE PACKAGE BODY LLONG AS l_cursor integer default dbms_sql.open_cursor; l_n number; --l_LONG_val varchar2(4000); l_LONG_val CLOB; l_LONG_len number; l_buflen number := 4000; l_curpos number := 0; v_tab VARCHAR2(62); v_col VARCHAR2(30); offset integer; v_length integer; v_collen integer;
FUNCTION get_obj_name (r ROWID) RETURN VARCHAR2 AS BEGIN select owner||'.'||object_name INTO v_tab from all_objects where object_id=( select dbms_rowid.rowid_object(r) from dual); RETURN v_tab; END; FUNCTION get_col_name (r ROWID) RETURN VARCHAR2 AS BEGIN select column_name INTO v_col from all_tab_columns where (owner,table_name) IN (selectowner,object_name from all_objects where object_id=( select dbms_rowid.rowid_object(r) from dual)) AND data_type='LONG'; RETURN v_col; END;
PROCEDURE open_curs (p_rowid IN ROWID) AS BEGIN v_tab:=get_obj_name(p_rowid); v_col:=get_col_name(p_rowid); l_cursor:=dbms_sql.open_cursor; dbms_sql.parse( l_cursor,'select ' || v_col || ' from ' || v_tab || ' where rowid = :x',dbms_sql.native ); dbms_sql.bind_variable( l_cursor, ':x', p_rowid ); dbms_sql.define_column_LONG(l_cursor, 1); l_n := dbms_sql.execute(l_cursor); END;
FUNCTION sub_str( p_rowid in rowid, p_startpos IN NUMBER DEFAULT 1, p_endpos IN NUMBER DEFAULT 4000) RETURN CLOB ASBEGIN IF p_endpos-p_startpos > 4000 THEN RETURN 'Max Return Length 4000 Bytes !'; END IF; open_curs(p_rowid); IF (dbms_sql.fetch_rows(l_cursor)>0) then dbms_sql.column_value_LONG( l_cursor, 1, p_endpos-p_startpos, p_startpos ,l_LONG_val, l_LONG_len); END IF; dbms_sql.close_cursor(l_cursor); return l_LONG_val; EXCEPTION WHEN OTHERS THEN RETURN sqlerrm;end; --sub_str
FUNCTION len (p_rowid in rowid) RETURN VARCHAR2 AS BEGIN open_curs(p_rowid);° IF (dbms_sql.fetch_rows(l_cursor) > 0 ) THEN offset := 0; v_collen := 0; LOOP dbms_sql.column_value_LONG(l_cursor,1,32767,offset,l_LONG_val, v_length); v_collen := v_collen + v_length; EXIT WHEN v_length < 32767 OR v_length IS NULL; offset := offset + v_length; END LOOP; END IF;dbms_sql.close_cursor(l_cursor);return v_collen;EXCEPTION WHEN OTHERS THEN RETURN sqlerrm;END; --len
PROCEDURE append ( p_rowid IN ROWID, p_value IN VARCHAR2)ASv_app VARCHAR2(32760);s VARCHAR2(1000);BEGINopen_curs(p_rowid);IF (dbms_sql.fetch_rows(l_cursor) > 0 ) THEN offset := 0; LOOP dbms_sql.column_value_LONG(l_cursor,1,32767,offset,l_LONG_val, v_length); v_app := v_app || l_LONG_val; EXIT WHEN v_length < 32767 OR v_length IS NULL; offset := offset + v_length; END LOOP; END IF;dbms_sql.close_cursor(l_cursor); BEGIN v_app:=v_app||p_value; s:='UPDATE '||get_obj_name(p_rowid)||' SET '||get_col_name(p_rowid)||' =:x WHERE rowid=:y'; EXECUTE immediate s USING v_app,p_rowid; EXCEPTION WHEN OTHERS THEN RAISE_APPLICATION_ERROR(-20000,'Max Col Length Limit 32767eached'); END;END; --append
FUNCTION like2 ( p_rowid IN ROWID, Like_str In VARCHAR2)RETURN NUMBERISBEGINopen_curs(p_rowid);IF (dbms_sql.fetch_rows(l_cursor) > 0 ) THEN offset := 0; LOOP dbms_sql.column_value_LONG(l_cursor,1,32767,offset,l_LONG_val, v_length); IF l_LONG_val LIKE like_str THEN RETURN 1; END IF; EXIT WHEN v_length < 32767 OR v_length IS NULL; offset := offset + v_length; END LOOP;END IF;dbms_sql.close_cursor(l_cursor);RETURN 0;END; --like2
FUNCTION in_str ( p_rowid IN ROWID, search_char IN VARCHAR2, position IN NUMBER DEFAULT 1) RETURN NUMBERISpos NUMBER;BEGINopen_curs(p_rowid);
IF (dbms_sql.fetch_rows(l_cursor) > 0 ) THEN offset := 0; LOOP dbms_sql.column_value_LONG(l_cursor,1,32767,offset,l_LONG_val, v_length); pos:= instr(l_LONG_val,search_char,mod(position,32767)); IF pos>0 and pos+offset>position THEN RETURN pos+offset; END IF; EXIT WHEN v_length < 32767 OR v_length IS NULL; offset := offset + v_length; END LOOP;END IF;dbms_sql.close_cursor(l_cursor);RETURN pos; END; -- instr Function
END; -- PACKAGE/SHOW ERROR
Beispielszenario:
CREATE TABLE t ( text LONG );
INSERT INTO t VALUES ( RPAD( '*', 32000, '*' ) );
BEGIN INSERT INTO t VALUES('#'); FOR i in 1.. 10000 LOOP UPDATE T SET text=text||';'||to_char(i); END LOOP; COMMIT;END;/
SELECT LLONG.LEN( rowid) FROM t;
GETLONG(ROWID)---------------------------------4000
SELECT LLONG.SUB_STR(rowid, 400000, 404000) FROM t;
Suchen in LONG Data Dictionary Spalten:
Als Beispiel wird hier in der Tabelle user_constraints Spalte search_condition nach einem Wert gesucht. Das Beispiel ist leich abänderbar, damit auch die Tabellen DBA_VIEWS (Spalte text) oder DBA_TRIGGERS (Spalte trigger_body) mit ihren LONG Spalten ausgelesen werden können.
CREATE OR REPLACE FUNCTION get_search_condition( p_cons_name IN VARCHAR2 )RETURN VARCHAR2 authid current_user IS l_search_condition user_constraints.search_condition%type; BEGIN SELECT search_condition into l_search_condition FROM all_constraints WHERE constraint_name = p_cons_name; RETURN l_search_condition;END;/
SELECT constraint_name FROM all_constraintsWHERE owner='SYSTEM'AND get_search_condition(constraint_name) LIKE '%NOT NULL%';
PS: Die Konvertierung der Funktion kann einige Zeit in Anspruch nehmen!
CONSTRAINT_NAME------------------------------SYS_C002892.....
Zum Vergleich ohne Funktion:
SELECT constraint_name FROM user_constraints; WHERE search_condition LIKE '%NOT NULL%'; where search_condition like '%NOT NULL%' *FEHLER in Zeile 2:ORA-00932: Inkonsistente Datentypen: NUMBER erwartet, LONG erhalten
Monatskalender in SQL*Plus
Haben Sie sich auch schon mal gewünscht, dass SQL*Plus einen Monat als schönen Kalender darstellt? Mit der folgenden Funktion können Sie sich den aktuellen Monat (Default) oder einen beliebig anderen Monat anzeigen lassen:
CREATE OR REPLACE FUNCTION show_cal (month IN VARCHAR2 DEFAULT sysdate)RETURN VARCHAR2 IS v_week VARCHAR2(4096); v_firstday NUMBER; v_lastday NUMBER;BEGIN v_firstday:=to_char(trunc(to_date(month),'MM'),'D'); v_lastday:=to_number(to_char(last_day(to_date(month)),'DD')); v_week:='Monat: '||to_char(to_date(month),'FM Month RRRR')||chr(10); v_week:=v_week||'Mon Die Mit Don Fre Sam Son'||chr(10)|| '-------------------------------------'||chr(10); FOR i IN 1.. v_lastday+v_firstday-1 LOOP IF i>=v_firstday then v_week:=v_week||rpad((i-v_firstday+1),4,' '); ELSE v_week:=v_week||rpad(chr(32),4,chr(32)); END IF; IF mod(i,7)=0 THEN v_week:=v_week||chr(10); END IF; END LOOP; RETURN v_week;END;/show errorsSELECT show_cal FROM dual;
Ausgabe:
SHOW_CAL---------------------------------------------------Monat: März 2007 Mon Die Mit Don Fre Sam Son--------------------------------------------------- 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
Da viele grafische Tools (Toad, SQL Developer, ...) mit einem Zeilenumbruch nichts anfangen können, hier eine zweite Alternative mit einer Pipelined Function (nur ab Oracle 10g).
CREATE OR REPLACE TYPE calender_week_typeAS OBJECT (woche VARCHAR2(64));/CREATE OR REPLACE TYPE calender_type as table of calender_week_type;/CREATE OR REPLACE FUNCTION show_cal (month IN VARCHAR2 DEFAULT sysdate)RETURN calender_type PIPELINED IS v_week VARCHAR2(4096); v_firstday NUMBER; v_lastday NUMBER;BEGIN v_firstday:=to_char(trunc(to_date(month),'MM'),'D'); v_lastday:=to_number(to_char(last_day(to_date(month)),'DD')); PIPE ROW (new calender_week_type('Monat: '||to_char(to_date(month), 'FM Month RRRR') )); PIPE ROW (new calender_week_type('Mon Die Mit Don Fre Sam Son')); PIPE ROW (new calender_week_type('--- --- --- --- --- --- ---')); FOR i IN 1.. v_lastday+v_firstday-1 LOOP IF i>=v_firstday then v_week:=v_week||rpad((i-v_firstday+1),4,' '); ELSE v_week:=v_week||rpad(chr(32),4,chr(32)); END IF; IF mod(i,7)=0 THEN PIPE ROW (new calender_week_type(v_week)); v_week:=''; END IF; END LOOP;END;/show errorsselect * from TABLE(CAST(show_cal AS calender_type));
Woche--------------------------------------------------Monat: März 2007 Mon Die Mit Don Fre Sam Son-------------------------------------------------- 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 7 Zeilen ausgewählt.
Tabellenzusammenfassung
Wollten Sie immer schon mal eine Ausgabe, die nur wenige Spalten besitzt, Platz sparend ausgeben?
In unserem Beispiel machen wir aus zwei Spalten eine, geben das Ganze aber dann doch wieder zweispaltig aus.
Verwirrt? Kein Problem, ein kleines Beispiel soll die Ausgabe verdeutlichen:
Normale Ausgabe:A B-- --1 23 45 67 8
Wird zu
C1 C2---- ----1 2 3 45 6 7 8
Das Ganze mit der Tabelle v$parameter:
Oder mit der Tabelle v$option:
Viel Spaß beim Einsparen von Ausgabeseiten und damit Zellulose und damit CO2
Wollten Sie schon immer auf einfache Art und Weise Ihre Constraints umbenennen, damit Sie sofort erkennen, um welchen Typ es sich handelt (beispielsweise in Fehlermeldungen, in welchen eine Verletzung eines Constraints bemerkt wird)? Dann haben wir jetzt den passenden Tipp für Sie, mit dem Sie alle Ihre Constraints mit wenig Aufwand umbenennen können.Der Befehl zum Umbenennen lautet allgemein:
Alle notwendigen Informationen erhalten wir aus den Data Dictionary Views DBA_CONSTRAINTS und DBA_CONS_COLUMNS. Aus DBA_CONSTRAINTS lesen wir den Eigentümer (OWNER), den Namen des Constraint (CONSTRAINT_NAME), den Typ des Constraint (CONSTRAINT_TYPE) und den Tabellennamen (TABLE_NAME), aus DBA_CONS_COLUMNS den Namen der Spalte, auf welcher der Constraint liegt (COLUMN_NAME). Um die zusammengehörenden Constraint-Eigenschaften zu erhalten, müssen die Tabellen verknüpft werden (OWNER und CONSTRAINT_NAME müssen bei beiden gleich sein). Durch einen SELECT geben wir alle Informationen automatisch als fertigen ALTER TABLE-Befehl aus. Dazu müssen Sie Strings (Konstanten) und Spaltennamen miteinander verknüpfen.
Ein Beispiel wäre hier (der Eigentümer lautet SCOTT, die Tabelle heißt EMP, der Spaltenname EMPNO und auf dieser Spalte liegt ein Constraint vom Typ Primary Key):
Da wir aber gerne eine genauere Bezeichnung der Constraint-Typen im Namen hätten, für
C (not null) = NN C (check) = CK P (Primary Key) = PK U (Unique Constraint) = UK R (Foreign Key) = FK
müssen wir noch weitere Bedingungen einfügen. Geben Sie hierzu eine CASE-Klausel in den SELECT-Befehl ein:
Wie Sie sehen, kann das noch nicht funktionieren, da für 'C' zwei verschiedene Werte ausgegeben werden sollen (zwei verschiedene Typen, C (not null) und C (check) werden mit dem gleichen Buchstaben gekennzeichnet). Nun müssen Sie noch zwei weitere Bedingungen anhängen. Dazu lässt sich die Spalte SEARCH_CONDITION der View DBA_CONSTRAINTS verwenden. Allerdings ist diese vom Datentyp LONG, was zur Folge hat, dass man auf diese Spalten keinen LIKE-Operator anwenden kann.
Deshalb greifen Sie auf einen bereits vorhandenen Tipp zurück, in welchem erklärt wird, wie in LONG-Spalten gesucht werden kann.
Aus Vereinfachungsgründen wird ein extra View erzeugt, der die Data Dictionary Views DBA_CONSTRAINTS und DBA_CONS_COLUMNS joint.
Der eigentliche SELECT-Befehl, der alle Befehle zum Umbenennen ausgibt lautet schließlich folgendermaßen:
Dann nur noch die Ausgabe in eine Spool-Datei umleiten, die Datei ausführen und fertig.Viel Erfolg!
Eigentlich bin ich ja ein großer Oracle Fan. Aber manchmal ärgere ich mich auch über Unzulänglichkeiten der Software. Man kann zwar einen Index danach überwachen, ob er verwendet wird, aber nicht, wie oft. Das ist so, als wenn der Tacho eines Autos nicht die Tageskilometer anzeigen würde, sondern nur "Auto wurde bewegt". Das zweite Problem ist, dass bei einer Überwachung eines Index nur der Inhaber des Index sieht, ob er verwendet worden ist oder nicht. Dem Administrator fehlt diese Möglichkeit der Überprüfung. Das ist doch Anarchie :-)
Nun, Problem bekannt, Problem gebannt. Das folgende Skript ist auf den Benutzer SCOTT abgestimmt und legt für ihn eine Tabelle und eine Procedure an. Sie können natürlich gerne auch einen anderen Benutzer verwenden (z.B. SYSTEM).
Die Idee ist folgende:
Die Datenbank zeichnet in der View v$object_usage auf, ob ein Index benutzt worden ist:
Wir prüfen also in einem bestimmten Zeitintervall, ob der Index benutzt worden ist. Wenn ja, dann zählen wir einen Zähler (Tabelle scott.index_usage_count Spalte count_usage) um eins hoch. Dann schalten wir die Indexüberwachung aus und wieder ein. Nach einer gewissen Zeitspanne wiederholt sich der Vorgang. Je kleiner Sie das Intervall setzen, desto genauer ist das Ergebnis. Auf jeden Fall ist es genauer als die Angabe benutzt/nicht benutzt. :-)
Auf der anderen Seite steigt mit der Verkleinerung des Intervalls die CPU-Last.
Insofern sollte man sich für den goldenen Mittelweg entscheiden (wir verwenden bei uns 15 Minuten).
Noch ein paar Anmerkungen:
Die Procedure schaltet nicht die Indexüberwachung ein, sondern überwacht sie nur.
Wenn Sie die Indexüberwachung für ein komplettes Schema (z.B. SCOTT) einschalten wollen, gehen Sie bitte wie folgt vor:
Noch ein paar Anmerkungen:Die Procedure schaltet nicht die Indexüberwachung ein, sondern überwacht sie nur.Wenn Sie die Indexüberwachung für ein komplettes Schema (z.B. SCOTT) einschalten wollen, gehen Siebitte wie folgt vor:
So, und nun viel Spaß bei der Indexüberwachung. Wenn Sie noch mehr tolle Tricks erfahren wollen, dann kommen Sie doch z.B. in einen unserer Kurse über DB Monitoring, Tabellen/Index-Administration oder DB Tuning. :-)Aber alle anderen Kurse bei uns kann man natürlich auch empfehlen...
Hatten Sie auch schon mal mit (einfachen) Anführungszeichen (… ' …) in Ihrer Datenbank zu kämpfen?? Entweder sie sind durch einen Ladevorgang mit fehlerhaften Daten (ungewollt) entstanden und sollen nun bereinigt werden oder sie sind ein korrekter Teil Ihres Datenbestandes, der durch Lese- bzw. Schreiboperationen angesprochen werden soll.
Egal, ob für Sie der erste oder der zweite Fall zutrifft, da das Anführungszeichen in aller Regel den Anfang bzw. das Ende eines Strings kennzeichnet, bedarf es schon eines Tricks, um es gezielt als Teil eines Strings zu verwenden.
Im folgenden Beitrag werden verschiedene Beispiele mit jeweils zwei Lösungsmöglichkeiten (mit bzw. ohne Quote Operator) vorgestellt. Da der Quote Operator erst ab Version 10g zulässig ist, muss in älteren Versionen mit anderen Mitteln (z.B. CHR(39)) gearbeitet werden.
Das Anführungszeichen ist Teil des zurückgegebenen Strings. Es soll der folgende String ausgegeben werden "Willkommen auf Muniqsoft's Homepage".
Lösung mit Quote Operator:
Lösung ohne Quote Operator (diese basiert auf einem zu ASCII kompatiblen Zeichensatz):
Das Anführungszeichen ist Teil eines Strings, der über einen INSERT-Befehl in eine Tabelle eingetragen werden soll.
Achtung: Für das Beispiel muss die ENAME-Spalte der EMP-Tabelle auf VARCHAR2(12) verlängert werden.
Es sollen alle Werte aus einer Spalte ermittelt werden, die ein Anführungszeichen enthalten.
Eine weitere Alternative bietet die in 10g eingeführte REGEXP_LIKE-Funktion:
Die Anführungszeichen in einer einzelnen Spalte sind ungewollt und sollen entfernt werden.
Es soll ein Skript erzeugt werden, das zu jedem Datensatz der EMP-Tabelle einen entsprechenden INSERT-Befehl erzeugt. Damit können Sie sich die Inhalte Ihrer wichtigsten Tabelle sichern.
Lösung ohne Quote Operator:
Die vorangegangenen Beispiele haben Ihnen einen Überblick im Umgang mit Anführungszeichen gegeben und Ihnen den in Version 10g neu eingeführten Quote Operator vorgestellt. Stattdessen kann natürlich auch weiterhin die Charakterdarstellung des ASCII-Wertes 39 benutzt werden (CHR(39)).
Egal, welche Variante Sie einsetzen, wir hoffen, dass Sie nun allen Problemen mit Anführungszeichen gewachsen sind.
Die Verallgemeinerung des letzten Beispiels für eine beliebige Tabelle ist etwas komplizierter und allein mit SQL-Mitteln nicht zu erreichen.
Haben Sie nicht auch schon einmal einen SQL-Befehl abgesetzt (z.B. DROP TABLE oder DROP INDEX) und sich anschließend gewünscht, Sie könnten die Zeit wieder zurückdrehen?Bislang war dies entweder nur mit dem Einspielen eines funktionierenden Backups und einem anschließenden unvollständigem Recovery (was in der Regel sehr zeitaufwändig war) oder mit dem Import einer zuvor angelegten Dump-Datei möglich. Bereits in Version 9i wurde das Feature FLASHBACK eingeführt, mit dessen Hilfe es möglich ist, Daten zu einem in der Vergangenheit liegenden Zeitpunkt auf Session- bzw. Statementebene zur Verfügung zu stellen. Es konnten jedoch keine DDL-Anweisungen rückgängig gemacht werden. Seit Version 10g wird dieses Feature nun um zusätzliche Konzepte erweitert, durch die sich u.a. auch schwerwiegende Anwenderfehler schnell und einfach beheben lassen.
Das FLASHBACK DATABASE-Konzept
Das FLASHBACK DATABASE-Konzept bietet eine schnelle und unkomplizierte Möglichkeit, ein unvollständiges Recovery durchzuführen und damit einen irrtümlich abgesetzten DROP TABLE oder DROP USER-Befehl zurück zu setzen. Dafür gibt es einen neuen Bereich in der Datenbank, die sog. FLASH RECOVERY AREA, in der spezielle FLASHBACK Log-Dateien abgelegt werden. Die Konfiguration dieses Bereichs wird durch verschiedene Initialisierungsparameter gesteuert:
DB_RECOVERY_FILE_DEST: Datei-Verzeichnis der FLASH RECOVERY AREADB_RECOVERY_FILE_DEST_SIZE: Größe der FLASH RECOVERY AREA (Default:3G in 11g)DB_FLASHBACK_RETENTION_TARGET: Zeitspanne in Minuten, um die die DB zurückversetzt werden kann (Default: 1440 = 1 Tag)Damit die FLASHBACK DATABASE-Option genutzt werden kann, muss FLASHBACK (bis Oracle 10.x) in der MOUNT-Phase der DB eingeschaltet werden (und sich die DB im Archivierungs-Modus befinden). Ab Version 11.x können Sie den Flashback Modus auch im laufenden Betrieb ein- und ausschalten.
Informationen zu Speicherplatz und Rücksprungzeit liefert Ihnen die View v$flashback_database_log:
Das Zurücksetzen der Datenbank im Fehlerfall erfolgt entweder anhand einer SCN oder eines Zeitstempels. Die aktuelle SCN kann über folgenden SELECT ermittelt werden:
Beispielszenario:Vor einer Stunde ist ein wichtiger Benutzer mitsamt seinen Objekten gelöscht worden. Das gesamte Benutzerschema muss unbedingt wieder hergestellt werden. Flashback ist bereits eingeschaltet worden.
Die Datenbank ist nun physikalisch in einen früheren, konsistenten Zustand versetzt worden und lässt sich nur noch mit OPEN RESETLOGS öffnen:
Das FLASHBACK TABLE-KonzeptMuss nicht die gesamte Datenbank zurückgesetzt werden, sondern lediglich einzelne Tabellen, wird dies nun mit Hilfe des FLASHBACK TABLE-Kommandos erreicht: Beispiel:Tabelle um 12 Minuten zurücksetzen
SQL> FLASHBACK TABLE scott.emp, scott.dept TO TIMESTAMP (SYSTIMESTAMP -INTERVAL '12' MINUTE);Zurücksetzen auf SCN 1234567:
Zurücksetzen auf den 8.8.2019 11 Uhr 09
Weitere Interval-Beispiele:3 Stunden, 4 Minuten und 11 Sekunden:
1 Stunde, 52 Minuten:
Mit folgenden SQL Befehl können alte Versionen von Tabellendaten ermittelt werden.
Hinweise:Auf diese Weise können nur DML-Anweisungen zurückgesetzt werden, keine DDL-Anweisungen. Constraints und Indizes werden mitberücksichtigt. Trigger sind standardmäßig ausgeschaltet, können aber mittels ENABLE TRIGGERS während des FLASHBACKs aktiviert bleiben. Da es während des FLASHBACKs u.U. zu Änderungen der ROWIDs kommen kann, muss vorher die Option ROW MOVEMENT eingeschaltet werden:
Beim Zurückspielen einer Tabelle aus dem Mülleimer, behalten alle mit der Tabelle verbundenen Objekte wie Constraints oder Indizes, den Namen, den Sie im Mülleimer bekommen hatten (laut Oracle ist das ein Feature und kein Bug:-) ).Sie heißen dann z.B. BIN$fTZdwdToK3rgVQAAAAAAAQ==$0. Der fettmarkierte Teil ist bei allen gelöschten Objekten gleich.
Das RECYCLEBIN-KonzeptSeit Version 10g gibt es bei Oracle eine Art Papierkorb, den sog. RECYCLEBIN. Das normale Löschen einer Tabelle verschiebt die Tabelle lediglich in den RECYCLEBIN und der Speicher wird nicht freigegeben. Den Inhalt des Papierkorbs kann man sich anzeigen lassen:
Eine gelöschte Tabelle kann wiederhergestellt werden über:
Hinweis: Es dürfen seit dem Zeitpunkt, auf dem zurückgesetzt werden soll, keine DDL Befehle (ALTER, TRUNCATE,...) auf dem Objekt abgesetzt worden sein.
Endgültig gelöscht wird eine Tabelle nun über folgende Befehle:
Sie können jedoch auch den Mülleimer der Datenbank abschalten:
Jedoch muss für einen Effekt danach die Datenbank neu durchgestartet werden. Witzigerweise konnten wir auch noch gedropte Tabellen aus dem Mülleimer in unserer Oracle 19c SE Datenbank retten, obwohl der Mülleimer abgeschalten war.Transaction Flashback
In diesem Monatstipp befassen wir uns mit der Erstellung eines Spool-file als HTML Dokument.
Mit dem SQL*Plus Befehl Set Markup HTML ON [SPOOL ON] lässt sich leicht ein Report bzw. ein ganzes Spool-file als HTML-Datei ausgeben. Dies ist nützlich, falls Sie beispielsweise einen Report in eine Webanwendung einbauen möchten.
Am besten schreiben Sie sich ein Skript, in welchem alle für Sie wichtigen Abfragen vorkommen. Auf jeden Fall sollte am Anfang des Skripts der Befehl spool <Pfad\Dateiname> vorkommen. Dieser bewirkt die Aufzeichnung Ihrer Abfragen (zum Beispiel: spool c:\TEMP\test.html). Anschließend müssen Sie noch die HTML-Ausgabe einschalten. Das erreichen Sie durch den Markup-Befehl: Set Markup HTML [ON/OFF] (Gibt die Markup-Sprache an)
SPOOL [ON/OFF] (Gibt an, ob diese auch für das Spool-file gilt)
Geben Sie nun mit dem Befehl prompt das Grundgerüst Ihrer HTML-Datei an.
Das kann zum Beispiel so aussehen:
prompt <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">-
<html><head><title> Spool-file als HTML</title></head>
Wenn sich ein SQL-Befehl wie in diesem Beispiel über mehrere Zeilen erstreckt, müssen Sie das durch ein "-" am Ende einer jeden Zeile signalisieren.
Wenn Sie nun einen einfachen SELECT-Befehl absetzen und anschließend das Spool-file mit spool off abschließen, wird dieser als HTML-Tabelle abgespeichert.
Das bisherige Skript sieht beispielsweise so aus:
Zu sehen ist nun eine einfache Tabelle ohne spezielle Formatierung:
Um das Ganze jetzt noch ein wenig aufzupeppen können wir noch Stylesheets einfügen. Platzieren Sie dazu einfach in dem ersten prompt-Befehl zwischen </title> und </head>-Tag einen weiteren Zusatz:
In diesem CSS-Zusatz können Sie alle möglichen Formatierungsangaben machen. Die genaue HTML-Syntax zu erläutern würde den Rahmen des Monatstipps hier sprengen. (Der CSS-Einschub ist außerdem nicht zwingend erforderlich).
Um die Angaben für alle weiteren Tabellen zu übernehmen müssen Sie in dem
set markup-Befehl eine Kleinigkeit erweitern.
Der Befehl lautet dann wie folgt:
Bisher sieht das Ganze dann so aus:
Als besonderes "Schmankerl" können Sie sich noch eine Übersichtsliste einfügen mit entsprechenden Links zu verschiedenen Tabellen.
Angenommen Sie haben eine HTML-Datei mit etwa 20 Tabellen vor sich, da wäre ein kleines Inhaltsverzeichnis mit eingebauten Links doch ziemlich praktisch...
Realisieren können Sie das, indem Sie zunächst in dem prompt-Befehl eine Liste mit Links einbauen. Der Tag dazu lautet <ul></ul>. Dieser erzeugt eine neue Liste, diese hat allerdings noch keine Einträge. Neue Einträge erstellen Sie mit dem Tag <li></li>. Eine Liste sieht dann beispielsweise so aus:
In unserem Fall müssen wir nach jeder Zeile noch ein "-" einfügen, da SQL*Plus die Eingabe nicht als ganzen Befehl wahrnimmt.
Jetzt benötigt man nur noch die Syntax für den Hyperlink. Dieser lautet:
<a name="Linkname">Linktext</a> (Legt einen Link fest)
<a href="#Linkname">Linktext</a> (verweist auf den Link mit Namen "Linkname")
Um einer Tabelle einen Link zuweisen zu können, müssen Sie erst noch einen Tabellentitel erstellen, auf diesen kann dann verwiesen werden.
Geben Sie einfach vor einem SELECT-Statement title 'Titeltext mit entsprechendem Link' ein und über der Tabelle erscheint eine Überschrift, auf welche man sich dann im Inhaltsverzeichnis beziehen kann.
Jetzt sieht das SQL-Skript so aus:
Tabelle EMP
Tabelle DEPT
Eine immer wiederkehrende Fragestellung - z.B. bei der Erstellung von Berichten - lautet vereinfacht: "Wie mache ich Zeilen zu Spalten?" Das heisst, Sie haben normalisierte Tabellen, wollen aber den Inhalt gruppieren und in Form einer Kreuztabelle (auch Pivot Tabelle genannt) darstellen.
Ein sehr vereinfachtes Beispiel soll demonstrieren, was damit gemeint ist. Sie haben folgende Tabelle mit Verkaufszahlen:
Diese wurde durch folgendes Skript erzeugt:
Nun hätten Sie gerne folgende Darstellung:
Das geht auch schon vor Version 11g, z.B. durch folgende Anweisung:
Zugegeben - nicht sonderlich intuitiv, nicht gut lesbar und einiges an Tipparbeit - aber es funktioniert.
In 11g wurde für solche Zwecke eigens eine neue Klausel eingeführt - PIVOT. Ein Select, der das gleiche Ergebnis (mit einem kleinen Schönheitsfehler, s.u. ) liefert, würde dann so aussehen:
Sehen wir uns die Syntax genauer an. Innerhalb der PIVOT-Klausel müssen Sie angeben:
Die Art der Aggregierung(en) - hier SUM(menge)
FOR-Klausel: Die Inhalte welcher Spalte zu Zeilen werden sollen - hier produkt
Nach allen weiteren im Select angegebenen Spalten wird implizit gruppiert - hier also nach Kunde. Sobald Sie nur nach bestimmten Spalten gruppieren wollen (was praktisch immer der Fall ist), müssen Sie mit einer Inline View (wie hier gezeigt) oder alternativ mit der WITH-Klausel arbeiten.
Der oben angesprochene Schönheitsfehler liegt darin, dass die Spaltenüberschriften genau den angegebenen Werten entsprechen, inklusive Hochkommata. Um das zu umgehen - oder weil Sie sowieso andere Überschriften wollen - können Sie mit Spaltenaliasen arbeiten, wie Sie es gewohnt sind:
Sie können auch, durch Kommata getrennt, mehrere Aggregatsfunktionen angeben; in diesem Fall müssen Sie zusätzlich einen Alias mit angeben, der dann an die Spaltenüberschrift angehängt wird:
Eine Einschränkung bleibt: Sie können nicht dynamisch arbeiten, sondern müssen explizit die Werte angeben.
Gibt man das Schlüsselwort XML mit an, so erhält man statt einzelner Spalten für jeden Wert eine einzige neue Spalte vom Typ XMLType, die alle Wertepaare als XML-Fragment beinhaltet. Hier hat man die Wahl zwischen dem Schlüsselwort ANY und einer Unterabfrage. Die explizite Angabe einzelner Werte ist in diesem Fall wiederum nicht zulässig. ANY steht als Platzhalter für alle Werte, die in der Spalte vorkommen. Der Unterschied in der Ausgabe liegt darin, dass bei einer Unterabfrage im erzeugten XML grundsätzlich alle Produkte erscheinen, auch wenn kein Eintrag für das entsprechende Produkt da ist, während bei ANY nur diejenigen Produkte erscheinen, für die es einen Wert gibt, in folgenden nur gezeigt für Kunde B. Beachten Sie, dass die PIVOT-Klausel noch vor der WHERE-Klausel stehen muss:
-- bzw:
Die Umkehrung - Spalten als Zeilen ausgeben - ist ab 11g auch sehr leicht möglich. Gehen wir von folgender Tabelle aus:
Erzeugt wurde sie durch:
Will man vor 11g aus Artikel- und zugehörigen Mengenangaben Einzeleinträge machen, so ist das zwar machbar, aber umständlich und sehr imperformant, da die Tabelle mehrfach gelesen werden muss:
Hier ist die neue UNPIVOT-Klausel wesentlich einfacher:
Werfen wir auch hier einen Blick auf die Syntax. Angegeben werden muss hier:
Eine Spaltenüberschrift für die Werte - hier menge
Eine Spaltenüberschrift für die Spalten, die zu Zeilen werden sollen - hier produkt
Auch hier gilt: Soll nur ein Teil der Spalten ausgegeben werden, so muss mit einer Unterabfrage gearbeitet werden.
Obiger Select beispielsweise wäre folgendermaßen umzuwandeln, wenn die Tabelle noch mehr Spalten enthielte:
Standardmäßig werden für NULL-Werte keine Zeilen ausgegeben. Diese können aber optional angefordert werden durch INCLUDE NULLS:
Wird statt INCLUDE NULLS angegeben EXCLUDE NULLS, so entspricht dies dem Standardverhalten.
In jeder Oracle Datenbank mit mehr als einer Session kann es zu Konflikten mit Sperren kommen. Sperren, in der Datenbanksprache Locks genannt, verhindern unter anderem, dass mehrere Benutzer zur gleichen Zeit die gleiche Zeile einer Tabelle verändern können.
Innerhalb einer Transaktion können eine oder mehrere Zeilen, sowie ganze Tabellen gesperrt werden. Oracle versucht automatisch (implizit) zu Beginn einer Transaktion die betroffenen Zeilen auf niedrigstem Level zu sperren. Jeder Benutzer kann auch manuell (explizit) eine Sperre auf Tabellen Ebene setzen.
Es gibt unterschiedliche Modi, wie Tabellen und Zeilen gesperrt werden. Die einzelnen Sperrmodi und welcher Zugriff dabei für andere Benutzer noch möglich ist, wird in diesem Monatstipp etwas genauer beleuchtet.
ROW SHARE
Ist die am wenigsten restriktive Sperrmethode
Erlaubt anderen Transaktionen gleichzeitigen Zugriff auf die gesperrte Tabelle. Select, Update, Insert, Delete Operationen, sowie row share, row exclusive, share und share row exclusiv Sperren sind erlaubt.
Verhindert, dass andere Transaktionen die Tabelle für exklusiven Zugriff sperren:
Erläuterung:
Nach dem Lock Table und Update Statement halten beide Sessions Sperren auf der Tabelle EMP. Wird mit der zweiten Session versucht, die Tabelle exklusiv zu sperren, wird dieses verhindert, indem die 1. Session die 2. Session blockiert. Gleichzeitig wechselt der Status der Sperre der 1. Session in der Spalte BLOCKING_OTHERS in der View DBA_DML_LOCKS von Not Blocking auf Blocking.Aufgelöst wird diese Situation, indem die blockierende Session die Transaktion mit COMMIT oder ROLLBACK beendet. Jede Sperre kann durch einen COMMIT oder ROLLBACK wieder aufgehoben werden.
Diese Sperrmethode wird automatisch von Oracle für DML Operationen verwendet.
Erlaubt anderen Transaktionen gleichzeitigen Zugriff auf die gesperrte Tabelle. Select, Update, Insert, Delete Operationen, sowie row share und row exclusive Sperren sind erlaubt.Wird versucht, auf eine im row exclusive Mode gesperrte Zeile eine weitere Schreiboperation (UPDATE) durchzuführen, funktioniert das jeweilige Statement erst, wenn die Sperre aufgehoben wird.Verhindert, dass andere Transaktionen die Tabelle für exklusiven Zugriff sperren und verhindert auch, dass die Tabelle im SHARE Mode gesperrt wird:
Erläuterung:Auch in diesem Beispiel wird klar, dass alle nicht erlaubten Sperrversuche einen Statuswechsel von BLOCKING_OTHERS auslösen.Aufgelöst wird diese Situation, indem die blockierende Session die Transaktion mit COMMIT oder ROLLBACK beendet. Jede Sperre kann durch einen COMMIT oder ROLLBACK wieder aufgehoben werden.
Diese Sperrmethode ist erforderlich, wenn ein Index auf einer Tabelle erstellt werden soll und wird automatisch von Oracle gesetzt, wenn ein CREATE INDEX Statement abgesetzt wird.
Erlaubt anderen Transaktionen gleichzeitige lesende Zugriffe auf die Tabelle. Auch eine andere Transaktion kann auf die gleiche Tabelle einen LOCK TABLE IN SHARE MODE absetzen. In einem solchen Fall kann keine der beiden Transaktionen Schreiboperationen auf der Tabelle durchführen. Schreiboperationen (siehe Beispiel update) warten bis die Sperre aufgehoben ist.Verhindert gleichzeitig Schreiboperationen auf Tabellenebene und Sperren in folgenden Modi:
Erläuterung:Aufgelöst wird diese Situation, indem die erste Session die Sperre mit COMMIT oder ROLLBACK beendet. Jede Sperre kann durch einen COMMIT oder ROLLBACK wieder aufgehoben werden.
Dieser Modus wird verwendet um eine komplette Tabelle unverändert zu lesen:
Erlaubt anderen Transaktionen die Tabelle zu lesen.Verhindert dass andere Transaktionen die Tabelle im SHARE Modus sperren, oder Änderungen durchführen dürfen:
Erläuterung:Aufgelöst wird diese Situation indem die erste Session die Sperre mit COMMIT oder ROLLBACK beendet. Jede Sperre kann durch einen COMMIT oder ROLLBACK wieder aufgehoben werden.
Das ist die restriktivste Methode eine Tabelle zu sperren. Dieser Sperrmodus erlaubt nur der eigenen Transaktion exklusiven Schreibzugriff auf die Tabelle.
Erlaubt anderen Transaktionen lesenden Zugriff auf die gesperrte Tabelle. Nur eine Transaktion kann eine exklusive Sperre auf eine Tabelle setzen. Weitere Schreiboperationen warten darauf, dass die Sperre aufgehoben wird.Verhindert jeglichen schreibenden Zugriff auf irgendeine Zeile durch andere Transaktionen, sowie manuelle Sperren in folgenden Modi:
An dieser Stelle kommt noch eine kurze Aufschlüsselung der Abkürzungen in der Spalte MODE_HELD der View DBA_DML_LOCKS. Die View DBA_DML_LOCKS wird von dem Skript ORACLE_HOME\rdbms\ADMIN\catblock.sql angelegt.
Dieser Monatstipp enthält nur einen kleinen Ausschnitt der in Oracle möglichen Sperren. Die unterschiedlichen Sperrmöglichkeiten werden u.a. in der View V$LOCK_TYPE gelistet. Dort findet sich auch eine kurze Beschreibung zu den jeweiligen Sperren.
Haben wir uns nicht alle schon mal darüber geärgert, dass auf NULL Spalten nur mit der folgenden Klausel abgefragt werden kann:
Seit Version 10g gibt es eine undokumentierte Funktion (und damit ist sie
leider auf Produktionsdatenbanken nur auf eigene Gefahr einsetzbar).
Die Funktion sys_op_map_nonnull kann zwei NULL Spalten vergleichen.
Beispiel: Alle Zeilen sollen ausgegeben werden, in denen die Spalte
mgr=comm= NULL ist:
Oder, alle Zeilen ausgeben, deren Spalte COMM = NULL ist:
Zum Vergleich: Die offizielle Klausel lautet:
Zum Löschen von doppelten Datensätzen gibt es eine ganze Reihe von Ansätzen. Die Suche nach der Schlagwort-Kombination duplicate delete und Oracle ergibt bei Google ca. 451.000 Treffer. Viele der Methoden funktionieren hervorragend bei den 10 bis 30 Beispieldatensätzen, die meist auch keine NULL-Werte enthalten. Wenn die Tabellen aber größer werden, stellt sich die Frage nach der performantesten Methode.
Dieser Monatstipp stellt die gängigsten Methoden am Beispiel einer manipulierten emp-Tabelle vor und vergleicht danach deren Performance anhand einer Tabelle mit 2 Mio. Datensätzen.
Vorbereitung der emp-Tabelle:Ein Datensatz wird verdoppelt, ein anderer verdreifacht, nur die Primärschlüssel bleiben unangetastet.
Auch hier gibt es mehrere Ansätze, der gebräuchlichste ist sicher:
Mit Spaltenliste ist hier die Kombination der Spalten gemeint, die keine Duplikate aufweisen soll.
Dieser alternative Select mit einer korrelierten Unterabfrage liefert die kompletten Datensätze der Duplikate, sofern man NULL-Spalten mit NVL entschärft (sonst wird hier z.B. das Duplikat von Smith nicht angezeigt)
Eine dritte, eher exotische Methode besteht darin, einen Unique-Constraint auf die Spaltenkombination zu setzen, die eindeutig sein soll und die Fehler (die Duplikate) in die von Oracle über das Skript utlexcpt.sql zur Verfügung gestellte Exceptions-Tabelle zu schreiben.
Dies ist sicher der bekannteste Ansatz:
In unserem Beispiel also:
Allgemeine Syntax:
Statt rowid < (SELECT MAX(rowid)... kann man natürlich auch rowid > (SELECT MIN(rowid)... oder rowid < ANY (SELECT rowid... verwenden
Nachteile:• Ohne die Behandlung von NULL-Spalten mit NVL erwischt man nur einen Teil der Datensätze !!!!• Korrelierte Update- und Delete-Statements sind als besonders unperformant berüchtigt• Bei Tabellen mit vielen Spalten wird das Statement sehr lang
Das Statement sieht bei der manipulierten emp-Tabelle dann so aus:
Von Tom Kyte empfohlen. Näheres zu analytischen Funktionen erfahren Sie in unserem SQL II Kurs.
Auch diesen Ansatz findet man in dem oben angeführten Artikel von Tom Kyte. Er ist besonders schnell, aber in Produktivumgebungen kaum umzusetzen. Man erstellt aus den gewünschten Daten eine neue Tabelle, löscht die alte, erstellt die Indizes neu und benennt die neue Tabelle um.
Für die Performance-Tests wurde eine Tabelle mit 2 Mio. Datensätzen auf Basis der dba_objects (Tom Kytes big_tab) im Schema Scott verwendet. Auch hier sind nur die Primärschlüssel noch unique.Duplikate ermitteln:
Löschen der Duplikate über eine nicht-korrelierte Unterabfrage
Löschen der Duplikate über eine korrelierte Unterabfrage.
Trotz der prinzipiell gleichen Hard- und Software-Ausstattung waren die Laufzeiten hier sehr unterschiedlich. 2 Server lagen reproduzierbar zwischen 16 und 18 Minuten, einer brachte es auf 5-6 Minuten.
Löschen der Duplikate über analytische Funktionen
Mit dem Release 11.2 hat Oracle eine Erweiterung des CREATE TABLE Statements eingeführt. Es handelt sich um die verzögerte Segmenterzeugung oder DEFERRED SEGMENT CREATION.
Was macht dieses neue Feature? Wird eine neue Tabelle erzeugt, werden nur die Metainformationen angelegt, aber noch kein Segment im Tablespace erzeugt. Das bedeutet, dass eine neue Tabelle erstmal keinen Platz "verschwendet". Solange die Tabelle leer ist, ändert sich auch daran nichts. Das Segment im Tablespace wird erst angelegt, wenn die ersten Daten in die Tabelle geschrieben werden. Das gleiche gilt auch für zugehörige Indizes und Lobsegmente.
Was bedeutet das für die Praxis? Es gibt viele große Softwareinstallationen die tausende von Tabellen anlegen, jedoch nur einen Teil davon in der jeweiligen Ausbaustufe benötigen. Bis jetzt wurde für jede Tabelle sofort das zugehörige Segment angelegt. Dabei kann unter Umständen viel Platz verschwendet werden, der so nicht benötigt wird.
Es gibt natürlich auch einen Nachteil den man bedenken sollte. Der erste Insert in eine leere Tabelle dauert länger, weil das Segment angelegt werden muss.
Doch nun zur Praxis. Welche Voraussetzungen sind nötig um die verzögerte Speicherplatzbelegung zu nutzen?
Die Syntax der CREATE TABLE Anweisung hat sich um die SEGMENT CREATION Klausel erweitert.
Das Verhalten der SEGMENT CREATION Klausel ist abhängig von dem Parameter DEFFERED_SEGMENT_CREATION. Steht der Parameter auf TRUE (default), dann werden die Segmente einer neuen Tabelle automatisch verzögert angelegt. Steht der Parameter auf FALSE, werden die Segmente sofort angelegt. Dieses Verhalten kann durch Angabe der Klausel jedoch überschrieben werden.
Nun einige Beispiele zur Verdeutlichung des Verhaltens:
In diesem Beispiel wird deutlich, dass die Tabelle existiert, aber bis jetzt noch kein Speicherplatz allokiert wurde.
Im nächsten Beispiel überschreiben wir das Defaultverhalten mit der SEGMENT DEFERRED Klausel:
Mit der Klausel SEGMENT CREATION IMMEDIATE wird sofort bei der Erstellung der Tabelle auch je ein Segment für Tabelle und Index angelegt.
Und jetzt zu den ersten Inserts:
Wie erwartet dauert der erste Insert länger, weil die Segmente für Tabelle und Index erst angelegt werden müssen. Und hier noch der Beweis, dass die Segmente angelegt wurden:
Es gibt leider in diesem Release einige Objekte auf die das neue Feature nicht angewendet werden kann:
Wenn eine Tabelle einmal gefüllt war, bleibt das Segment bestehen, auch wenn ein Truncate oder Alter Table Kommando abesetzt wurde.
Letztendlich bietet diese neue Art der Speicherplatzverwaltung Vorteile, vor allem wenn es sich um große Softwareinstallationen handelt, weil kein unnötiger Platz im Tablespace verschwendet wird.
Mehr Informationen zu den neuen Features von Oracle erhalten Sie auch in unserer Schulungen.
Viele wollen SQL lernen, sind aber abgeschreckt vor dem Aufwand, sich etwas zu installieren. Eigentlich ist das gar nicht schlimm, siehe auch unser Tipp zur Installation von Oracle XE 18c. Wer es aber noch einfacher haben möchte, geht in die Cloud. Oracle bietet einen kostenlosen Zugriff auf einen Server an, wo auch SQL gelernt werden kann. Der nachfolgende Tipp soll das Vorgehen kurz erläutern.
1. Man besucht die Webseite https://livesql.oracle.com und klickt dann auf "Sign In".
2. Dann wird man auf die Seite zum Registrieren weitergeleitet.
3. Nun gibt man die geforderten Daten ein und klickt dann auf "Account erstellen".
4. Dann wird einem die Bestätigungsmail zugeschickt, diese muss vor dem Nutzen des Accounts bestätigt werden.
5. Danach meldet man sich mit den eingegebenen Daten auf der Webseite an.
6. In der Suchleiste kann man nach "Skripten und Tutorials" suchen.
7. Klickt man auf "View Scripts and Tutorials" findet man die Code Library (Code Bibliothek).
8. Drückt man auf "Start Coding Now" gelangt man zum SQL Worksheet (Arbeitsblatt), wo man Commands (Befehle) eingeben kann.
9. Klickt man auf "My Session" kann man seine SQL Statements abrufen.
10. Drückt man auf "Schema" findet man ein einfaches Schema zum Üben von SQL.
11. Unter "Design" kann man ein Schema schnell entwerfen.
12. Klickt man auf "My Scripts" kann man alle Skripte und Sessions abrufen, die gespeichert sind.
13. Der letzte Punkt ist die "Code Library" (Code Bibliothek), dort findet man Beispiele und Tutorials.
Wir wünschen viel Spaß bei der Benutzung und viel Erfolg beim Üben. In einem unserer Kurse, kann man das gelernte dann gerne vertiefen.
Ist es Ihnen schon häufiger passiert, dass Sie Änderungen an Ihren Daten vornehmen wollten und dabei in bestehende Tabellen- oder Zeilensperren gelaufen sind? Im unangenehmsten Fall „hängte“ sich Ihre Session solange auf, bis die gesperrten Zeilen wieder freigegeben wurden. Und selbst wenn Sie vor dem Durchführen Ihrer Änderung festgestellt haben, dass eine bereits vorhandene Sperre die Manipulation verhindert, hatte es Sie sehr viel Aufwand gekostet, die „störende“ Session auszumachen.
Der folgende Beitrag soll Ihnen beim Auffinden von Sperren behilflich sein und die Zuordnung der Sperren zu den jeweiligen Transaktionen, den Benutzern und den gesperrten Objekten erleichtern.
Zu Demonstrationszwecken werden die Benutzer in diesem Beitrag mit TEST01, TEST02, ... bezeichnet. Die Benutzer greifen alle auf die selben Tabellen EMP und DEPT zu und versuchen diese zu manipulieren.
Fall 1: (Default-Einstellung bei Oracle): User TEST01 sperrt durch seine Transaktion einzelne Zeilen einer Tabelle und User TEST02 läuft mit seiner Änderung in die Sperre hinein. Die Session von TEST02 ist blockiert und der User muss warten, bis die Transaktion des Users TEST01 beendet ist.
Fall 2: (SELECT ... FOR UPDATE NOWAIT): User TEST01 startet erneut eine Transaktion:
TEST02 möchte parallel den Namen der Abteilung 40 ändern, prüft allerdings vorher, ob es bereits eine Sperre auf diesem Datensatz gibt:
Und erhält folgende Fehlermeldung:
Nun weiß TEST02 zwar, dass er momentan nicht in der Lage ist, diesen Datensatz zu verändern, er weiß jedoch nicht, welcher Benutzer ihn in seiner Tätigkeit behindert. Dies gilt es im weiteren Verlauf des Beitrags zu ermitteln.
Zum Aufruf der folgenden Views benötigen Sie mindestens das Leserecht aller Data Dictionary Views (z. B. über die Rolle SELECT_CATALOG_ROLE).
Ermittlung der blockierenden Sessions
Der folgende SELECT ermittelt die blockierende Session, in deren Sperre Sie gelaufen sind.
Zurück zu Fall 2 unseres Ausgangsproblems. TEST02 hat mittels SELECT ... FOR UPDATE NOWAIT festgestellt, dass der zu ändernde Datensatz bereits gesperrt ist. Um festzustellen, welcher Benutzer für die Sperre verantwortlich ist, setzt er nun folgenden Befehl ab:
Dieses Beispiel ist für den Beitrag etwas idealisiert worden. Im Normalfall sehen Sie nicht zwingend, welche Session genau Ihre zu ändernde Zeile sperrt. Die Spalte SQL_TEXT gibt lediglich den zuletzt abgesetzten Befehl eines Benutzers an, dies muss aber nicht unbedingt der Befehl sein, mit dem Ihre Zeile(n) gesperrt worden ist. Haben mehrere Benutzer unterschiedliche Zeilen einer Tabelle gesperrt und Sie wollen feststellen, welche Session Sie blockiert, müssen Sie wohl oder übel in die Sperre laufen und über den zuvor aufgeführten SELECT die blockierende Session ausfindig machen.
Den Abschluss dieses Beitrags bildet ein SELECT, der Ihnen alle Benutzersessions auflistet, die eine Transaktion gestartet haben bzw. in eine Transaktionssperre gelaufen sind. Im konkreten Fall könnten dies mehrere Benutzer sein, die hintereinander in die selbe Zeilensperre laufen und damit alle blockiert sind. Nun gilt es herauszufinden, in welcher Reihenfolge die „hängengebliebenen“ Sessions wieder freigegeben werden. Als zusätzliche Information lassen Sie sich dazu die Zeit (LAST_CALL_ET), die seit dem Absetzen des letzten Befehls in einer Session verstrichen ist ausgeben.
Als Ausgabe erhalten Sie eine Übersicht über alle gestarteten Transaktionen (TADDR) und welche Session in eine bereits bestehende Sperre (LOCKWAIT) einer anderen Session (HOLDING_SESSION) gelaufen ist.
Fazit
Die in diesem Beitrag angesprochenen Data Dictionary Views sind lediglich die wichtigsten rund um das Thema Sessions, Transaktionen und Sperren. Mit Hilfe der hier aufgeführten Befehls-Beispiele soll es Ihnen möglich sein, sich die gewünschten Informationen übersichtlich anzeigen zu lassen.
In unserem heutigen Tipp widmen wir uns dem wichtigsten Konzept einer Datenbank überhaupt: Der Tabelle!
Nachdem ich immer wieder nach Beispielen zu speziellen CREATE TABLE Befehlen für Oracle Datenbanken suchen muss, war es mal an der Zeit eine eigene Zusammenfassung zu erstellen.
Fall 1: Eine Tabelle soll mit den gängigsten numerischen Datentypen erstellt werden:
Fall 2: Als nächstes nehmen wir die text und sonstigen Datentypen:
Fall 3: Die Tabelle soll mit der neuen LOB Speichertechnik (ab 11.1), den Securefiles, erstellt werden:
Fall 4: Die Tabelle soll erst dann physisch angelegt werden, wenn die erste Zeile eingetragen wurde:
Fall 5: Die Tabelle soll mit dem Attribut NOLOGGING erstellt werden:
Nachträglich kann die Tabelle wieder in LOGGING bzw. NOLOGGING geändert werden:
Fall 6: Sie wollen einen speziellen Tablespace für die Tabelle angeben:
Fall 7: Die Tabelle soll einen Primärschlüssel besitzen:
Fall 8: Die Tabelle soll einen Fremdschlüssel (Foreign Key) besitzen:
Fall 9: Die Tabelle soll eine spezielle Sortierung für eine Spalte eingestellt bekommen. Diese Funktion steht erst ab 12.2 zur Verfügung. Außerdem muss der Parameter max_string_size= EXTENDED gesetzt sein.
EXTENDED
Fall 10: Die Tabelle soll nur die Daten temporär aufbewahren (bis zum Sessionende):
Fall 11: Die Tabelle soll nur die Daten temporär aufbewahren (bis zum Transaktionsende):
Fall 12: Ab Version 18 kann eine temporäre Tabelle erzeugt werden, die zum Transaktionsende samt Definition gelöscht wird.Hinweis: Sie muss ein Prefix (ora$ptt) im Namen aufweisen. Dies kann jedoch über den Initialisierungsparameter PRIVATE_TEMP_TABLE_PREFIX geändert werden.
PRIVATE_TEMP_TABLE_PREFIX
Fall 13: Auch erst ab Version 18 verfügbar, ist die zweite Variante der privaten temporären Tabelle, die erst zum Sessionende gelöscht wird:
Fall 14: Die Tabelle soll sich die Änderungs-SCN pro Zeile und nicht nur pro Block merken:
Fall 15: Die Tabelle soll auf einer Datei basieren, die ausserhalb der Datenbank liegt:
Als Benutzer mit DBA Rechten ausführen:
Als Benutzer SCOTT ausführen:
Fall 16: Blockchain Table (Erst ab Version 21c)
Fall 17: Interval Range-Partitionierte Tabelle (1 Monatsintervall)
Fall 18: Interval Range-Partitionierte Tabelle (1 Tagesintervall)
Fall 19: Interval Range-Partitionierte Tabelle (1 Jahresintervall)
Fall 20: Range Partitioninierung mit eigenem Intervall
Die WITH-Klausel ist ja eines der bestgehütesten Geheimnisse der Sprache SQL. In unseren Schulungen ernten wir immer wieder Erstaunen, wenn wir die Funktion vorstellen. Dabei ist sie schon seit einigen Oracle Versionen verfügbar.
Richtig spannend wurde es jedoch erst ab Version 12.x, doch dazu später mehr.
Schauen wir uns erst einmal die einfachste WITH-Klausel an:
Die WITH-Klausel ermöglicht uns, eine Ergebnismenge vorab zu aggregieren, um sie dann evtl. sogar mehrmals in einem SELECT wieder einzusetzen.
Hier wird die Query unter dem Namen "S" in Form einer Tabelle zur Verfügung gestellt. Nur hat die Ergebnismenge im ersten Fall halt nur eine Spalte und eine Zeile.
Ein Beispiel mit der EMP Tabelle darf natürlich nicht fehlen:
Ab Version 12.x können nun auch Proceduren und Funktionen in der WITH-Klausel verwendet werden. Erstaunlicherweise muss man dazu noch nicht mal ein "CREATE PROCEDURE" Recht besitzen. Ein Schelm, wer böses damit anstellt.
Manchmal möchte man auch einen Aufruf in SQL starten, der eigentlich nur in PL/SQL funktioniert,das ist mit der WITH-Klausel kein Problem.
Beispiel: Sie haben eine Fehlernummer von Oracle erhalten, aber nicht den Text. Der Versuch in SQL:
Mit der WITH-Klausel:
Oder mal eine komfortablere Datumskonvertierungsfunktion ...
Das geht auch mit Datumswerten wie: '01.01.01', '01-Mai.99', '30/DeC.2001', '02-jan:1932'. Das versuchen Sie mal mit der to_date Funktion ...
Auch mehrere WITH-Klauseln sind möglich:
Sie müssen sie nur mit einem Komma trennen.
Zum Schluss noch ein etwas anspruchsvolleres Beispiel, das wir gerne in den Kursen zeigen:Es soll ein Skript angelegt werden, das einen Benutzer mit all seinen zugewiesenen Rechten anzeigt.
Der Vorteil hier ist, dass man den Schemanamen in der WITH-Klausel, quasi wie eine Variable definieren kann, die man dann an mehreren Stellen weiter unten im SQL Statement wieder verwenden kann.
Nur kann es passieren, dass einer der Teil-Selects keine Ergebnismenge zurückbringt. Dieser ist dann so nett, mit dem Fehler ORA-31608 abzustürzen. Damit stürzt dann aber auch gleich das komplette SQL-Statement ab. Deswegen haben wir oben mit der COUNT Funktion geprüft, ob überhaupt Rechte vorhanden sind.
Ab 12c können wir aber auch das schöner lösen:
oder wollten Sie immer schon mal viele DDL befehle in einem Rutsch ausführen?Achtung die folgende Funktion löscht sofort alle invaliden Synonyme der Benutzer MDSYS und CTXSYS:
Na, auf den Geschmack gekommen? Weitere Beispiele erhalten Sie z.B. in unserem PL/SQL Kurs. Besuchen Sie uns doch mal im schönen Unterhaching bei München.
Wenn man eine range intervall partitionierte Tabelle anlegt, vergibt Oracle nicht sehr besonders sprechende Namen.So heißen Partitionen schon mal SYS_P140, SYS_P141, ...Dass muss aber nicht so bleiben, sie können natürlich jede Partitiion umbenennen mittels:
Was aber für 300 Partitionen sehr mühsam ist. warum also nicht das ganze etwas automatisieren?Die Informationen, welche Partition welchen Bereich abdeckt steht in der Spalte high_value der View USER_TAB_PARTITIONS.Wenn die Tabelle nicht Ihnen gehört, Sie aber Rechte darauf besitzen, können Sie in ALL_TAB_PARTITIONS nachsehen.Nur ist das Problem, das es sich bei dieser Spalte um eine Long Spalte handelt, die nicht normal gefiltert werden kann. Deswegen gehen wir einen kleinen Umweg via XMLum aus der Tabelle einen Teil herausschneiden zu können. Wir haben uns in diesem Beispiel auf Range Partitionierung nach dem Datum konzentriert, was in der Praxis wohl am häufigsten vorkommt.Es ist auch leicht auf andere Range Datentypen anpassbar.
Diese Ausgabe erzeugt nun die SQL Befehle, die Sie dann nur noch ausführen müssen.PS: Wenn Sie Beratung zum Thema Oracle Partitionierung, melden Sie sich doch einfach bei uns. Wir bieten Schulungen und Consulting zu vielen Oracle Bereichen...
Haben Sie auch schon einmal verzweifelt nach der richtigen Oracle Dokumentation Ihrer Oracle Datenbank gesucht.Leider hat Oracle im Gegensatz zu Postgres keinen einheitlichen Doc Server, sondern legt die Dokumenation immer auf einen neuen Server...Die folgende Matrix soll Ihnen die Suche nach der richtigen Version für Ihre Oracle Datenbank helfen
Und für alle Oracle APEX Fans:
Nun kommen Sie bei Ihrer Doku-Suche schneller zum Ziel. Wer die Dokumentation nicht so gerne liest, kann natürlich auch gerne in eine von unseren Schulungen kommen.
In den letzten Jahren wird von vielen Entwicklern verstärkt das Tool Visual Studio Code bei der Entwicklung verwendet.Wir haben das Tool in Verbindung mit Oracle SQL und PL/SQL mal näher angesehen und beschreiben in diesem Tipp die EinrichtungNetterweise gab es zu dem Thema schon einen Blog von Morten Braten, der aber noch die Version 0.1.0 verwendet, aktuelle Version ist 2.0.0 (Stand Juni 2019). Wir haben ein paar Sachen ergänzt und korrigiert, damit es unter der aktuellen Visual Studio Code Version läuftSie können Visual Studio Code hier herunterladen.Zusätzlich sollten Sie folgende Extensions installieren:
Extensions werden installiert durch Klick auf das zweite Icon von unten, links im Hauptfenster (Screenshot (1)). Danach im Bereich "Search..." den gesuchten Namen eintragen, Suche starten und bei der gewünschten Extension auf "Install" klicken.Danach legen Sie bitte einen Ordner im Betriebssystem an (z.B. c:\temp\VC)Gehen Sie nun auf File/Open Folder und wählen Sie den Ordner aus. Der Inhalt des Ordners wird Ihnen links (Screenshot (2)) angezeigt.Wenn Sie auf eine Datei (links) klicken, wird der Inhalt im Fenster (Screenshot (3)) angezeigt.Nun müssen wir einige Konfigdateien anlegen, die Visual Studo Code sagen, was zu tun ist.
Datei 1: _run_sqlplus.bat
Datei 2: _show_errors.sql
und die wichtigste, Datei 3: task.json im Unterordner .vscode
Passen Sie bitte in dieser Datei in der Zeile "args" ihre Daten bzgl. Benutzername/Passwort und Datenbank Daten an.Nun können wir loslegen:Wir öffnen den Ordner (File/Open Folder), bei uns war das der Ordner c:\temp\VCIn diesen Ordner legen Sie eine bereits existierende SQL bzw. PL/SQL Datei (in unserem Screenshot(2)) z.B: die Datei vc_test.sqlWenn Sie eine neue Datei erstellen möchten, klicken Sie mit der rechten Maustaste in den Dateibereich, Menue: New File. Geben Sie einen Namen an, dann wird die Datei im Ordner angelegt.Klicken Sie also auf die gewünschte Datei, die dann im rechten Fenster angezeigt wird.Ändern Sie ggf noch etwas ab und speichern Sie Datei (z.B. durch STRG-S)Drücken Sie nun STRG-Shift-B (für Build) und ein kleines Fenster öffnet sich oben.Wählen Sie dort ./_run_sqlplus.bat aus und die Datei wird in Oracle SQL*Plus ausgeführt.Die Ausgabe wird im Reiter "TERMINAL" unten ausgegeben (Screenshot(4)). Sollte in der Select Liste dort nicht _run_sqlplus stehen, dann bitte ändern.Mit dem Mülleimer Symbol (Screenshot(5)) kann das Fenster wieder geleert werden.Mit Klick auf das Dateiensymbol (Screenshot(6)) werden die aktuellen Dateien im Ordner angezeigt, ein nochmaliger Klick öffnet den Code als Vollbild.Wer es etwas kürzer mag (aber unflexibler), kann als tasks.json Datei auch die folgende verwenden:
Dann kann man sich auch die Dateien _run_sqlplus.bat und _show_errors.sql sparen hat dann aber
Weitere tolle Tipps erfahren Sie in einem unserer vielen Entwicklungskurse...
INFO
Fügt neue Zeilen in eine Tabelle hinzu Syntax:
INSERT INTO tabelle [(spalte, ...)] VALUES (value, ...);
INSERT INTO emp (empno, ename, job, deptno) VALUES (8000,'HUBER','TRAINEE',40); INSERT INTO dept VALUES (50, 'HEAD_QUARTER', null);
Kopiert Zeilen aus einer anderen Tabelle Syntax:
INSERT INTO tabelle [(spalte ...)] unterabfrage
INSERT INTO emp_copy SELECT * FROM emp WHERE job = 'MANAGER';
Syntax:
UPDATE tabelle SET spalte = value ändert vorhandene Zeilen [WHERE condition] ändert bestimmte Zeilen
UPDATE emp SET sal = 2000 WHERE deptno = 10; UPDATE emp SET sal = 1111, comm = 1000, deptno = 50 WHERE ename = 'HUBER'; UPDATE emp SET (deptno, sal, comm) = (SELECT deptno, sal, comm FROM emp WHERE empno = 7499) WHERE ename = 'MILLER';
Löscht Zeilen aus einer Tabelle Syntax:
DELETE FROM tabelle [WHERE condition]
DELETE FROM emp WHERE job = 'TRAINEE'; DELETE FROM emp WHERE deptno = (SELECT MIN(deptno)FROM emp);
Kombination von INSERT- und UPDATE-Anweisungen (ab 9i) Syntax:
MERGE INTO tabelle USING tabelle ON (condition) WHEN MATCHED THEN UPDATE SET (condition) WHEN NOT MATCHED THEN INSERT (condition)
MERGE INTO annsal a USING emp e ON (a.empno = e.empno) WHEN MATCHED THEN UPDATE SET a.peryear = (e.sal*12) WHEN NOT MATCHED THEN INSERT (a.empno, a.peryear) VALUES (e.empno, e.sal*12);
COMMIT; → Datenänderungen werden permanent festgeschrieben ROLLBACK; → alle Änderungen werden zurückgesetzt ROLLBACK TO SAVEPOINT test; → alle vorgenommenen Änderungen werden bis zum SAVEPOINT zurückgesetzt
SET TRANSACTION READ COMMITTED; → (Standardeinstellung) SET TRANSACTION READ ONLY; → erlaubt nur Lesen der Daten, kein Ändern
Erstellt Tabelle mit Spalten und -attributen Syntax:
CREATE TABLE tabellenname (spalte type(n), ...)
CREATE TABLE kollegen ( k_nr number(3) NOT NULL, nname varchar2(20), ort varchar2(20) DEFAULT 'MÜNCHEN');
Erstellt Tabelle mittels Unterabfrage aus anderer Tabelle Syntax:
CREATE TABLE tabellenname AS subquery
CREATE TABLE dept10 AS SELECT empno, ename, sal*12 j_gehalt, hiredate FROM emp WHERE deptno = 10;
Fügt eine neue Spalte in eine Tabelle Syntax:
ALTER TABLE tabellenname ADD (spalte type(n), ...)
ALTER TABLE kollegen ADD (email char(30));
Ändert eine vorhandene Spalte Syntax:
ALTER TABLE tabellenname Modify (spalte type(n), ...)
ALTER TABLE kollegen MODIFY (email varchar2(35));
Löscht eine Spalte aus einer Tabelle Syntax:
DROP COLUMN spalte
ALTER TABLE emp DROP COLUMN job; ALTER TABLE dept DROP COLUMN deptno CASCADE CONSTRAINTS;
Löscht die gesamte Tabelle unwiderruflich Syntax:
DROP TABLE tabellenname;
DROP TABLE kollegen; DROP TABLE dept CASCADE CONSTRAINTS;
Benennt eine Tabelle um Syntax:
RENAME table TO table_neu
RENAME kollegen TO ex_kollegen;
TRUNCATE TABLE tabellenname
TRUNCATE TABLE ex_kollegen;
Bettet Unterabfrage in die Anweisung 'CREATE VIEW' mit ein Syntax:
CREATE VIEW viewname AS subquery
CREATE OR REPLACE VIEW empvu20 (nr, nname, beruf) AS SELECT empno, ename, job FROM emp WHERE deptno = 20 WITH CHECK OPTION;
Löscht eine View Syntax:
DROP VIEW viewname
DROP VIEW empvu20;
Fügt Daten einer View hinzu Syntax:
INSERT INTO view [(spalte, ...)] VALUES (value, ...)
INSERT INTO emp_view (empno, ename, deptno) VALUES (9000,'HUGO',20);
Benutzen einer Inline View als Datenquelle Syntax:
SELECT spalte, spalte FROM tabelle alias, (inline view) alias bedingung
SELECT a.ename, a.sal, a.deptno, b.maxsal FROM emp a, (SELECT deptno, max(sal) maxsal FROM emp GROUP BY deptno) b WHERE a.deptno = b.deptno AND a.sal<b.maxsal;
Top-N-Analysen ermitteln die größten bzw. kleinsten Werte einer Spalte Syntax:
SELECT rownum alias, spalte, ... FROM (inline view ORDER BY spalte) bedingung
SELECT rownum rang, ename, sal FROM (SELECT ename, sal FROM emp ORDER BY sal desc) WHERE rownum< = 5;
Stellt sicher, dass keine Null-Werte in die Spalte eingetragen werden können Beispiel:
CREATE TABLE t ( name varchar2(50) CONSTRAINT cons1 NOT NULL, vname varchar2(20)); ALTER TABLE t MODIFY (vname NOT NULL);
Setzt voraus, dass jeder Wert einer Spalte eindeutig ist Beispiel:
CREATE TABLE kunden ( nr number, sub_nr number, name varchar2(50), CONSTRAINT kdn_nr_uk UNIQUE (nr,sub_nr)); ALTER TABLE kunden ADD CONSTRAINT kunden_uk UNIQUE (nr);
UNIQUE KEY + NOT NULL Beispiel:
CREATE TABLE kunden2 ( nr number, sub_nr number, CONSTRAINT kunden_pk PRIMARY KEY (nr,sub_nr)); ALTER TABLE kunden2 ADD CONSTRAINT kunden_pk PRIMARY KEY (nr);
Legt Beziehung zu Primärschlüssel in derselben oder andererTabelle fest Beispiel:
CREATE TABLE rechnung ( kd_nr number(2) CONSTRAINT kunden_fk REFERENCES kunden(kd_nr) ON DELETE cascade); ALTER TABLE emp ADD CONSTRAINT emp_deptno_fk FOREIGN KEY (deptno) REFERENCES dept(deptno);
Definiert eine Beziehung, die jede Zeile erfüllen muss Beispiel:
CREATE TABLE kunden3 ( kd_nr number CONSTRAINT kunden_ch CHECK (kd_nr>0 AND kd_nr<1000); ALTER TABLE kunden4 ADD CONSTRAINT kunden_geschlecht CHECK (geschlecht IN ('M','W')));
Löscht bestehende Constraints Syntax: ALTER TABLE tabelle DROP PRIMARY KEY | UNIQUE (spalte) CONSTRAINT constraint [cascade] Beispiel:
ALTER TABLE kunden3 DROP CONSTRAINT kunden_ch;
Sequenzen, Indizes und Synonyme
Legt eine Sequenz an Syntax: CREATE SEQUENCE name [optionen] Beispiel:
CREATE SEQUENCE emp_empno INCREMENT BY 10 START WITH 2500 MAXVALUE 9990;
Verwendet beim Einfügen neuer Daten eine Sequenz Syntax: INSERT INTO tabelle [(spalte, ...)] VALUES (name.NEXTVAL, value,...) Beispiel:
INSERT INTO emp (empno, ename, job, sal) VALUES (emp_empno.NEXTVAL,'HUBER','CLERK',2500);
Legt einen Index auf Spalte einer Tabelle an Syntax: CREATE [UNIQUE] INDEX name ON tabelle (spalte) [tablespace idx_tbs] Beispiel:
CREATE UNIQUE INDEX dept_deptno_idx ON dept (deptno) TABLESPACE indx;
Legt ein Synonym für ein DB-Objekt an Syntax: CREATE [PUBLIC] SYNONYM name FOR object_name Beispiel:
CREATE PUBLIC SYNONYM emp FOR scott.emp;
Legt einen neuen Benutzer mit Passwort an Syntax: CREATE USER user_name IDENTIFIED BY password [optionen]; Beispiel:
CREATE USER hugo IDENTIFIED BY lion DEFAULT TABLESPACE users TEMPORARY TABLESPACE temp QUOTA 1G ON users;
Ändert das Passwort eines Benutzers Syntax: ALTER USER username IDENTIFIED BY password Beispiel:
ALTER USER hugo IDENTIFIED BY cat;
Gewährt Zugriffsrechte auf die DB und darin enthaltene Strukturen Syntax: GRANT objekt_priv [(spalten)] ON objekt TO {user|role|public} [WITH GRANT OPTION] Beispiel:
GRANT CREATE SESSION, CREATE TABLE TO huber; GRANT CONNECT, RESOURCE TO hugo; GRANT SELECT, INSERT ON freunde TO hugo, heidi WITH GRANT OPTION; GRANT UPDATE (job, mgr, deptno) ON emp TO buchhaltung;
Entzieht Zugriffsrechte Syntax: REVOKE {privileg, ...|all} ON objekt FROM {user, ...|role|public} [cascade constraints] Beispiel:
REVOKE SELECT, INSERT ON freunde FROM hugo;
DESC table zeigt Tabellenstruktur an DESC emp
COL spalte ändert SpaltenformatFORMAT [a]... COL ename FORMAT a20 COL sal FORMAT 99,999.99
CONNECT user/pwd stellt Verbindung zu einem anderen Benutzer her CONNECT system/manager
DEFINE zeigt definierte Variablen an DEFINE
SET parameter ändert Parameter für die Sitzung SET lines 1000 pages 100 head offSHOW parameter zeigt Parameter der laufenden Sitzung an SHOW linesize SHOW all
EDIT [dateiname] ruft Editor auf (Standard: afiedt.buf) ED ED c:\oracle\ora81\rdbms\admin\scott.sqlEXIT beendet SQL*Plus EXIT GET dateiname schreibt / holt Inhalt einer gespeicherten Datei in SQL-Puffer GET c:\oracle\ora81\bin\date
SAVE dateiname speichert aktuellen Inhalt des SQL-Puffers in Datei SAVE date SAVE c:\temp\test.sqlSPOOL dateiname speichert Abfrageergebnisse in einer Datei SPOOL c:\temp\sicherung.lst
START dateiname führt gespeicherte Befehlsdatei aus START c:\oracle\ora81\bin\date@ dateiname führt gespeicherte Befehlsdatei aus è @ date
CL[EAR] option nimmt Löschoperation vor CL SCR
L[IST] [n] [m n] &
Wer kennt es nicht, immer wenn man mal listagg benötigt, reciht der Speicherplatz nicht, weil listagg maximal 32767 Bytes zurückgeben kann.Mit einem kleinen Trick klappt es auch mit CLOB als Rückggaabetyp. Dazu verwenden wir die XML Funktion XMLELEMENT.Keine Angst, sie müssen kein XML dazu beherschen:-)Wir haben das ganze in eine WITH Funktion gekappselt, für den Fall, dass Sie kein CREATE PROCEDURE RECHT haben.Die Funktion hat bis zu 3 Parameter,column_name ist der Name der Spalte, die zusammengefasst werden solltable_name ist der Name der Tabelle, in der die Spalte enthalten istwhere_cond ist Optional und beinhaltet eine Filterklausel Default 1=1 also alles)order_by Optional kann auch ein Sortierreihenfolge bei der Ausgabe definiert werden (Default: Sortiert nach der Ausgabespalte)delimiter ist auch Optional und gibt das Trennzeichen zwischen der Ausgabe der Spaltenwerte an (Default ',' )
Wenn Sie das Ganze als eigenständige Funktion anlegen möchten:
Testcases:
Damit können Sie nun (fast) unbegrenzt die Werte einer Spalte zusammenfassen und weiterverarbeiten.