Oracle APEX: WWV_FLOW_FND_USER_FK-Verletzung beim Importieren von Workspaces

Ich bin jüngst im Rahmen eines APEX-Projekts auf ein Problem gestoßen, für das ich im Oracle Support Portal keinen einzigen Eintrag finden konnte – nach einigem Überlegen und Testen aber auf eine relativ einfache Lösung gekommen bin.

Ausgangslage

Das erste Arbeitspaket im Rahmen des Projektes ist fertig entwickelt und soll dem Kunden zur Abnahme in der dafür von diesem bereitgestellten Test-Umgebung deployed werden. Da es in dieser Umgebung keine APEX-Entwicklungs-Umgebung gibt, sind die Sourcen per SQLplus, PL/SQL-Developer oder mit ähnlichen Tools zu deployen.

Nach der fehlerfreien Erstellung von Schemas, Tabellen, Packages etc. folgt der Import des APEX-Workspaces; das aus APEX exportierte Script hierfür schaut sinngemäß wie folgt aus:

set define off verify off feedback off
whenever sqlerror exit sql.sqlcode rollback
--------------------------------------------------------------------------------
--
-- ORACLE Application Express (APEX) export file
--
-- You should run the script connected to SQL*Plus as the Oracle user
-- APEX_050100 or as the owner (parsing schema) of the application.
--
-- NOTE: Calls to apex_application_install override the defaults below.
--
--------------------------------------------------------------------------------
begin
wwv_flow_api.import_begin (
p_version_yyyy_mm_dd=>'[DATE]'
,p_default_workspace_id=>276...332
);
end;
/
prompt WORKSPACE 276...332
--
-- Workspace, User Group, User, and Team Development Export:
-- Date and Time: [DATE_AN_TIME]
-- Exported By: ADMIN
-- Export Type: Workspace Export
-- Version: 5.1.2.00.09
-- Instance ID: 714...698
--
-- Import:
-- Using Instance Administration / Manage Workspaces
-- or
-- Using SQL*Plus as the Oracle user APEX_050100

begin
wwv_flow_api.set_security_group_id(p_security_group_id=>276...332);
end;
/
----------------
-- W O R K S P A C E
-- Creating a workspace will not create database schemas or objects.
-- This API creates only the meta data for this APEX workspace
prompt Creating workspace LEHM...
begin
wwv_flow_fnd_user_api.create_company (
p_id => 276...354
,p_provisioning_company_id => 276...332
,p_short_name => '[SHORT_NAME]'
,p_display_name => '[DISPLAY_NAME]'
,p_first_schema_provisioned => '[SCHEMA_NAME]'
,p_company_schemas => '[SCHEMA_NAMES]'
,p_account_status => 'ASSIGNED'
,p_allow_plsql_editing => 'Y'
,p_allow_app_building_yn => 'Y'
,p_allow_packaged_app_ins_yn => 'Y'
,p_allow_sql_workshop_yn => 'Y'
,p_allow_websheet_dev_yn => 'Y'
,p_allow_team_development_yn => 'Y'
,p_allow_to_be_purged_yn => 'Y'
,p_allow_restful_services_yn => 'Y'
,p_source_identifier => '[IDENTIFIER]'
,p_path_prefix => '[PATH_PREFIX]'
,p_files_version => 1
);
end;
/
----------------
-- G R O U P S
--
prompt Creating Groups...
begin
wwv_flow_api.create_user_groups (
p_id => 927...937,
p_GROUP_NAME => 'OAuth2 Client Developer',
p_SECURITY_GROUP_ID => [ID],
p_GROUP_DESC => 'Users authorized to register OAuth2 Client Applications');
end;
/
begin
wwv_flow_api.create_user_groups (
p_id => 927...936,
p_GROUP_NAME => 'RESTful Services',
p_SECURITY_GROUP_ID => [ID],
p_GROUP_DESC => 'Users authorized to use RESTful Services with this workspace');
end;
/
begin
wwv_flow_api.create_user_groups (
p_id => 927...912,
p_GROUP_NAME => 'SQL Developer',
p_SECURITY_GROUP_ID => [ID],
p_GROUP_DESC => 'Users authorized to use SQL Developer with this workspace');
end;
/
prompt Creating group grants...
----------------
-- U S E R S
-- User repository for use with APEX cookie-based authentication.
--
prompt Creating Users...
begin
wwv_flow_fnd_user_api.create_fnd_user (
p_user_id => '276...332',
p_user_name => 'ADMIN',
p_first_name => '',
p_last_name => '',
p_description => '',
p_email_address => '[MAIL_ADDRESS]',
p_web_password => '[PASSWORD_STRING]',
p_web_password_format => '[PASSWORD_FORMAT]',
p_group_ids => '',
p_developer_privs => 'ADMIN:CREATE:DATA_LOADER:EDIT:HELP:MONITOR:SQL',
p_default_schema => '[SCHEMA_NAME]',
p_account_locked => 'N',
p_account_expiry => to_date('[DATE_AND_TIME]','YYYYMMDDHH24MI'),
p_failed_access_attempts => 0,
p_change_password_on_first_use => 'Y',
p_first_password_use_occurred => 'Y',
p_allow_app_building_yn => 'Y',
p_allow_sql_workshop_yn => 'Y',
p_allow_websheet_dev_yn => 'Y',
p_allow_team_development_yn => 'Y',
p_allow_access_to_schemas => '');
end;
/
[MORE_USER_CREATION_STEPS]
prompt Check Compatibility...
begin
-- This date identifies the minimum version required to import this file.
wwv_flow_team_api.check_version(p_version_yyyy_mm_dd=>'[DATE]');
end;
/

begin wwv_flow.g_import_in_progress := true; wwv_flow.g_user := USER; end; 
/
[SOME_EMPTY_ANONYMOUS_BLOCKS]
begin
wwv_flow_api.import_end(p_auto_install_sup_obj => nvl(wwv_flow_application_install.get_auto_install_sup_obj, false));
commit;
end;
/
set verify on feedback on define on
prompt ...done

Das Problem

An der Stelle, wo der erste User-Account erstellt werden soll, schlägt die Script-Ausführung dann fehl:

ORA-02291: Integritäts-Constraint (APEX_050100.WWV_FLOW_FND_USER_FK) verletzt – übergeordneter Schlüssel nicht gefunden

ORA-06512: in „APEX_050100.WWV_FLOW_FND_USER_INT“, Zeile 2010

ORA-06512: in „APEX_050100.WWV_FLOW_FND_USER_API“, Zeile 328

ORA-06512: in Zeile 2

 

Eine Suche im Oracle Support Portal nach WWV_FLOW_FND_USER_FK liefert (heute am 14.01.2020) exakt Null Ergebnisse – es ist also Eigeninitiative gefragt.
Eine Web-Suche offenbart recht schnell, dass der genannte Foreign Key ein Verweis auf den Workspace ist – was einigermaßen überraschend ist, da der Abschnitt der Workspace-Erstellung aus dem Script mit PL/SQL procedure successfully completed erfolgreich durchgelaufen war.

 

Die Lösung

Auf die Lösung bin ich erst gestoßen als ich im PL/SQL-Developer jeden Schritt einzeln ausgeführt habe. Bereits nach dem ersten Block erkannte der Developer eine aktive offene Transaktion – dies ist an entsprechenden Icons zu erkennen.

Die nachfolgenden Schritte verursachten keinerlei Probleme – bis die erste User Group importiert werden sollte. Hier fiel dann auf, dass dem Developer zufolge die Transaktion verschwand. Und der nachfolgende Import von User Accounts schlug daraufhin mit der bereits beschriebenen Fehlermeldung fehl.

Als ich die User-Group-Erstellung ausließ, funktionierte auch der User-Account-Import wieder problemlos. 😊

Da die User Groups im Rahmen des genannten Projekts nicht weiter verwendet werden, ist die Lösung ausreichend: das Entfernen der Erstellung der User Groups aus dem Workspace-Erstellungs-Scripts.

Die Ursache

Die genaue Ursache lässt sich ohne den entschlüsselten Code des Packages WWV_FLOW_API nur schwer ermitteln – höchstwahrscheinlich fehlt in der Procedure CREATE_USER_GROUPS aber ein RAISE um einen auftretenden Fehler weiterzureichen…

 

Django – Part 1: Installation auf Ubuntu-Server mittels pip

Eine Möglichkeit zur Erstellung einer Webapplikation stellt unter anderem das OpenSource-Framework Django dar, welches als Grundlage die Programmiersprache Python verwendet.
Wie man dieses Framework installiert und zum Laufen bringt, soll folgender Beitrag zeigen.

Auf Ubuntu sollte Python schon installiert sein, mit folgendem Befehl kann man prüfen, welche Version konkret installiert ist:

python3 -V

Sollte Python noch nicht installiert sein, kann man dies über folgenden Befehl nachholen:

sudo apt install python3.6

Weiters benötigen wir das Paketverwaltungsprogramm von Python namens pip (akronym für pip installs packages).

sudo apt install python3-pip

Als weiteren Schritt installieren wir noch das Package python3-venv, mit dessen Hilfe eine virtuelle Python-Laufzeitumgebung erstellt werden kann.

sudo apt install python3-venv

Natürlich können alle drei Packages auf einmal installiert werden, sie wurden hier nur der Beschreibung halber aufgeteilt.

Der Befehl würde dann folgendermaßen aussehen:

sudo apt install python3.6 python3-pip python3-venv

Nachdem nun alle benötigten Ubuntu-Packages installiert sein sollten, erstellen wir nun einen Ordner, in welchem das neue Projekt angelegt werden soll:

mkdir ~/testapp

und navigieren in diesen Ordner:

cd ~/testapp

Als nächsten Schritt erstellen wir nun eine virtuelle Python-Laufzeitumgebung mit folgendem Befehl:

python3 -m venv testapp_venv

Einer der Vorteile einer virtuellen Python-Laufzeitumgebung für einzelne Projekte ist die separate Verwaltung einzelner Packages, deshalb ist dieser Schritt sehr empfohlen, aber nicht zwingend notwendig.

Um nun die virtuelle Umgebung zu aktivieren, ist folgender Befehl notwendig:

source testapp_venv/bin/activate

Der Name der virtuellen Umgebung sollte nun in runden Klammern im Terminal ersichtlich sein. Alle Aktionen, die nun ausgeführt werden, verwenden die Python-Sourcen der virtuellen Umgebung.

Als Nächstes installieren wir also die benötigten Django-Sourcen über pip in der neu erstellten virtuellen Python-Umgebung:

pip install django

Damit ist Django für diese virtuelle Umgebung installiert.

Will man die virtuelle Umgebung wieder verlassen, so kann man dies mit folgendem Befehl machen:

deactivate

Als letzten Schritt erstellen wir nun ein erstes Dummy-Projekt und nennen dieses app1.

Hat man zuvor die virtuelle Umgebung verlassen, so sollte man diese nun nochmals mittels

source ~/testapp/testapp_venv/bin/activate

aktivieren. Wurde der deactivate-Befehl nicht ausgeführt, ist man noch in der virtuellen Umgebung und kann direkt den Befehl zur Erstellung eines neuen Django-Projektes ausführen:

cd ~/testapp

django-admin startproject app1 .

Dadurch werden im aktuellen Ordner diverse für Django benötigte Unterordner und Dateien erzeugt. Die Struktur und Funktionsweise dieser Files werden in einem zukünftigen Blogartikel näher erläutert.

Aktuell wollen wir nur noch das Projekt einmal starten, wir rufen dazu aus der virtuellen Umgebung heraus folgenden Befehl auf:

python manage.py runserver localhost:8000

Öffnet man auf der Maschine nun einen Browser, sollte mittels der URL http://localhost:8000 oder http://127.0.0.1:8000 folgende Seite aufgebaut werden:

Damit ist die erste Django-App erstellt und es kann nun begonnen werden, diese auszubauen.

Doch dazu mehr im Teil 2.

Programmgesteuertes Verschieben des ODI Load Plan in einen bestimmten Ordner

Haben Sie schon einmal erlebt, dass Ihr Load Plan nicht dort ist wo Sie ihn hinterlegt haben?

Aufgrund der manchmal schlechten Implementierung der ODI-Studio Benutzeroberfläche kommt es sehr oft vor, dass jemand anderes (oder sogar Sie selbst) einen Load Plan an einen Ort verschoben hat, an dem er nicht sein sollte.

Wenn die entsprechende Warnung in den ODI Studio Einstellungen deaktiviert ist, erkennt der Entwickler nicht einmal, dass er verschoben wurde.

Sobald Sie dann das Chaos entdeckt haben könnte es auch noch schwierig sein den Load Plan wegen Tonnen von Szenarien in der ODI-Studio Ansicht „Ladepläne und Szenarien“ zurückzusetzen.

Aus diesem Grund habe ich ein nützliches Skript geschrieben, um einen Load Plan wieder an seinen richtigen Platz zu bringen.

Viel Spaß!

import oracle.odi.core.persistence.transaction.ITransactionStatus;
import oracle.odi.core.persistence.transaction.support.DefaultTransactionDefinition;
import oracle.odi.domain.runtime.scenario.finder.IOdiScenarioFolderFinder;
import oracle.odi.domain.runtime.scenario.OdiScenarioFolder;
import oracle.odi.domain.runtime.loadplan.OdiLoadPlan;
import oracle.odi.domain.runtime.loadplan.finder.IOdiLoadPlanFinder;

//
// Find a subfolder with given folderName in parentFolder. 
// When no folder exists and creationFlag = true then the folder will be created.
//
def getScenFolder(parentFolder,folderName,creationFlag,tme) {
  def ff = (IOdiScenarioFolderFinder)tme.getFinder(OdiScenarioFolder.class);
  // There would be a findName() method in the finder class, 
  // but it delivers the first folder having the given name even if more than one exists. 
  // So here all folders have to be checked.
  def fs = ff.findAll(); 
  def folder = null;
  for (f in fs) {
    // If the folder has the same name and the parent folder is the given parentFolder then correct folder was found. 
    // The last part of the expression also checks the top level folder.
    if (f.getName()==folderName&&((f.getParentScenFolder()==parentFolder)||(!f.getParentScenFolder()&&!parentFolder))) {
      folder = f;
    }
  }
  // If the folder doesn't exist and creation is requested then it will be created´.
  if ((!folder)&&creationFlag) {
    if (parentFolder) {
      folder = new OdiScenarioFolder(parentFolder, folderName);
    }
    else {
      folder = new OdiScenarioFolder(folderName);
    }
    tme.persist(folder);
  }
  return folder;
}

//
// Find the folder represented by the full folderPath. 
// The creationFlag indicated if the path should be created if is not exists.
//
def getScenFolder(folderPath,creationFlag,tme){  
  // Split the folder path into parts based on slashes
  parts = folderPath.split("/");
  def parentFolder = null;
  // Loop through the parts to find the correct folder beginning with the root to the leaves.
  for (part in parts) {
    if (part) {
      folder = getScenFolder(parentFolder,part,creationFlag,tme);
      if (!folder) return null; // Stop when either no folder was found or no folder was created.
      parentFolder = folder; // Save found folder as parentfolder for the next level loop.
    }
  }
  return folder;
}

//
// Find the loadplan
//
def getLoadPlan(lpName,tme){
  lpf = (IOdiLoadPlanFinder)tme.getFinder(OdiLoadPlan.class);
  lp = lpf.findByName(lpName);
  return lp;
}

//
// Move the loadplan named with lpName to the folder given by the full folder path in folderPath. 
//
def fixLP(folderPath,lpName,creationFlag){
  txnDef = new DefaultTransactionDefinition();
  tm = odiInstance.getTransactionManager();
  tme = odiInstance.getTransactionalEntityManager();
  txnStatus = tm.getTransaction(txnDef);
  
  try{
    lp = getLoadPlan(lpName,tme);
    // Get the scenario folder given by the full path (creationFlag could force the creation of this path)
    scenf = getScenFolder(folderPath,creationFlag,tme);
    lp.setScenarioFolder(scenf); // Move the load plan to the scenario folder
    tme.persist(lp); // Persist the changes
    tm.commit(txnStatus); // Commit everything
  } catch (Throwable all) {
    println(all)
    tm.rollback(txnStatus); // Rollback if something went wrong
    println(all.getStackTrace());
  }
}

//
// Call the main function to move the loadplan where it should be.
//
fixLP('/ALL_LOADPLANS/MY_LOADPLANS','MY_LOADPLAN',true);
Postgresql Cheat Sheets

PostgreSQL Cheat Sheets – Sammlung

Für PostgreSQL finden sich einige sehr nützliche PostgreSQL Cheat Sheets, um im Alltag mit der Datenbank eine schnelle und nützliche Referenz zur Hand zu haben.

Die korrekte Übersetzung von Cheat-Sheet lautet „Schummelzettel“.

Allerdings zielt die Verwendung in erster Linie nicht auf das Schummeln ab, sonder es handelt sich vielmehr um eine übersichtliche Zusammenstellung von wichtigsten Details zu einem ganz bestimmten Thema.

Im Idealfall sollte die Länge von einem A4 Blatt nicht überschritten werden, aber es gibt natürlich auch umfassendere Cheat-Sheets.

Zum Thema PostgreSQL haben wir folgende nützliche Cheat-Sheets gefunden:

[Link ] PostgreSQL Cheat Sheet von postgresqltutorial.com – 3 Seiten

In diesem Cheat Sheet werden folgende PostgreSQL Themen behandelt:

  • quering data from table
  • quering from multible tables (verschiedene Joins – left join, right join, outer join, cross join, usw..)
  • SQL operatoren – union, intersect, except, like – not like, in – not in, between and, is null – is not null
  • Table Management – create, drop, alter, truncate, etc…
  • SQL Constraints – primary key, foreign key, unique, check, usw..
  • Data Modification – insert into values, insert into select * from table, update set, delete from, etc…
  • View Management – create view as select, create recursive view, create temporary view, etc…
  • Index Management – create index, create unique index, drop index
  • SQL Aggreate Functions – avg, count, sum, max, min
  • Trigger Management – create trigger when event, etc…

[ Link ] Postgresql Cheat Sheet von alberton.info – 1 Seite

In diesem Cheat-Sheet werden folgende PostgreSQL Themen behandelt:

  • Data Types
  • Internal Functions
  • Usefull Queries
  • Information Shema

[ Link ] PostgreSQL Sting Functions Cheat Sheet von SQLBackupAndFTP.com – 1 Seite

In diesem Cheat-Sheet werden PostgreSQL String Functions behandelt:

  • Conversion
  • Measurement
  • Modification

[Link ] Postgresql terminal commands – cheatography.com – 1 Seite

In diesem Cheat-Sheet werden PostgreSQL terminal commands behandelt:

  • Connecting
  • PSQL
  • Roles and database management
  • Database backup

Informationen zu unseren professionellen PostgreSQL Managed Services finden Sie hier 

 

 

Das war die DBConcepts Sommer-Party 2019

Zum dritten Mal in Folge fand heuer wieder unsere beliebte DBConcepts Sommerparty in der Crêperie an der Alten Donau statt.

Über fünfzig TeilnehmerInnen konnten wir heuer begrüßen, um gemeinsam in entspannter Atmosphäre einen gemütlichen und lustigen Abend zu verbringen.

Als Willkommensgeschenk gab es coole Sonnenbrillen für jeden Gast um bei starker Sonnenstrahlung entsprechend geschützt zu sein. 😉

Zwischendurch sorgte ein kleiner aber heftiger Hagelschauer für Weltuntergangsstimmung und eine kurze Unterbrechung der Tretboot-Challenge.

Die unerschrockenen Teams kämpften gleich danach wieder um jede Zehntelsekunde, die Gewinner von Verlierern trennten.

Obwohl bei einigen Gästen alte Blessuren verheilen mussten und eine Teilnahme an der Tretboot-Challenge unmöglich machten, fanden sich 11 Teams um den Champion Titel untereinander auszumachen.

Im Anschluss verwöhnte der Grillmeister mit vielen verschiedenen Köstlichkeiten unseren Gaumen.
Die durstigen Kollegen, Kunden und Freunden von DBConcepts wurden den ganzen Abend mit entsprechende Erfrischungen versorgt und mit cooler Musik von DJ Brain abgekühlt.

Sehr besonders freut uns das begeisterte Feedback aller Gäste, die uns zur gelungen Sommer-Party gratuliert haben.

Nach der Sommer-Party ist immer vor der Sommer-Party.

In diesem Sinne freuen wir uns schon auf nächstes Jahr!

 

 

Migration Oracle Reports auf Oracle BI Publisher

Im April 2019 gab Oracle ein Statement of Direction bezüglich Oracle Reports heraus.

Die wichtigen Punkte grob zusammengefasst:

  1. Fusion Middleware Release 12c R2 (Version 12.2.1.3) ist die letzte Oracle Reports Release
  2. Premier Support gibt es nur mehr für Release 12c R2 bis August 2022
  3. Extended Support gibt es nur mehr für Release 12c R2 bis August 2025
  4. Oracle empfiehlt von Reports auf BI Publisher umzusteigen, welcher die zukünftige Reporting Lösung ist

Die Möglichkeit von Reports auf BI Publisher zu migrieren gab es bereits für BI Publisher Version 11.

Es finden sich Anleitungen dazu in der Oracle Dokumentation „Fusion Middleware Administrator’s Guide for Oracle Business Intelligence Publisher“ unter dem Punkt „Converting Oracle Reports“ (siehe https://docs.oracle.com/cd/E80149_01/bip/BIPAD/GUID-B66E8D0D-7C18-4E17-9394-D889F58088E7.htm#BIPAD4799)

Der zentrale Punkt in dieser Anleitung ist ein Konvertierungstool, welches aus den Reports, welche im XML Format gespeichert sein müssen, entsprechende Dateien für den BI Publisher erzeugt.

Dieses Tool wurde allerdings Anfang 2019 als deprecated eingestuft und wird nicht mehr von Oracle unterstützt (siehe Note Doc ID 2442584.1).

Der einzige Migrationspfad der damit bleibt ist die manuelle Übernahme.

Je nach Komplexität des Reports kann das einige Zeit in Anspruch nehmen, da der Report in BI Publisher neu erstellt werden muss. Das geschieht in 2 Schritten:

  1. Übernahme der SQL Abfragen für die Daten (falls Variablen verwendet werden, erkennt der BI Publisher das automatisch und erstellt sie ggf.) – hier muss auf folgendes geachtet werden:
    Abfragen welche z.B. so beginnen: Select v.* – also einen Table-Alias mit dem Universal Selektor kombinieren – funktionieren nicht korrekt, wenn in weitere Folge Joins für diese Tabelle angelegt werden
  2. Erstellen des Reports mit Design, Struktur und allem anderen.
    Für diesen Schritt kann keine wirkliche Anleitung gegeben werden, da die Möglichkeiten zu vielfältig sind.

Grundsätzlich sollte vorab definiert werden ob die Reports im BI Publisher im RTF oder im XDO Format erstellt werden, damit sie später einheitlich sind.

Parsen von JSON-Daten in einer Oracle-Datenbank

Wer als (APEX-)Entwickler heutzutage Web-Applikationen erstellt, stößt immer häufiger auf Anforderungen der Art „Integration von Daten einer fremden Quelle“.

Also beispielsweise die Integration von Wetterdaten eines Wetterservices. Während vor einigen Jahren überwiegend XML als Format für derartige Datenaustausche verwendet wurde, gehen aktuelle Entwicklungen immer mehr dazu über an dieser Stelle JSON-Strukturen zu verwenden.

Vor allem in etwas älteren APEX-Applikationen kann das mitunter zu Performance-Problemen führen.

In Oracle Datenbank Version 11 gab es etwa noch gar keine native Unterstützung für die Verarbeitung von JSON-Daten – weder in PL/SQL noch in SQL.

Die erste Möglichkeit überhaupt war das Package APEX_JSON, das mit APEX 5.0 eingeführt wurde und mangels Alternativen recht weite Verbreitung fand.

Erst mit 12.1.0.2 begann die Unterstützung in SQL mit der Einführung von JSON_TABLE, mit 12.2 in PL/SQL durch die Einführung der Types JSON_ELEMENT_T, JSON_OBJECT_T etc.

Seither werden diese Funktionalitäten laufend verbessert und erweitert, so dass es sich je nach konkretem Anwendungsfall also durchaus lohnen kann, früher implementierte JSON-Verarbeitung mittels APEX_JSON zumindest zu hinterfragen und bei Bedarf durch native Funktionalitäten abzulösen.

Ein kleiner Test soll die Unterschiede in der Performance aufzeigen:

Vorbereitungen

Um keine Test-Daten generieren zu müssen, verwenden wir an dieser Stelle einen Service des US Geological Survey (USGS): den Erdbeben-Katalog (API Dokumentation: https://earthquake.usgs.gov/fdsnws/event/1/ ). Ein kleines Script erzeugt eine Tabelle für die Testdaten und befüllt diese auch gleich mit einem CLOB mit 20.000 Einträgen im JSON-Format:

CREATE TABLE test_json (id NUMBER GENERATED ALWAYS AS IDENTITY,
data CLOB);
INSERT INTO test_json (data)
VALUES (apex_web_service.make_rest_request(p_url => 'https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&limit=20000',
p_http_method => 'GET'));
SELECT t1.id,
dbms_lob.getlength(t1.data)
FROM test_json t1;

ID DBMS_LOB.GETLENGTH(DATA) 
--- -------------------------
1 15727921

Zur Verdeutlichung der Performance-Unterschiede reicht folgende Anforderung aus: Wir wollen ermitteln wie viele Elemente/Erdbeben das Array FEATURES innerhalb der JSON-Daten enthält (auch wenn wir aufgrund der Parameter des Webservice-Calls bereits erahnen können, dass die Antwort 20.000 lauten wird).

Bei der Verwendung von APEX_JSON gehen wir wie folgt vor:

DECLARE
v_count NUMBER;
v_json_data CLOB;
v_time TIMESTAMP;
BEGIN
SELECT t1.data
INTO v_json_data
FROM test_json t1
WHERE t1.id = 1;

v_time := SYSTIMESTAMP;
apex_json.parse(p_source => v_json_data);
dbms_output.put_line('Parsing Time: ' || EXTRACT(SECOND FROM (SYSTIMESTAMP - v_time)));

v_time := SYSTIMESTAMP;
v_count := apex_json.get_count(p_path => 'features' );
dbms_output.put_line('Feature Count: ' || v_count);
dbms_output.put_line('Counting Time: ' || EXTRACT(SECOND FROM (SYSTIMESTAMP - v_time)));
END;

Parsing Time: 23,200156
Feature Count: 20000
Counting Time: 0,000012

Für eine Applikation, die „nur mal eben live ausgeben soll“, wie viele Erdbeben es in einem bestimmten Zeitraum gegeben hat, sind 23 Sekunden zu viel.

Sehen wir uns daher die Verwendung neuerer Funktionalitäten in PL/SQL an:

DECLARE
v_count NUMBER;
v_json_data CLOB;
v_json_object json_object_t;
v_time TIMESTAMP;
BEGIN
SELECT t1.data
INTO v_json_data
FROM test_json t1
WHERE t1.id = 1;

v_time := SYSTIMESTAMP;
v_json_object := json_object_t.parse(jsn => v_json_data);
dbms_output.put_line('Parsing Time: ' || EXTRACT(SECOND FROM (SYSTIMESTAMP - v_time)));

v_time := SYSTIMESTAMP;
v_count := v_json_object.get_array('features').get_size;
dbms_output.put_line('Feature Count: ' || v_count);
dbms_output.put_line('Counting Time: ' || EXTRACT(SECOND FROM (SYSTIMESTAMP - v_time)));
END;

Parsing Time: 6,513685
Feature Count: 20000
Counting Time: 0,000067

Das ist schon deutlich schneller als in der Variante mit APEX_JSON – für moderne Applikationen aber sind 6sec für vermeintlich einfache Datenabfragen zu lange.

Daher versuchen wir nun die Anzahl primär per SQL zu ermitteln:

DECLARE
v_count NUMBER;
v_time TIMESTAMP;
BEGIN
v_time := SYSTIMESTAMP;

SELECT COUNT(*)
INTO v_count
FROM test_json t1, JSON_TABLE(data, '$.features[*]' COLUMNS (row_number FOR ORDINALITY,
mag NUMBER PATH '$.properties.mag',
eq_id VARCHAR2(30) PATH '$.id')) AS t2
WHERE t1.id = 1;

dbms_output.put_line('Feature Count: ' || v_count);
dbms_output.put_line('Total Time: ' || EXTRACT(SECOND FROM (SYSTIMESTAMP - v_time)));
END;

Feature Count: 20000
Total Time: 0,165338

Obwohl hier alles in einem einzigen Schritt ausgeführt wird, braucht die gesamte Abarbeitung der Anfrage nur mehr knappe 0,2sec und ist damit um einen Faktor deutlich jenseits der 100 schneller als die APEX_JSON-Implementierung.

Wer also größere JSON-Objekte in 12.2+ noch mit APEX_JSON verarbeitet, sollte dringend einmal über eine kleine Frischzellenkur nachdenken…

Webservices direkt aus der DB (XMLDB)

Übersicht

Web Services werden heutzutage immer öfter benutzt um Daten unabhängig vom Standort, zwischen zwei Applikationen auszutauschen und Funktionen aufzurufen.

Oracle stellt mit den Native Oracle XML DB Web Services eine Möglichkeit zur Verfügung, die es erlauben SQL und XQuery Abfragen an einen Host zu senden.

Außerdem kann auf PL/SQL Stored Procedures und Functions zugegriffen werden.

Dabei unterstützt Oracle XML DB das Netzwerkprotokoll SOAP 1.1. Durch die HTTP POST Methode werden die SOAP Requests an die Oracle XML DB Web Services übermittelt.

Der Standort der Web Services und WSDL Dokumente können in der Oracle XML DB Datei xdbconfig.xml konfiguriert werden.

Konfiguration

Um die Web Services zu aktivieren, ist es zuallererst notwendig dem Datenbankbenutzer als SYS User die XDB_WEBSERVICES Rolle zuzuweisen.

Durch die Zuweisung der Rolle können die Web Services benutzt werden. Standardmäßig ist die Benutzung via HTTPS freigeschalten.

Dazu können noch weitere Rollen vergeben werden:

XDB_WEBSERVICES_OVER_HTTP – Benutzung via http

XDB_WEBSERVICES_WITH_PUBLIC – Zugriff auf PUBLIC Datenbank-Objekte

Hat ein User die Zugangsberechtigung auf eine Datenbank mittels Web Services so kann er nur auf die ihm zugewiesenen Datenbank-Objekte zugreifen.

Mit XDB_WEBSERVICES_WITH_PUBLIC kann er nun auch auf PUBLIC Objekte zugreifen.

Zugriff

Das Web Service für Datenbank-Abfragen befindet sich auf http://host:port/orawsv.

Bei host:port handelt es sich um den Datenbank-Host und HTTP(S)-Port. Der Pfad enthält eine WSDL-File, die eingehende und ausgehende Dokumente in XML spezifiziert.

Um auf Stored Procedures und Functions zugreifen zu können, muss http://host:port/orawsv/dbschema/package/fn_or_proc angewählt werden. Host:port enthält wieder den Datenbank-Host und  HTTP(S)-Port. Fn_or_proc gibt dabei die Procedure bzw. Function an.

Mit dbschema wird das Datenbank-Schema angegeben.

Sollte eine Procedure oder Function außerhalb eines Packages sein, so kann package ausgelassen werden.

Anmeldung zur DBConcepts Sommer-Party 2019

Es ist wieder so weit!
Feiern Sie mit uns den Sommer ganz relaxed an der Alten Donau bei einem fantastischen Grill-Buffet und kühlen Getränken!

Für besonders gute Laune sorgt auch heuer wieder die beliebte DBConcepts Tretboot-Challenge.

Weiterlesen

DBConcepts auf den DOAG Datenbank Tagen in Düsseldorf

Zum ersten Mal war heuer DBConcepts auf den DOAG Datenbank Tagen als Sponsor vertreten, welche vom 3. Juni bis 4. Juni in Düsseldorf stattgefunden haben.

In Summe über 200 Teilnehmer/innen informierten sich in zahlreichen Vorträgen über aktuelle Entwicklungen und Trends im Oracle Datenbank Umfeld.

Unser Kollege und Oracle Performance Tuning Spezialist Lothar Flatz gab in seinem Vortrag mit dem Titel „Trouble im Shared Pool“ seine sehr interessanten Einblicke und Erfahrungen preis.

Durch den hohen Bekanntheitsgrad und seine besonders geschätzte Expertise kam es zur erwarteten Überlastung des Vortragraumes.

Leider konnten die Veranstalter nicht rechtzeitig reagieren, woduch circa 30 Teilnehmer den Ausführungen stehend folgen mussten.
Dieser Umstand tat aber der Begeisterung über den Vortrag keinen Abbruch.

Viele Besucher am DBConcepts Ausstellerstand informierten sich über das Leistungsspektrum der „Oracle Experten“ aus Österreich und meldeten sich zusätzlich zum DBConcepts Newsletter an.

Bei den zahlreichen Gesprächen kristallisierte sich heraus, dass der Großteil der Besucher DBConcpts bereits kannten und als Experten im Oracle Technologie Umfeld in Verbindung bringen.

Wir halten allen Teilnehmer/innen unseres Euromillionen-Gewinnspiel die Daumen, dass die Glücksfee bei ihnen vorbei kommt 🙂

DOAG Datenbank Tag 2019 Messestand von DBConcepts

 

Überfüllter Vortrag von Lothar Flatz Trouble im Shared Pool