X

Oracle Application Express
Tipps, Tricks und Best Practice

Recent Posts

XLSX Upload leicht gemacht: Ein einfacher XLSX-Parser.

Die Anforderung, XLSX-Dateien in eine APEX-Anwendung hochzuladen, ist eigentlich recht gängig. Aus dem Stand unterstützt APEX aber nur den Upload von CSV-Dateien. Aus der Entwicklergemeinde sind Blog-Postings und auch Plug-Ins verfügbar - viele davon arbeiten im Hintergrund allerdings mit APEX Collections, sind daher an eine APEX-Session gebunden und lassen sich nur schwer außerhalb einer APEX-Anwendung (bspw. in einem Scheduler-Job) nutzen. Dieses Blog-Posting stellt einen XLSX-Parser vor, der nur mit SQL und PL/SQL arbeitet. Die Implementierung steckt in einer Table-Function, welche die aus der XLSX-Datei extrahierten Daten in Form von Zeilen und Spalten zurückgibt. Es werden keine Daten temporär gespeichert - Collections werden nicht benötigt. Das XLSX-Format ist technisch ein ZIP-Archiv (das kann man auch ausprobieren: einfach von .xlsx in .zip umbenennen und öffnen). In diesem ZIP-Archiv befinden sich XML-Dateien mit den Daten aus dem Excel-Workbook. Um die Daten zu extrahieren, muss man also die richtigen XML-Dateien finden und diese parsen. Und zum XML-Parsing bringt die Datenbank glücklicherweise alles mit, was gebraucht wird. Der folgende Code erzeugt also das PL/SQL Package XLSX_PARSER. In diesem wird das APEX_ZIP Package verwendet, um das ZIP-Archiv zu entpacken, und die SQL Funktion XMLTABLE, um das XML zu parsen und die Daten zu extrahieren. Der REST ist ein wenig PL/SQL-Logik, um die aus dem XML extrahierten Daten korrekt als Zeilen und Spalten zurückzugeben. Der Code, so wie er hier vorliegt, unterstützt die Extraktion von bis zu 50 Spalten aus, XLSX-Dateien mit mehreren Worksheets werden unterstützt. Wenn mehr als 50 Spalten gebraucht werden, so kann der Code sehr leicht erweitert werden - es sind Kommentare enthalten, die auf die zu ändernden Stellen hinweisen: So muss die RECORD-Definition zu Beginn des Package und ein Code-Abschnitt in der PARSE Funktion erweitert werden. Das obere Limit sind 1.000 Spalten. Damit genug der Vorrede - hier ist der Code: create or replace package xlsx_parser is c_date_format constant varchar2(255) := 'YYYY-MM-DD'; -- we currently support 50 columns - but this can easily be increased. Just increase the columns in the -- record definition and add corresponing lines into the package body type xlsx_row_t is record( line# number, col01 varchar2(4000), col02 varchar2(4000), col03 varchar2(4000), col04 varchar2(4000), col05 varchar2(4000), col06 varchar2(4000), col07 varchar2(4000), col08 varchar2(4000), col09 varchar2(4000), col10 varchar2(4000), col11 varchar2(4000), col12 varchar2(4000), col13 varchar2(4000), col14 varchar2(4000), col15 varchar2(4000), col16 varchar2(4000), col17 varchar2(4000), col18 varchar2(4000), col19 varchar2(4000), col20 varchar2(4000), col21 varchar2(4000), col22 varchar2(4000), col23 varchar2(4000), col24 varchar2(4000), col25 varchar2(4000), col26 varchar2(4000), col27 varchar2(4000), col28 varchar2(4000), col29 varchar2(4000), col30 varchar2(4000), col31 varchar2(4000), col32 varchar2(4000), col33 varchar2(4000), col34 varchar2(4000), col35 varchar2(4000), col36 varchar2(4000), col37 varchar2(4000), col38 varchar2(4000), col39 varchar2(4000), col40 varchar2(4000), col41 varchar2(4000), col42 varchar2(4000), col43 varchar2(4000), col44 varchar2(4000), col45 varchar2(4000), col46 varchar2(4000), col47 varchar2(4000), col48 varchar2(4000), col49 varchar2(4000), col50 varchar2(4000)); type xlsx_tab_t is table of xlsx_row_t; --================================================================================================================== -- table function parses the XLSX file and returns the first 15 columns. -- pass either the XLSX blob directly or reference a name in the APEX_APPLICATION_TEMP_FILES table. -- -- p_xlsx_name - NAME column of the APEX_APPLICATION_TEMP_FILES table -- p_xlsx_content - XLSX as a BLOB -- p_worksheet_name - Worksheet to extract -- -- usage: -- -- select * from table( -- xlsx_parser.parse( -- p_xlsx_name => :P1_XLSX_FILE, -- p_worksheet_name => :P1_WORKSHEET_NAME ) ); -- function parse( p_xlsx_name in varchar2 default null, p_xlsx_content in blob default null, p_worksheet_name in varchar2 default 'sheet1', p_max_rows in number default 1000000 ) return xlsx_tab_t pipelined; --================================================================================================================== -- table function to list the available worksheets in an XLSX file -- -- p_xlsx_name - NAME column of the APEX_APPLICATION_TEMP_FILES table -- p_xlsx_content - XLSX as a BLOB -- -- usage: -- -- select * from table( -- xlsx_parser.get_worksheets( -- p_xlsx_name => :P1_XLSX_FILE ) ); -- function get_worksheets( p_xlsx_content in blob default null, p_xlsx_name in varchar2 default null ) return apex_t_varchar2 pipelined; --================================================================================================================== -- date and datetimes are stored as a number in XLSX; this function converts that number to an ORACLE DATE -- -- p_xlsx_date_number numeric XLSX date value -- -- usage: -- select xlsx_parser.get_date( 46172 ) from dual; -- function get_date( p_xlsx_date_number in number ) return date; end xlsx_parser; / sho err create or replace package body xlsx_parser is g_worksheets_path_prefix constant varchar2(14) := 'xl/worksheets/'; --================================================================================================================== function get_date( p_xlsx_date_number in number ) return date is begin return case when p_xlsx_date_number > 61 then DATE'1900-01-01' - 2 + p_xlsx_date_number else DATE'1900-01-01' - 1 + p_xlsx_date_number end; end get_date; --================================================================================================================== procedure get_blob_content( p_xlsx_name in varchar2, p_xlsx_content in out nocopy blob ) is begin if p_xlsx_name is not null then select blob_content into p_xlsx_content from apex_application_temp_files where name = p_xlsx_name; end if; exception when no_data_found then null; end get_blob_content; --================================================================================================================== function extract_worksheet( p_xlsx in blob, p_worksheet_name in varchar2 ) return blob is l_worksheet blob; begin if p_xlsx is null or p_worksheet_name is null then return null; end if; l_worksheet := apex_zip.get_file_content( p_zipped_blob => p_xlsx, p_file_name => g_worksheets_path_prefix || p_worksheet_name || '.xml' ); if l_worksheet is null then raise_application_error(-20000, 'WORKSHEET "' || p_worksheet_name || '" DOES NOT EXIST'); end if; return l_worksheet; end extract_worksheet; --================================================================================================================== procedure extract_shared_strings( p_xlsx in blob, p_strings in out nocopy wwv_flow_global.vc_arr2 ) is l_shared_strings blob; begin l_shared_strings := apex_zip.get_file_content( p_zipped_blob => p_xlsx, p_file_name => 'xl/sharedStrings.xml' ); if l_shared_strings is null then return; end if; select shared_string bulk collect into p_strings from xmltable( xmlnamespaces( default 'http://schemas.openxmlformats.org/spreadsheetml/2006/main' ), '//si' passing xmltype.createxml( l_shared_strings, nls_charset_id('AL32UTF8'), null ) columns shared_string varchar2(4000) path 't/text()' ); end extract_shared_strings; --================================================================================================================== procedure extract_date_styles( p_xlsx in blob, p_format_codes in out nocopy wwv_flow_global.vc_arr2 ) is l_stylesheet blob; begin l_stylesheet := apex_zip.get_file_content( p_zipped_blob => p_xlsx, p_file_name => 'xl/styles.xml' ); if l_stylesheet is null then return; end if; select lower( n.formatCode ) bulk collect into p_format_codes from xmltable( xmlnamespaces( default 'http://schemas.openxmlformats.org/spreadsheetml/2006/main' ), '//cellXfs/xf' passing xmltype.createxml( l_stylesheet, nls_charset_id('AL32UTF8'), null ) columns numFmtId number path '@numFmtId' ) s, xmltable( xmlnamespaces( default 'http://schemas.openxmlformats.org/spreadsheetml/2006/main' ), '//numFmts/numFmt' passing xmltype.createxml( l_stylesheet, nls_charset_id('AL32UTF8'), null ) columns formatCode varchar2(255) path '@formatCode', numFmtId number path '@numFmtId' ) n where s.numFmtId = n.numFmtId ( + ); end extract_date_styles; --================================================================================================================== function convert_ref_to_col#( p_col_ref in varchar2 ) return pls_integer is l_colpart varchar2(10); l_linepart varchar2(10); begin l_colpart := replace(translate(p_col_ref,'1234567890','__________'), '_'); if length( l_colpart ) = 1 then return ascii( l_colpart ) - 64; else return ( ascii( substr( l_colpart, 1, 1 ) ) - 64 ) * 26 + ( ascii( substr( l_colpart, 2, 1 ) ) - 64 ); end if; end convert_ref_to_col#; --================================================================================================================== procedure reset_row( p_parsed_row in out nocopy xlsx_row_t ) is begin -- reset row p_parsed_row.col01 := null; p_parsed_row.col02 := null; p_parsed_row.col03 := null; p_parsed_row.col04 := null; p_parsed_row.col05 := null; p_parsed_row.col06 := null; p_parsed_row.col07 := null; p_parsed_row.col08 := null; p_parsed_row.col09 := null; p_parsed_row.col10 := null; p_parsed_row.col11 := null; p_parsed_row.col12 := null; p_parsed_row.col13 := null; p_parsed_row.col14 := null; p_parsed_row.col15 := null; p_parsed_row.col16 := null; p_parsed_row.col17 := null; p_parsed_row.col18 := null; p_parsed_row.col19 := null; p_parsed_row.col20 := null; p_parsed_row.col21 := null; p_parsed_row.col22 := null; p_parsed_row.col23 := null; p_parsed_row.col24 := null; p_parsed_row.col25 := null; p_parsed_row.col26 := null; p_parsed_row.col27 := null; p_parsed_row.col28 := null; p_parsed_row.col29 := null; p_parsed_row.col30 := null; p_parsed_row.col31 := null; p_parsed_row.col32 := null; p_parsed_row.col33 := null; p_parsed_row.col34 := null; p_parsed_row.col35 := null; p_parsed_row.col36 := null; p_parsed_row.col37 := null; p_parsed_row.col38 := null; p_parsed_row.col39 := null; p_parsed_row.col40 := null; p_parsed_row.col41 := null; p_parsed_row.col42 := null; p_parsed_row.col43 := null; p_parsed_row.col44 := null; p_parsed_row.col45 := null; p_parsed_row.col46 := null; p_parsed_row.col47 := null; p_parsed_row.col48 := null; p_parsed_row.col49 := null; p_parsed_row.col50 := null; end reset_row; --================================================================================================================== function parse( p_xlsx_name in varchar2 default null, p_xlsx_content in blob default null, p_worksheet_name in varchar2 default 'sheet1', p_max_rows in number default 1000000 ) return xlsx_tab_t pipelined is l_worksheet blob; l_xlsx_content blob; l_shared_strings wwv_flow_global.vc_arr2; l_format_codes wwv_flow_global.vc_arr2; l_parsed_row xlsx_row_t; l_first_row boolean := true; l_value varchar2(32767); l_line# pls_integer := 1; l_real_col# pls_integer; l_row_has_content boolean := false; begin if p_xlsx_content is null then get_blob_content( p_xlsx_name, l_xlsx_content ); else l_xlsx_content := p_xlsx_content; end if; if l_xlsx_content is null then return; end if; l_worksheet := extract_worksheet( p_xlsx => l_xlsx_content, p_worksheet_name => p_worksheet_name ); extract_shared_strings( p_xlsx => l_xlsx_content, p_strings => l_shared_strings ); extract_date_styles( p_xlsx => l_xlsx_content, p_format_codes => l_format_codes ); -- the actual XML parsing starts here for i in ( select r.xlsx_row, c.xlsx_col#, c.xlsx_col, c.xlsx_col_type, c.xlsx_col_style, c.xlsx_val from xmltable( xmlnamespaces( default 'http://schemas.openxmlformats.org/spreadsheetml/2006/main' ), '//row' passing xmltype.createxml( l_worksheet, nls_charset_id('AL32UTF8'), null ) columns xlsx_row number path '@r', xlsx_cols xmltype path '.' ) r, xmltable ( xmlnamespaces( default 'http://schemas.openxmlformats.org/spreadsheetml/2006/main' ), '//c' passing r.xlsx_cols columns xlsx_col# for ordinality, xlsx_col varchar2(15) path '@r', xlsx_col_type varchar2(15) path '@t', xlsx_col_style varchar2(15) path '@s', xlsx_val varchar2(4000) path 'v/text()' ) c where p_max_rows is null or r.xlsx_row <= p_max_rows ) loop if i.xlsx_col# = 1 then l_parsed_row.line# := l_line#; if not l_first_row then pipe row( l_parsed_row ); l_line# := l_line# + 1; reset_row( l_parsed_row ); l_row_has_content := false; else l_first_row := false; end if; end if; if i.xlsx_col_type = 's' then if l_shared_strings.exists( i.xlsx_val + 1) then l_value := l_shared_strings( i.xlsx_val + 1); else l_value := '[Data Error: N/A]' ; end if; else if l_format_codes.exists( i.xlsx_col_style + 1 ) and ( instr( l_format_codes( i.xlsx_col_style + 1 ), 'd' ) > 0 and instr( l_format_codes( i.xlsx_col_style + 1 ), 'm' ) > 0 ) then l_value := to_char( get_date( i.xlsx_val ), c_date_format ); else l_value := i.xlsx_val; end if; end if; pragma inline( convert_ref_to_col#, 'YES' ); l_real_col# := convert_ref_to_col#( i.xlsx_col ); if l_real_col# between 1 and 50 then l_row_has_content := true; end if; -- we currently support 50 columns - but this can easily be increased. Just add additional lines -- as follows: -- when l_real_col# = {nn} then l_parsed_row.col{nn} := l_value; case when l_real_col# = 1 then l_parsed_row.col01 := l_value; when l_real_col# = 2 then l_parsed_row.col02 := l_value; when l_real_col# = 3 then l_parsed_row.col03 := l_value; when l_real_col# = 4 then l_parsed_row.col04 := l_value; when l_real_col# = 5 then l_parsed_row.col05 := l_value; when l_real_col# = 6 then l_parsed_row.col06 := l_value; when l_real_col# = 7 then l_parsed_row.col07 := l_value; when l_real_col# = 8 then l_parsed_row.col08 := l_value; when l_real_col# = 9 then l_parsed_row.col09 := l_value; when l_real_col# = 10 then l_parsed_row.col10 := l_value; when l_real_col# = 11 then l_parsed_row.col11 := l_value; when l_real_col# = 12 then l_parsed_row.col12 := l_value; when l_real_col# = 13 then l_parsed_row.col13 := l_value; when l_real_col# = 14 then l_parsed_row.col14 := l_value; when l_real_col# = 15 then l_parsed_row.col15 := l_value; when l_real_col# = 16 then l_parsed_row.col16 := l_value; when l_real_col# = 17 then l_parsed_row.col17 := l_value; when l_real_col# = 18 then l_parsed_row.col18 := l_value; when l_real_col# = 19 then l_parsed_row.col19 := l_value; when l_real_col# = 20 then l_parsed_row.col20 := l_value; when l_real_col# = 21 then l_parsed_row.col21 := l_value; when l_real_col# = 22 then l_parsed_row.col22 := l_value; when l_real_col# = 23 then l_parsed_row.col23 := l_value; when l_real_col# = 24 then l_parsed_row.col24 := l_value; when l_real_col# = 25 then l_parsed_row.col25 := l_value; when l_real_col# = 26 then l_parsed_row.col26 := l_value; when l_real_col# = 27 then l_parsed_row.col27 := l_value; when l_real_col# = 28 then l_parsed_row.col28 := l_value; when l_real_col# = 29 then l_parsed_row.col29 := l_value; when l_real_col# = 30 then l_parsed_row.col30 := l_value; when l_real_col# = 31 then l_parsed_row.col31 := l_value; when l_real_col# = 32 then l_parsed_row.col32 := l_value; when l_real_col# = 33 then l_parsed_row.col33 := l_value; when l_real_col# = 34 then l_parsed_row.col34 := l_value; when l_real_col# = 35 then l_parsed_row.col35 := l_value; when l_real_col# = 36 then l_parsed_row.col36 := l_value; when l_real_col# = 37 then l_parsed_row.col37 := l_value; when l_real_col# = 38 then l_parsed_row.col38 := l_value; when l_real_col# = 39 then l_parsed_row.col39 := l_value; when l_real_col# = 40 then l_parsed_row.col40 := l_value; when l_real_col# = 41 then l_parsed_row.col41 := l_value; when l_real_col# = 42 then l_parsed_row.col42 := l_value; when l_real_col# = 43 then l_parsed_row.col43 := l_value; when l_real_col# = 44 then l_parsed_row.col44 := l_value; when l_real_col# = 45 then l_parsed_row.col45 := l_value; when l_real_col# = 46 then l_parsed_row.col46 := l_value; when l_real_col# = 47 then l_parsed_row.col47 := l_value; when l_real_col# = 48 then l_parsed_row.col48 := l_value; when l_real_col# = 49 then l_parsed_row.col49 := l_value; when l_real_col# = 50 then l_parsed_row.col50 := l_value; else null; end case; end loop; if l_row_has_content then l_parsed_row.line# := l_line#; pipe row( l_parsed_row ); end if; return; end parse; --================================================================================================================== function get_worksheets( p_xlsx_content in blob default null, p_xlsx_name in varchar2 default null ) return apex_t_varchar2 pipelined is l_zip_files apex_zip.t_files; l_xlsx_content blob; begin if p_xlsx_content is null then get_blob_content( p_xlsx_name, l_xlsx_content ); else l_xlsx_content := p_xlsx_content; end if; l_zip_files := apex_zip.get_files( p_zipped_blob => l_xlsx_content ); for i in 1 .. l_zip_files.count loop if substr( l_zip_files( i ), 1, length( g_worksheets_path_prefix ) ) = g_worksheets_path_prefix then pipe row( rtrim( substr( l_zip_files ( i ), length( g_worksheets_path_prefix ) + 1 ), '.xml' ) ); end if; end loop; return; end get_worksheets; end xlsx_parser; / sho err Nach dem Einspielen des Package stehen die folgenden Funktionen bereit: FUNCTION GET_WORKSHEETS RETURNS APEX_T_VARCHAR2 Argument Name Type In/Out Default? ------------------------------ ----------------------- ------ -------- P_XLSX_CONTENT BLOB IN DEFAULT P_XLSX_NAME VARCHAR2 IN DEFAULT FUNCTION PARSE RETURNS XLSX_TAB_T Argument Name Type In/Out Default? ------------------------------ ----------------------- ------ -------- P_XLSX_NAME VARCHAR2 IN DEFAULT P_XLSX_CONTENT BLOB IN DEFAULT P_WORKSHEET_NAME VARCHAR2 IN DEFAULT P_MAX_ROWS NUMBER IN DEFAULT Die XLSX-Datei kann entweder als BLOB oder als Dateiname übergeben werden. In letzterem Fall wird der BLOB aus der Tabelle APEX_APPLICATION_TEMP_FILES geholt - das ist nützlich, wenn die Datei mit einem APEX File Browse Element hochgeladen wurde. Eine typische Aufruf-Sequenz sieht wie folgt aus: Im XLSX vorhandene Worksheets mit XLSX_PARSER.GET_WORKSHEETS auslesen Die Daten eines konkreten Worksheet  XLSX_PARSER.PARSE extrahieren. Probieren Sie es gleich aus: Erstellen Sie dazu eine APEX-Anwendung mit einer leeren Seite. Fügen Sie dann die folgenden Komponenten hinzu. Eine Region vom Typ Static HTML. Ein File Browse Element zum Hochladen der XLSX-Datei: "PX_XLSX_FILE". Nehmen Sie Table APEX_APPLICATION_TEMP_FILES als Storage Type und behalten Sie die Dateien bis zum End Of Session. Fügen Sie dann noch einen Button zum Hochladen hinzu. Stellen Sie sicher, dass die Button-Action auf Submit Page steht. Die Seite sollte dann in etwa wie folgt aussehen: Sie können schon eine Datei hochladen, aber es wird dann noch nichts passieren. Als nächstes soll die Seite erweitert werden, so dass sie eine Auswahlliste enthält, welche die in der XLSX-Datei vorhandenen Worksheets anzeigt.  Fügen Sie ein Element vom Typ Select List namens  PX_WORKSHEET hinzu. Nehmen Sie SQL Query als List Of Values Type und verwenden Sie die folgende SQL-Abfrage: select column_value d, column_value r from table( xlsx_parser.get_worksheets( p_xlsx_name => :PX_XLSX_FILE ) ) Stellen Sie Display Extra Values auf No, Display Null Value auf Yes und nehmen Sie - Choose - als Null Display Value. Fügen Sie schließlich eineServer-Side condition hinzu, so dass die Auswahlliste nur angezeigt wird, wenn das File Browse Element PX_XLSX_FILE einen Wert hat (IS NOT NULL). Starten Sie die Seite nochmal. Wenn Sie nun eine XLSX-Datei hochladen, sollte die Seite wie folgt aussehen. Nun können Sie also eines der in der XLSX-Datei enthaltenen Worksheets auswählen. Nun ist es an der Zeit, die Daten aus dem ausgewählten Worksheet zu extrahieren: Für den Moment wollen wir sie einfach nur darstellen, nehmen Sie daher also einen Classic Report. Die SQL-Abfrage des Classic Report verwendet die Table-Function des XLSX-Parsers: XLSX_PARSER.PARSE. select * from table( xlsx_parser.parse( p_xlsx_name => :PX_XLSX_FILE, p_worksheet_name => :PX_WORKSHEET ) ); Fügen Sie das Element PX_WORKSHEET zu dem Page Items to Submit Attribut des Classic Report hinzu. Dann braucht es noch eine Dynamic Action,  damit der Bericht sich aktualisiert, sobald ein Worksheet in der Auswahlliste selektiert wurde. Die Dynamic Action sollte auslösen, wenn ein Change Ereignis auf dem Element PX_WORKSHEET stattfindet. Als TRUE Action nehmen Sie Refresh und wählen Sie die Classic Report Region aus Affected Region aus. Nun sollte Ihre Anwendungsseite den folgenden Ablauf unterstützen: Starten Sie die Seite und laden Sie ein XLSX hoch. Wählen Sie ein Worksheet aus der Auswahlliste aus. Die Berichtsregion sollte sich aktualisieren und die extrahierten Daten anzeigen. Fertig. Und das Schöne daran ist, dass dieser Ansatz sehr offen ist - die die Daten von der Table Function wie eine Tabelle bereitgestellt werden, können sie auch beliebig weiterverarbeitet werden ... Man könnte sie einfach in eine Tabelle kopieren ... create table worksheet_data as select col02 as first_name, col03 as last_name, col05 as country from table( xlsx_parser.parse( p_xlsx_name => :PX_XLSX_FILE, p_worksheet_name => :PX_WORKSHEET ) ); Einige Transformationen anwenden ... insert into worksheet_data( select cast( col02 as varchar2(200) ) as first_name, cast( col03 as varchar2(200) ) as last_name, case col04 when 'Female' then 'F' when 'Male' then 'M' end as gender, cast( col05 as varchar2(200) ) as country, to_number( to_char( sysdate, 'YYYY') ) - to_number( col06 ) as birth_year from table( xlsx_parser.parse( p_xlsx_name => :PX_XLSX_FILE, p_worksheet_name => :PX_WORKSHEET ) ) where line# != 1) Oder das Ganze mit einem DBMS_SCHEDULER job in den Hintergrund schicken - was bei größeren XLSX-Dateien sicherlich interessant ist. Beachten Sie dabei allerdings, dass ein DBMS_SCHEDULER job in einer eigenen Datenbanksession - und unabhängig von APEX läuft. In der Tabelle APEX_APPLICATION_TEMP_FILES wird er also nichts finden - kopieren Sie den BLOB daher vorher in eine eigene Tabelle ... Probieren Sie den Beispiel-Code aus! Sobald die Table Function Daten zurückliefert, sind Ihren Möglichkeiten keinerlei Grenzen mehr gesetzt. Und wenn Sie mehr Spalten brauchen - der Code ist sehr leicht erweiterbar.

Die Anforderung, XLSX-Dateien in eine APEX-Anwendung hochzuladen, ist eigentlich recht gängig. Aus dem Stand unterstützt APEX aber nur den Upload von CSV-Dateien. Aus der Entwicklergemeinde sind...

Deutschsprachige APEX und PL/SQL Community

APEX_STRING: String-Operationen leicht gemacht!

In der Community dreht sich derzeit fast alles um Application Express 18.1 - so findet Ihr auf dem englischsprachigen Application Express Blog eine Vielzahl von Artikeln zur neuen Unterstützung für REST Services und REST Enabled SQL. In diesem Blog Posting geht es um das APEX_STRING package - das ist seit 5.1 vorhanden - Ihr könnt es also sofort nutzen. APEX_STRING enthält einige Funktionen und Prozeduren zum einfachen Umgang mit Zeichenketten. So ist mit der Funktion SPLIT ein String Tokenizer enthalten ... select apex_string.split( 'a,b,c,d,e,f,g,h', ',' ) from dual; COLUMN_VALUE --------------------------- a b c d e f Auch in PL/SQL kann SPLIT verwendet werden. declare l_array wwv_flow_t_varchar2; begin l_array := apex_string.split( 'a,b,c,d,e,f,g,h', ',' ); for i in 1 .. l_array.count loop dbms_output.put_line( apex_string.format( 'element at index %s: %s', i, l_array(i) ) ); end loop; end; / element at index 1: a element at index 2: b element at index 3: c element at index 4: d element at index 5: e : Ist man nur an den ersten Werten eines größeren Strings (oder CLOBs) interessiert, so ist der Parameter P_LIMIT bedeutsam: Ist das Limit erreicht, bricht die Funktion ab und spart so Rechenaufwand. Das letzte Element im zurückgegebenen Array enthält den Rest des Strings. Neben VARCHAR2 unterstützt SPLIT auch den CLOB-Datentypen, SPLIT_NUMBERS liefert außerdem ein Array von NUMBER-Datentypen zurück. Aufmerksame Leser dürften bemerken, dass in obigem Block noch eine weitere Funktion aus dem APEX_STRING-Paket verwendet wurde: FORMAT. FORMAT vereinfacht das Zusammensetzen von Strings aus festen Literalen und variablen Inhalten: anstelle vieler Konkatenationen mit || setzt man %s oder %{N} in den String ein. APEX_STRING ersetzt das n-te %s durch das n-the Argument p{N} (also das erste %s durch das Argument p0, das zweite durch p1 und so fort). Die Argumente p{N} können mit %{N} auch direkt angesprochen und so mehrfach verwendet werden: So spricht man p0 mit %0, p1 mit %1 an und so fort.   select apex_string.format( p_message => 'Text mit Platzhaltern: USER: %s, SYSDATE: %s, RANDOM VALUE: %s', p0 => user, p1 => sysdate, p2 => dbms_random.value ) as formatted_text from dual; FORMATTED_TEXT ------------------------------------------------------------------------------ Text mit Platzhaltern: USER: SYS, SYSDATE: 21.01.2017 06:39:47, RANDOM VALUE: ,52835625740274072229710302735665090347 select apex_string.format( p_message => 'Text mit Platzhaltern: USER: %0, SYSDATE: %1, nochmal USER: %0', p0 => user, p1 => sysdate ) as formatted_text from dual; FORMATTED_TEXT ------------------------------------------------------------------------------ Text mit Platzhaltern: USER: SYS, SYSDATE: 21.01.2017 06:42:34, nochmal USER: SYS Mit den PLIST Prozeduren können einfache Property Listen verwaltet werden. Ein Eintrag in einer solchen Liste, die in einem Array vom Typ WWV_FLOW_T_VARCHAR2 abgelegt wird, ist ein Key-Value-Paar. Folgerichtig gibt es die Funktionen PLIST_GET, PLIST_PUT und PLIST_DELETE. Der folgende Code zeigt, wie's geht: declare l_props wwv_flow_t_varchar2 := wwv_flow_t_varchar2(); begin apex_string.plist_put( l_props, 'username', 'SCOTT' ); apex_string.plist_put( l_props, 'password', 'tiger' ); apex_string.plist_put( l_props, 'connection', 'localhost:1521/orcl' ); for i in 1 .. l_props.count loop dbms_output.put_line( apex_string.format( '%0: %1', i, l_props( i ) ) ); end loop; dbms_output.put_line( apex_string.format( p_message => '*** %0/%1@%2', p0 => apex_string.plist_get(l_props, 'username'), p1 => apex_string.plist_get(l_props, 'password'), p2 => apex_string.plist_get(l_props, 'connection') )); apex_string.plist_delete( l_props, 'password' ); for i in 1 .. l_props.count loop dbms_output.put_line( apex_string.format( '%0: %1', i, l_props( i ) ) ); end loop; end; / 1: username 2: SCOTT 3: password 4: tiger 5: connection 6: localhost:1521/orcl *** SCOTT/tiger@localhost:1521/orcl 1: username 2: SCOTT 3: 4: 5: connection 6: localhost:1521/orcl Die Prozedur PUSH ist ein bequemer Weg, einen einfachen Wert an das Array anzuhängen. Der folgende Code zeigt sie im Zusammenspiel mit der Funktion SHUFFLE, welche das Array "durcheinanderwürfelt". declare l_array wwv_flow_t_varchar2; begin apex_string.push( l_array, 'Value 1' ); apex_string.push( l_array, 'Value 2' ); apex_string.push( l_array, 'Value 3' ); apex_string.push( l_array, 'Value 4' ); apex_string.shuffle( l_array ); for i in 1 .. l_array.count loop dbms_output.put_line( apex_string.format( '%0: %1', i, l_array( i ) ) ); end loop; end; / 1: Value 3 2: Value 2 3: Value 1 4: Value 4   Eine sehr mächtige Funktion ist für GREP, diese funktioniert wie die REGEXP_* SQL-Funktionen, liefert mehrere Ergebnisse aber als Array zurück. Das folgende Beispiel zeigt das: Aus einem Text sollen alle Email-Adressen extrahiert werden (die Regexp für Emailadressen ist sicherlich nicht die beste, für dieses Beispiel soll es aber reichen). select * from table( apex_string.grep( 'This is a text with some mail adresses. The first one is a.a@oracle.com, we have ' || 'noname@company.com and thesupport@firma.de', '[A-Z0-9._%+-]*@[A-Z0-9.-]*\.[A-Z]*', 'i' )); COLUMN_VALUE -------------------------- a.a@oracle.com noname@company.com thesupport@firma.de Die "normale" Funktion REGEXP_SUBSTR liefert stets genau einen String zurück; APEX_STRING.GREP dagegen ein Array mit einem Element für jeden Match. Genau das macht die Arbeit mit den extrahierten Werten massiv einfacher. Zum Schluß stellen wir mit JOIN das Gegenstück zur eingangs vorgestellten Funktion SPLIT vor und schließen so den Kreis. declare l_array wwv_flow_t_varchar2; begin apex_string.push( l_array, 'Value 1' ); apex_string.push( l_array, 'Value 2' ); apex_string.push( l_array, 'Value 3' ); apex_string.push( l_array, 'Value 4' ); apex_string.shuffle( l_array ); dbms_output.put_line( apex_string.join( l_array, '#' ) ); end; / Value 4#Value 2#Value 1#Value 3 APEX_STRING ist ein mit Sicherheit sehr nützliches Paket - welches einen näheren Blick absolut verdient.  

In der Community dreht sich derzeit fast alles um Application Express 18.1 - so findet Ihr auf dem englischsprachigen Application Express Blog eine Vielzahl von Artikeln zur neuen Unterstützung für...

SQL und PL/SQL

Tipps zum JSON Parsing für APEX-Entwickler

Die Arbeit mit JSON ist, auch für den APEX-Entwickler, inzwischen zum Alltag geworden. So liefern REST Services, die man aus der APEX-Anwendung heraus aufruft, typischerweise JSON zurück - auch Konfigurationsdaten werden mehr und mehr als JSON gespeichert und verwaltet. So entsteht die Aufgabe, ein JSON Dokument zu parsen oder eines zu generieren. Je nach verwendeter Datenbankversion gibt es unterschiedliche viele Möglichkeiten, ein JSON Dokument mit APEX zu verarbeiten.  Nutzt man 11.2 oder 12.1.0.1, so bietet die Datenbank selbst keine nativen JSON-Funktionen an - das PL/SQL Paket APEX_JSON ist das Mittel der Wahl. Ab 12.1.0.2 stehen SQL-Funktionen wie JSON_TABLE, JSON_QUERY oder JSON_VALUE bereit. Diese können in SQL-Abfragen genutzt werden. Ab 12.2 stehen zusätzlich SQL-Funktionen zum Erzeugen von JSON und ein PL/SQL Parser bereit. Nutzt man also eine Datenbank der Version 12.2 oder gar 18.1, so bieten sich sehr viele Möglichkeiten an, mit JSON zu arbeiten. Oft wird weiterhin APEX_JSON genutzt, einfach weil man es kennt - oder auch "weil es ein APEX-Package ist". Aber es lohnt sich wirklich, die nativen Funktionen der Datenbank genauer anzusehen - mit wenig Aufwand kann eine ganze Menge Performance herausgeholt werden. Dieser Tipp stellt einige Aspekte vor. Hören Sie nicht auf zu lesen, wenn Sie mit einer 11.2 Datenbank arbeiten oder eine solche unterstützen müssen. Mit der PL/SQL Conditional Compilation können Sie Code für unterschiedliche Datenbankversionen aufnehmen - je nach Datenbankversion bekommt der PL/SQL Compiler bestimmten Code vorgelegt. Wenn Sie mehrere Datenbankversionen unterstützen müssen, kann Ihr Code somit aus dem Stand von einer neuen Datenbankversion profitieren - als Nebeneffekt trainieren Sie bereits für die neuen Features der aktuellen Datenbankversionen. Vorbereitungen Zunächst braucht es ein JSON-Dokument, welches geparst werden soll. Am einfachsten ist es, man holt sich eines aus dem Internet - das in früheren Tipps schon öfter verwendete Earthquake-Beispiel bietet sich an. Mit dem APEX_WEB_SERVICE-Paket kann das JSON sehr leicht abgeholt werden. Zum testen speichert man es am besten in eine Tabelle ab, wie folgt: create table earthquake_json( id number generated always as identity, fetched_at timestamp default systimestamp, document clob, constraint document_isjson check (document is json) ); insert into earthquake_json( document ) values ( apex_web_service.make_rest_request( p_url => 'https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_month.geojson', p_http_method => 'GET' ) ); select id, fetched_at, dbms_lob.getlength(document) from earthquake_json; ID FETCHED_AT DBMS_LOB.GETLENGTH(DOCUMENT) --- ---------------------------- ---------------------------- 1 26.02.18 02:29:59,988083000 6743973 Das JSON-Dokument hat die im folgenden abgebildete Struktur - das Attribut features ist ein Array, mit einem Element für jedes registrierte Erdbeben.  Dieses JSON-Dokument gilt es zu parsen. Es ist bewusst ein etwas größeres gewählt worden, damit die Performance-Unterschiede (es gibt welche) recht deutlich werden. JSON mit PL/SQL parsen Zunächst möchten wir wissen, wieviele Elemente im Array features enthalten sind. Zunächst arbeiten wir mit APEX_JSON als PL/SQL Block. Das ist ab APEX 5.0, auf allen Datenbankversionen, verfügbar.  declare l_clob clob; l_feature_count pls_integer; l_time timestamp; begin select document into l_clob from earthquake_json where id = 1; l_time := systimestamp; apex_json.parse( p_source => l_clob ); dbms_output.put_line( 'Parsing Time: ' || extract( second from ( systimestamp - l_time ) ) ); l_time := systimestamp; l_feature_count := apex_json.get_count( 'features' ); dbms_output.put_line( 'Array Count: ' || l_feature_count ); dbms_output.put_line( 'Get Array Count Time: ' || extract( second from ( systimestamp - l_time ) ) ); end; Parsing Time: 12,293681 Array Count: 9456 Get Array Count Time: 0,000039 Der Aufruf von APEX_JSON.PARSE hat etwa 12 Sekunden verbraucht; das eigentliche JSON-Parsing ist also recht teuer. Das eigentliche "Zählen" der Elemente geht dann ganz schnell. Grund ist, dass APEX_JSON.PARSE eine PL/SQL Record-Struktur aufbaut, welche das JSON repräsentiert. Ist diese einmal aufgebaut, so sind die Zugriffe recht fix. Schauen wir uns dagegen mal die ab Oracle 12.2 vorhandenen PL/SQL Object Types zum JSON-Parsing an (JSON_OBJECT_T, JSON_ARRAY_T, JSON_ELEMENT_T). Jeder Objekttyp enthält eine statische Methode PARSE. Auch hier wird das JSON-Dokument durch eine In-Memory Struktur repräsentiert, so dass die Zugriffe auf einzelne Elemente danach recht fix sind.  Ein PL/SQL-Block, der zum Parsing JSON_OBJECT_T anstelle von APEX_JSON verwendet, sieht gar nicht so stark verändert aus ... declare l_clob clob; l_feature_count pls_integer; l_time timestamp; l_json json_object_t; l_features json_array_t; begin select document into l_clob from earthquake_json where id = 1; l_time := systimestamp; l_json := json_object_t.parse( l_clob ); dbms_output.put_line( 'Parsing Time: ' || extract( second from ( systimestamp - l_time ) ) ); l_time := systimestamp; l_features := l_json.get_array( 'features' ); dbms_output.put_line( 'Array Count: ' || l_features.get_size ); dbms_output.put_line( 'Get Array Count Time: ' || extract( second from ( systimestamp - l_time ) ) ); end; Parsing Time: 0,124148 Array Count: 9456 Get Array Count Time: 0,000083 ... bei der verbrauchten Zeit ergeben sich aber massive Unterschiede: 0.12 Sekunden vs. 12 Sekunden ist etwa Faktor 100. Bei kleineren JSON-Dokumenten fallen die Unterschiede zwar nicht so krass aus, aber sie sind dennoch da. JSON-Parsing mit APEX_JSON ist wesentlich teurer als mit den nativen PL/SQL Funktionen. Der Grund ist ein einfacher: APEX_JSON Ist eine reine PL/SQL Implementierung; die nativen Funktionen sind im Datenbankkern in C implementiert. Und gerade eine Aufgabe wie das Parsen von JSON profitiert massiv von einer nativen Implementierung. JSON mit einer SQL Abfrage parsen Wenn es darum geht, die Details der verschiedenen Erdbeben zu verarbeiten, ist dieser prozedurale Ansatz allerdings nicht immer optimal. So wollen wir das stärkste und das schwächste Erdbeben des JSON-Dokumentes herausfinden. Nun, wir befinden uns in einer Datenbank; und was liegt näher als die Nutzung von SQL? Mit der nativen SQL Funktion JSON_TABLE ist das kein Problem ... with eqdata as ( select e.id, e.title, e.mag from earthquake_json j, json_table( document, '$.features[*]' columns( id varchar2(20) path '$.id', mag number path '$.properties.mag', title varchar2(200) path '$.properties.title' ) ) e ), minmax as ( select min(e.mag) minmag, max(e.mag) maxmag from eqdata e ) select e.id, e.title, e.mag from eqdata e, minmax m where e.mag in ( m.minmag, m.maxmag ) / ID TITLE MAG ----------- ---------------------------------------------- ----- us2000d7q6 M 7.5 - 89km SSW of Porgera, Papua New Guinea 7.5 uw61366531 M -0.8 - 36km NNE of Amboy, Washington -0.8 2 rows selected. Elapsed: 00:00:01.545 Diese Art des JSON Parsings ist sehr elegant, lassen sich so doch alle Möglichkeiten von SQL, auch für JSON-Dokumente, nutzen. Man kann das natürlich auch per PL/SQL Block realisieren; das SQL-Beispiel ist aber doch sehr klarer, strukturierter, leichter wartbar und wahrscheinlich auch schneller. JSON_TABLE steht ab der Datenbankversion 12.1.0.2 zur Verfügung - auf einer 11.2-Datenbank gibt es kein JSON_TABLE. Allerdings kann APEX_JSON hier weiterhelfen: Es bietet zwar kein Äquivalent zu JSON_TABLE an, allerdings gibt es die TO_XMLTYPE-Funktion, mit der sich ein JSON nach XML wandeln lässt. Und die SQL-Funktion XMLTABLE gibt es schon seit Oracle 9.2. Das für eine 11.2 Datenbank angepasste Beispiel sieht wie folgt aus. with eqdata as ( select e.id, e.title, e.mag from earthquake_json j, xmltable( '/json/features/row' passing apex_json.to_xmltype( j.document ) columns id varchar2(20) path 'id/text()', mag number path 'properties/mag/text()', title varchar2(200) path 'properties/title/text()' ) e ), minmax as ( select min(e.mag) minmag, max(e.mag) maxmag from eqdata e ) select e.id, e.title, e.mag from eqdata e, minmax m where e.mag in ( m.minmag, m.maxmag ) / ID TITLE MAG ----------- ---------------------------------------------- ----- us2000d7q6 M 7.5 - 89km SSW of Porgera, Papua New Guinea 7.5 uw61366531 M -0.8 - 36km NNE of Amboy, Washington -0.8 2 rows selected. Elapsed: 00:00:27.152 Allerdings hat das seinen Preis. Mit etwa 27 Sekunden braucht der Vorgang wesentlich länger als JSON_TABLE mit etwa einer Sekunde. Ein großer Teil der Zeit wird für APEX_JSON.TO_XMLTYPE verbraucht, was man recht einfach nachweisen kann ... select apex_json.to_xmltype(document) from earthquake_json where id = 1; APEX_JSON.TO_XMLTYPE(DOCUMENT) ------------------------------ <json>... Elapsed: 00:00:12.711 ... und was in etwa dem Zeitverbrauch von APEX_JSON.PARSE entspricht, der weiter oben bereits festgestellt wurde. Aus diesen einfachen Tests lassen sich einfache Regeln zum Umgang mit JSON in APEX (aber auch außerhalb von APEX) ableiten ... APEX_JSON bringt JSON-Funktionen in 11.2 und 12.1.0.1-Datenbanken, hat aber seinen Preis. Da es in PL/SQL implementiert ist, ist JSON-Parsing, im Vergleich zu nativen Funktionen, recht teuer. Wenn native Funktionen zum JSON Parsing zur Verfügung stehen (also ab 12.1.0.2), sind diese eigentlich immer vorzuziehen - es ist sehr empfehlenswert, sich deren Syntax und Arbeitsweise genauer anzusehen.    Die JSON_TABLE-Funktion (12.1.0.2 oder höher) bzw. APEX_JSON.TO_XMLTYPE und XMLTYPE erlauben es, mit SQL-Queries auf JSON-Dokumenten zu arbeiten. Geht es darum, JSON-Daten als APEX-Bericht zu visualisieren oder anderweitig auszuwerten, ist die Arbeit mit SQL wesentlich eleganter als das prozedurale Arbeiten mit PL/SQL.    In eigenen Applikationen sollten Aufrufe von APEX_JSON.PARSE, je nach Anwendungsfall, nach und nach in JSON_TABLE und JSON_OBJECT_T.PARSE überführt werden.   Weiterführende Links Dokumentation zu APEX_JSON (ab APEX 5.0) https://docs.oracle.com/cd/E59726_01/doc.50/e39149/apex_json.htm#AEAPI29635   Dokumentation zu JSON_TABLE (ab Datenbank 12.1.0.2) https://docs.oracle.com/database/121/SQLRF/functions092.htm#SQLRF56973   Dokumentation zu JSON_OBJECT_T (Datenbank 12.2.0.1) https://docs.oracle.com/en/database/oracle/oracle-database/12.2/adjsn/using-PLSQL-object-types-for-JSON.html#GUID-F0561593-D0B9-44EA-9C8C-ACB6AA9474EE   Zurück zu blogs.oracle.com/apexcommunity_deutsch  

Die Arbeit mit JSON ist, auch für den APEX-Entwickler, inzwischen zum Alltag geworden. So liefern REST Services, die man aus der APEX-Anwendung heraus aufruft, typischerweise JSON zurück - auch...

Classic Reports einmal anders - mit Report Templates

Denkt man als APEX-Entwickler an einen Bericht, so hat man meist die typische, tabellarische Darstellung vor Augen. Aber das geht auch anders. Der Classic Report ist sicherlich eine der vielseitigsten Komponenten in Application Express. Der Grund dafür ist, dass die Report-Darstellung komplett templategetrieben ist. Man kann eigene Templates erstellen, aber auch aus den von Application Express bereits mitgelieferten wählen. Dieses Posting gibt einen kleinen Überblick über diese Templates - einiges davon ist aus der Universal Theme Sample Application (apex.oracle.com/ut) entnommen - dort finden Sie noch mehr Beispiele und Informationen zum Look &  Feel von APEX-Komponenten. Starten Sie mit dem ersten Berichts-Template. Navigieren Sie dazu, im Page Designer, zu den Berichtsattributen Ihres Berichts, und dann zum Abschnitt Appearance. Wählen Sie beispielsweise das Template Media List aus, speichern Sie die Änderung ab und starten Sie die Seite neu. Das ist noch nicht wirklich das gewünschte Ergebnis - man sieht deutlich, dass das Template ganz bestimmte Ergebnisspalten von der SQL-Abfrage erwartet. Noch genauer sieht man das, wenn man in die Template-Definition hineinsieht. Diese erreichen Sie übrigens bequem mit dem Klick auf die Schaltfläche > rechts neben der Auswahlliste für das Berichtstemplate. Platzhalter wie #ICON_CLASS# oder #LIST_TITLE# sind leicht zu erkennen. Mit diesen Informationen kann die SQL-Abfrage nun angepasst werden. select ename as list_title, job as list_text, 'fa fa-user' as icon_class, null as edit_link, sal as list_badge from emp Starten Sie die Seite nach dem Speichern nochmals ...  Das sieht schon besser aus. Allerdings sind zum Template noch Template Options verfügbar. Diese werden wiederum im Page Designer eingerichtet, indem Sie auf die Schaltfläche rechts von Template Options klicken. Aktivieren Sie Show Badges und Apply Theme Colors, speichern Sie, und starten Sie die Seite danach nochmals. Nur eine kleine Änderung an der SQL-Abfrage - und wenige Mausklicks - und schon haben Sie eine völlig andere Visualisierung Ihrer Daten. Das funktioniert mit den anderen Berichts-Templates analog ... Hier ein Beispiel for das Berichtstemplate Comments ... zunächst die SQL-Abfrage (die Daten der Tabelle EMP sind nicht sehr gut für dieses Template geeignet, als Beispiel reicht es jedoch aus). select ename as user_name, 'My comment is: ' || job as comment_text, hiredate as comment_date, null as user_icon, 'Comment' as actions, ' ' as attribute_1, sal as attribute_2, ' ' as attribute_3, ' ' as attribute_4 from emp Das Ergebnis sieht dann wie folgt aus. Beim Template Alert kommt es nicht nur auf die Platzhalter an, sondern vor allem auf den Inhalt des Platzhalters #ALERT_TYPE#. Zunächst die SQL-Abfrage ... select ename as alert_title, job as alert_desc, sal as alert_action, case when sal < 1000 then 'info' when sal between 2000 and 3000 then 'success' when sal between 3000 and 4000 then 'warning' else 'danger' end as alert_type from emp Das Ergebnis sieht dann wie folgt aus - eine "Berichtszeile" enthält tatsächlich nur die drei Attribute title, desc und action. Das Aussehen wird vor allem von der Spalte alert_type gesteuert - und hier ist es wichtig, die Schlüsselworte info, success, warning und danger zu kennen. Das Ergebnis sieht wie folgt aus. Das Template Badge List eignet sich gut, wenn der Report nur aus einer Zeile besteht.Liefert die SQL-Abfrage mehrere Zeilen zurück, ist es nicht so gut geeignet. Die Spaltennamen der SQL-Abfrage gehen hier direkt in die Darstellung ein. Am Beispiel ... select empno, ename, extract(year from hiredate) as hired, sal, comm, deptno from emp where rownum = 1 Man sieht, dass hier keine besonderen Platzhalter verwendet werden - die Spaltennamen können beliebig sein. Das Template stellt diese, wie folgt, direkt dar. Das Timeline Template eignet sich gut zur Darstellung zeitlich hintereinander liegender Vorgänge ... select apex_string.get_initials(ename) as user_avatar, ename as user_name, hiredate as event_date, 'fa fa-user' as event_icon, case deptno when 10 then 'is-new' when 20 then 'is-removed' when 30 then 'is-updated' when 40 then 'is-updated' end as event_status, 'Hired' as event_type, job as event_title, sal || case when comm is not null then ' - ' end || comm as event_desc from emp Das Ergebnis sieht wie folgt aus - an der Query ist schon erkennbar, dass das Template recht viele Spalten und damit viele Darstellungsoptionen unterstützt. Das Template Search Results eignet sich sehr gut im Zusammenspiel mit einem Suchfeld. Die folgende SQL-Abfrage liefert Spalten für dieses Template zurück. select ename search_title, job search_desc, null search_link, 'Sal' label_01, sal value_01, 'Hiredate' label_02, hiredate value_02, 'Comm' label_03, comm value_03, 'Deptno' label_04, deptno value_04 from emp where instr(ename||job, :P10_SEARCH)>0 or :P10_SEARCH is null Natürlich wird diese Abfrage bei großen Datenmengen langsam sein; hier könnte bspw. Oracle Text als Suchtechnologie in Betracht kommen. Das Ergebnis wird in etwa wie folgt aussehen. Zum Abschluss sei das Template Cards erwähnt. Die Art der Darstellung findet man recht oft im Internet - und mit diesem Template wird es wirklich einfach, Tabellendaten auf die gleiche Art und Weise zu visualisieren ... Zunächst zur SQL-Abfrage ... select ename card_title, apex_string.get_initials(ename) card_initials, job card_text, hiredate card_subtext, 'f?p=...' card_link from emp Das Ergebnis sieht zunächst so aus ... Das Template Cards bietet viele Template Options an. So lassen sich Icons oder Initialen für eine Karte einschalten; man kann die Karten in den Farben des Theme darstellen und steuern, wieviele Karten pro Zeile dargestellt werden sollen. Man sieht sehr schön, dass man das Aussehen eines klassischen Berichts sehr schön steuern kann - und das ganz ohne HTML- und CSS-Kenntnisse - es reicht aus, die SQL-Abfrage so anzupassen, dass das Berichts-Template mit den richtigen Ergebnisspalten versorgt wird. Probieren Sie es einfach mal aus. Zurück zur Community-Seite      

Denkt man als APEX-Entwickler an einen Bericht, so hat man meist die typische, tabellarische Darstellung vor Augen. Aber das geht auch anders. Der Classic Report ist sicherlich eine der vielseitigsten...

Application Express 5.2 Early Adopter 1 ist da

Seit dem 21. Dezember 2017 steht das Early Adopter Release von Application Express 5.2 zum Testen bereit. Wie immer findet das Early Adopter Programm online statt - unter apexea.oracle.com kann man sich einen Workspace beantragen und mit dem Testen der neuen Features beginnen.  Im Mittelpunkt des neuen Release stehen diesmal: Sobald Sie eine neue Applikation erstellen, nutzen Sie bereits den neuen Create Application Wizard; im Gegensatz zu früheren Versionen lassen sich so - aus dem Stand - wesentlich mächtigere Anwendungen erstellen. Unterstützung für REST Enabled SQL - erzeugen Sie eine Application Express Komponente (einen Report oder Chart) auf Basis einer SQL-Abfrage, die auf einer anderen Datenbank ausgeführt wird - und das ohne Database Link. Die Kommunikation findet über eine spezielle REST Schnittstelle statt, die von Oracle REST Data Services bereitgestellt wird. Völlig neue Unterstützung für RESTful Services: Erzeugen Sie eine APEX-Komponente (einen Report, Kalender oder Chart) direkt auf einen externen RESTful Service. Alle Kommunikation und alles JSON oder XML-Parsing wird von Application Express übernommen. Neue Oracle JET Charts: Gantt und Box Plot Charts stehen nun zusätzlich zur Verfügung; auch für REST Services oder REST Enabled SQL. Darüber hinaus gibt es, wie immer, viele neue Funktionen und Features im Detail. Auf der englischen Ausgabe dieses Blogs steht bereits ein Tutorial für REST Enabled SQL zur Verfügung - und weitere werden folgen. Am besten erzeugen Sie sich gleich einen Workspace - und auf geht's!

Seit dem 21. Dezember 2017 steht das Early Adopter Release von Application Express 5.2 zum Testen bereit. Wie immer findet das Early Adopter Programm online statt - unter apexea.oracle.com kann man...

APEX

So nutzen Sie den Rich Text Editor voll aus!

Das APEX Element Rich Text Editor erlaubt es, im Gegensatz zu einem normalen Textbereich, formatierten Text zu bearbeiten. Der Text wird dann als HTML-Code an Application Express gesendet und typischerweise auch als solcher in Tabellen abgelegt. Das technische Fundament des Rich Text Editors ist die Javascript Bibliothek CKEditor. Zwar sind die im Page Designer angebotenen Konfigurationsmöglichkeiten recht überschaubar, doch bietet der CKEditor eine umfangreiche API zur Konfiguration an - und diese lässt sich ab Application Express 5.1 sehr leicht nutzen. Navigieren Sie dazu, im Page Designer, zu Ihrem Rich Text Editor Element und dort zum Abschnitt Advanced. Das Attribute Initialization Javascript Code ist der Einstieg in die Konfigurationsmöglichkeiten des CKEditor. Typischerweise wird dort eine Funktion wie folgt hinterlegt. function ( configObject ) { configObject.{attribute-name-1} = {attribute-value-1}; configObject.{attribute-name-2} = {attribute-value-2}; : return configObject; }   Application Express ruft diese Funktion kurz vor der Initialisierung des Editors auf und übergibt das Standard-Konfigurations-Objekt. Dieses können Sie nun in Ihrem Javascript-Code verändern und an Application Express zurückgeben. Der CKEditor wird dann mit der von Ihnen veränderten Konfiguration initialisiert. Einen Überblick über die Möglichkeiten finden Sie in der API-Dokumentation des CKEditor. Machen Sie ein paar erste Tests: Die folgende Javascript-Funktion setzt die Grundfarbe für den Editor auf eine Art Grün, und die Möglichkeit, die Größe des Editors zu verändern wird abgeschaltet. Danach starten Sie die Seite, um sich das Ergebnis anzusehen. function ( configObject ) { configObject.uiColor = "#AADC6E"; configObject.resize_enabled = false; return configObject; }   CKEditor bietet sehr viele Varianten an, wie das HTML generiert werden soll. So führt die Return-Taste standardmäßig dazu, dass mit einem <p> Tag ein neuer Absatz produziert wird. Das kann geändert werden - es soll ein einfacher Zeilenumbruch mit dem Tag <br> erzeugt werden. function ( configObject ) { configObject.enterMode = 2; configObject.uiColor = "#AADC6E"; configObject.resize_enabled = false; return configObject; } Wem die von Application Express angebotenen Varianten der Toolbar nicht gefallen, kann sich eine eigene zusammenstellen. function ( configObject ) { configObject.enterMode = 2; configObject.uiColor = "#AADC6E"; configObject.resize_enabled = false; configObject.toolbar = [ ['Link','Unlink','Anchor'], ['Image','Table','HorizontalRule','Smiley','SpecialChar'], '/', ['Bold','Italic','Underline','Strike','-','Subscript','Superscript','-', 'RemoveFormat'], ['NumberedList','BulletedList','-','Outdent','Indent','Blockquote'] ]; return configObject; } Die API-Dokumentation des CKEditor bietet noch viele weitere Einstellungsmöglichkeiten an. Am besten ist es sicherlich, sich dafür einmal etwas Zeit zu nehmen - als Ergebnis bekommt man einen Rich Text Editor nach Maß - in der APEX-Anwendung. Doch das ist noch nicht alles. Kombiniert man diese Konfigurationen geschickt mit zusätzlichen APEX-Seiten oder RESTful services, so lässt der Rich Text Editor sich sehr elegant mit einem Bilder-Upload versehen oder man kann ich "responsive" gestalten. Wie das geht, erfahren Sie im vollständigen Community Tipp.    

Das APEX Element Rich Text Editor erlaubt es, im Gegensatz zu einem normalen Textbereich, formatierten Text zu bearbeiten. Der Text wird dann als HTML-Code an Application Express gesendet...

DATE, TIMESTAMP und Formatmasken

Mit DATE oder TIMESTAMP Datentypen wird ständig gearbeitet; sie kommen in jedem Datenmodell vor und folglich muss jede APEX-Anwendung damit umgehen können. Dieser Tipp enthält einige Informationen zum Umgang mit diesen Datentypen - und worauf man als APEX-Entwickler achten sollte. DATE oder TIMESTAMP? Die Oracle-Datenbank bietet vier Datentypen zum Speichern von Zeitstempeln an: DATE ist der älteste Datentyp zum Speichern von Zeitstempeln in der Datenbank. Anders als der Name nahelegt, speichert DATE immer sowohl das Datum als auch die Uhrzeit ab. Intern werden ganz konkrete Werte für Jahr, Monat, Tag, Stunde, Minute und Sekunde abgelegt. Die SQL-Funktion SYSDATE gibt den aktuellen Zeitstempel zurück. SQL> alter session set nls_date_format='YYYY-MM-DD HH24:MI:SS'; Session geändert. SQL> select sysdate, dump(sysdate) as date_bytes from dual; SYSDATE DATE_BYTES -------------------- ----------------------------------- 2017-11-23 23:41:08 Typ=13 Len=8: 225,7,11,23,23,41,8,0 TIMESTAMP erweitert den DATE-Datentypen um Sekundenbruchteile. Das interne Speicherformat für den TIMESTAMP sieht auch die Zeitzone vor, um diese aktiv nutzen zu können, braucht es jedoch die nachfolgend erläuterten Datentypen TIMESTAMP WITH TIME ZONE und TIMESTAMP WITH LOCAL TIME ZONE. Wie man am folgenden Beispiel sehen kann, liefert LOCALTIMESTAMP den aktuellen Zeitstempel als TIMESTAMP-Datentyp zurück. SQL> alter session set nls_timestamp_format='YYYY-MM-DD HH24:MI:SS.FF6'; Session geändert. SQL> select localtimestamp, dump(localtimestamp) ts_bytes from dual; LOCALTIMESTAMP TS_BYTES --------------------------- --------------------------------------------------------------------- 2017-11-24 08:45:41.434175 Typ=187 Len=20: 225,7,11,24,8,45,41,0,24,252,224,25,1,0,3,0,127,1,0,0 TIMESTAMP WITH TIME ZONE unterstützt die Zeitzone, und zwar explizit. Die Zeitzonen-Information wird beim Erzeugen einer Instanz explizit übergeben und in der Ausgabe sollte sie stets enthalten sein (richtige Formatmaske verwenden). Gibt man einen TIMESTAMP WITH TIME ZONE ohne Zeitzonen-Information aus, so ist die Information unvollständig. Es finden keine implizite Umrechnungen zwischen Zeitzonen statt; dem Entwickler stehen SQL-Funktionen dafür zur Verfügung. Instanz. Um den aktuellen Zeitstempel zu bekommen, verwendet man die SQL-Funktionen SYSTIMESTAMP oderCURRENT_TIMESTAMP. Ersterer liefert die Systemzeit in der Datenbank-Zeitzone, letzterer in der Session Zeitzone zurück. Im folgenden ein Beispiel für SYSTIMESTAMP. SQL> alter session set nls_timestamp_tz_format='YYYY-MM-DD HH24:MI:SS.FF6 TZR'; Session geändert. SQL> select systimestamp from dual; SYSTIMESTAMP ---------------------------------- 2017-11-23 23:57:04.609608 -08:00 TIMESTAMP WITH LOCAL TIME ZONE unterstützt die Zeitzone implizit. Eine Instanz wird ohne Angabe einer Zeitzone erzeugt; es wird die Session-Zeitzone angenommen; die Ausgabe erfolgt ebenfalls in der Session Zeitzone. Oracle-Intern werden die Instanzen auf die Datenbank-Zeitzone normalisiert.  Das folgende Beispiel zeigt, wie TIMESTAMP WITH LOCAL TIME ZONE funktioniert. Zuerst wird eine Tabelle erzeugt und darin der aktuelle Zeitstempel gespeichert. SYSTIMESTAMP liefert zwar einen TIMESTAMP WITH TIME ZONE zurück, dieser wird aber automatisch auf TIMESTAMP WITH LOCAL TIME ZONE konviertiert. Wird die Tabelle danach selektiert, so wird stets auf die aktuelle Session-Zeitzone umgerechnet. SQL> create table zeitstempel( ts timestamp with local time zone ); Table created. SQL> insert into zeitstempel values ( systimestamp ); 1 row created. SQL> alter session set time_zone='Europe/Berlin'; Session altered. SQL> select * from zeitstempel; TS --------------------------------------------------------------------------- 2017-11-24 12:55:39.761283 SQL> alter session set time_zone='EST'; Session altered. SQL> select * from zeitstempel; TS --------------------------------------------------------------------------- 2017-11-24 06:55:39.761283 Formatmasken Ob man mit einer Anwendung oder einem Werkzeug wie SQL Developer oder SQLPlus arbeitet; jede Ausgabe einer DATE- oder TIMESTAMP Instanz ist eine Konvertierung nach VARCHAR2. Die macht man entweder mit TO_CHAR explizit - oder die Datenbank macht es implizit. Und hierfür sind die NLS-Datumsformatmasken wichtig. Für die verschiedenen Datentypen gibt es verschiedene NLS-Parameter für die Default-Formatmaske. Weiterlesen ...

Mit DATE oder TIMESTAMP Datentypen wird ständig gearbeitet; sie kommen in jedem Datenmodell vor und folglich muss jede APEX-Anwendung damit umgehen können. Dieser Tipp enthält einige Informationen zum...

APEX

Interactive Grid ohne Tabelle? Das geht!

Mit dem in Application Express 5.1 neu eingeführten Interactive Grid haben die meisten Entwickler sicherlich schon experimentiert - und tatsächlich ist es damit sehr einfach, ein tabellarisches Formular auf eine Tabelle oder View zu erzeugen. Einfach die SQL-Abfrage hinterlegen; das Interactive Grid in den Attributen editierbar machen und ... fertig. Allerdings kann man mit dem Interactive Grid wesentlich mehr machen als nur Tabellen zu editieren.  So lassen sich die im Raster angezeigten Daten mit der SQL-Abfrage beliebig generieren - die Eingaben der Nutzer können nach dem Page Submit mit PL/SQL verarbeitet werden. So lassen sich auch Seiten erzeugen, bei denen der Nutzer Daten from Scratch in das Raster eingibt; danach kann eine völlig freie Verarbeitung stattfinden - ganz unabhängig von einer Tabelle oder View. Von hier aus stehen eigentlich alle Möglichkeiten offen. Sie können die eingegebenen Daten in PL/SQL Prozeduren verarbeiten und am Ende in Tabellen ablegen; Sie können damit externe Web Services aufrufen oder beliebige andere Dinge tun; das interactive Grid ist nun nicht mehr allein ein Formular für eine Tabelle - es kann zur Datenerfassung für alle möglichen Zwecke dienen. Unser aktueller Community-Tipp zeigt, anhand eines Beispiels, wie Sie ein solches Interactive Grid einrichten und die PL/SQL Verarbeitung konfigurieren. Schauen Sie mal rein!  

Mit dem in Application Express 5.1 neu eingeführten Interactive Grid haben die meisten Entwickler sicherlich schon experimentiert - und tatsächlich ist es damit sehr einfach, ein...

APEX

Seitenelemente und Javascript: Mehr als $s und $v ...

Nahezu jede APEX-Anwendung enthält mehr oder weniger Javascript Code. Vor allem Formulare lassen sich mit Hilfe von Dynamic Actions und Javascript Code wesentlich nutzerfreundlicher gestalten. Wenn es darum geht, Formularelemente mit Javascript zu verändern oder zu steuern, kennen viele APEX-Entwickler die seit Jahren vorhandenen Javascript-Funktionen $s, $v und $x. Mit $s setzt man den Wert eines Formularelements, mit $v liest man ihn aus und $x stellt fest, ob ein APEX-Element mit dem übergebenen Namen überhaupt auf der Seite existiert. Diese Funktionen existieren schon sehr lange in Application Express - und sie sind auch sehr beliebt, zumal die Namen so schön kurz und damit schnell getippt sind. Allerdings sind diese Abkürzungen; die eigentlichen Funktionen befinden sich im Javascript Namensraum "apex.item". Und dieser enthält mehr Funktionen, als Sie vielleicht denken. Diese Funktionen sind ein hervorragendes Handwerkszeug, um Formulare interaktiver und nutzerfreundlicher zu gestalten. Greifen Sie per Javascript auf den Elementwerte zu, egal ob der Typ ein Rich Text Editor, ein Date Picker, eine Auswahlliste oder ein einfaches Textfeld ist. Aktivieren, deaktivieren oder verstecken Sie ein Formularelement mit einem einfachen Javascript Aufruf. Sie können sogar feststellen, ob der Anwender den Wert bereits geändert hat oder nicht, was besonders nützlich ist, wenn man entscheiden muss, ob ein AJAX-Request zum Server nun nötig ist oder nicht. Vorher sei allerdings die grundsätzliche Empfehlung zu Javascript-Code in APEX-Anwendungen nochmals erwähnt: Wo es möglich ist, sollte deklarativ mit Dynamic Actions gearbeitet und "manueller" Javascript-Code vermieden werden. Geht es also darum, ein Seitenelement zu verstecken oder anzuzeigen, so ist eine Dynamic Action mit Show oder Hide Aktionen meist die bessere Wahl. Nur dort, wo Dynamic Actions nicht möglich oder nicht angemessen sind, sollte eigener Javascript-Code zum Einsatz kommen. Lesen Sie in unserem aktuellen Tipp, welche Funktionen es im Namensraum apex.item gibt und wie Sie diese nutzen können.

Nahezu jede APEX-Anwendung enthält mehr oder weniger Javascript Code. Vor allem Formulare lassen sich mit Hilfe von Dynamic Actions und Javascript Code wesentlich nutzerfreundlicher gestalten. Wenn es...

Oracle Datenbank (mit APEX) auf Docker - so geht's!

Die Oracle Datenbank ist traditionell auf vielen Plattformen verfügbar. Neben dem Betriebssystem als Plattform, gibt es auch verschiedene gängige Virtualisierungslösungen; und in jüngster Zeit wird die Container-Technologie Docker mehr und mehr populär. Docker unterscheidet sich vom Konzept der virtuellen Maschinen dadurch, dass nicht immer ein eigenes Betriebssystem aufgebaut wird. Vielmehr basieren mehrere Docker Container auf gemeinsam genutzten Ressourcen, so dass die Möglichkeiten des Host-Systems wesentlich besser ausgenutzt werden. Vor allem Entwickler können Docker hervorragend nutzen, um eine Application-Express-Umgebung nicht nur schnell bereitzustellen, sondern auch (für Tests) zu klonen oder einfach zurückzusetzen. In allen Fällen ist wird nur der Docker-Container mit der Datenbank und Application Express - und nicht etwa ein ganzes Betriebssystem (mit allen Hintergrundprozessen) gestartet. Ralf Durben von der ORACLE Deutschland B.V. & Co KG hat in diesem Community Tipp zusammengestellt, wie man ein Docker Image mit der Oracle-Datenbank erstellt und nutzt. Es wird nicht nur darauf eingegangen, wie man die fertigen Oracle-Datenbank Images aus dem Docker Store verwendet, sondern auch, wie man sich selbst sein eigenes, individuelles Docker-Image erzeugen kann. Dieser Tipp ist ein Muss (nicht nur) für jeden Application Express Entwickler, der hin und wieder eine Entwicklungs- oder Testumgebung aufsetzen muss - gleich reinschauen!

Die Oracle Datenbank ist traditionell auf vielen Plattformen verfügbar. Neben dem Betriebssystem als Plattform, gibt es auch verschiedene gängige Virtualisierungslösungen; und in jüngster Zeit wird...

APEX

REST Services und Application Express: Filtering

Die Anforderung, mit REST Services zu arbeiten, haben auch Application Express-Entwickler mehr und mehr auf dem Schreibtisch. Zwar können Web Service Referenzen für REST Services in den Gemeinsamen Komponenten eingerichtet werden; die Unterstützung ist jedoch recht limitiert: So muss die JSON-Antwort, die der REST Service sendet, typischerweise manuell geparst und verarbeitet werden. In den vergangenen Wochen konnten Sie bereits mehrere Tipps zum Thema im Rahmen der APEX-Community lesen: So haben Sie erfahren, wie Sie, mit der seit Version 5.1 vorhandenen neuen Packaged Applications REST Client Assistant ... SQL und PL/SQL Code zum Zugriff auf den REST Service und zum JSON-Parsing erzeugen größere Datenmengen seitenweise vom REST Service abrufen können ein Wallet konfigurieren, um problemlos auf SSL-gesicherte Services zuzugreifen​ In den bisherigen Tipps wurden stets alle Daten des REST Service abgerufen - entweder alle auf einmal, oder seitenweise. In der Praxis sind aber oft nur Teilmengen der Daten gefragt: man möchte die Ergebnismenge mit einem Filter einschränken. "Das ist ja einfach" dürfte nun der allererste Gedanke sein - schließlich findet das JSON-Parsing mit SQL und den SQL-Funktionen XMLTABLE oder JSON_TABLE statt. Beide stellen die Daten so bereit, als ob sie aus einer Tabelle kämen - in der WHERE-Klausel können nun also beliebige SQL-Filter verwendet werden.  Problematisch wird dies allerdings, wenn die Antwort des REST Service potenziell sehr große Datenmengen zurück liefert - man stelle sich vor, ohne Filter würde eine sechs- oder gar siebenstellige Anzahl an Zeilen zurückgegeben. Viele REST-Services geben die Daten dann seitenweise ab; das wurde im Community-Tipp Größere Datenmengen seitenweise vom REST Service abrufen näher betrachtet. Ginge man wie oben vor, bedeutet das, dass man zunächst alle Daten, über mehrere Seiten hinweg, vom REST Service abrufen würde. Anschließend würde man, per SQL-Filter, das meiste davon wieder verwerfen. Es ist klar, dass dies für den kleinere Services (unter 1.000 Zeilen) noch in Ordnung geht, bei größeren Datenmengen ist diese Vorgehensweise allerdings problematisch. Lesen Sie in unserem aktuellen Tipp, wie Sie die Ergebnismenge eines REST Service filtern können - wann Sie problemlos mit SQL arbeiten und wann Sie eine Filter-Syntax, am Beispiel von Oracle REST Data Services, nutzen können.   

Die Anforderung, mit REST Services zu arbeiten, haben auch Application Express-Entwickler mehr und mehr auf dem Schreibtisch. Zwar können Web Service Referenzen für REST Services in den Gemeinsamen...

APEX

APEX, HTTPS, Zertifikate und das Oracle Wallet

Die Artikelreihe zum Thema "Application Express und REST Services" pausiert für eine Ausgabe - dafür schieben wir einen Tipp ein, der zumindest mit dem Thema verwandt ist. Es geht HTTPS-Requests einer APEX-Anwendung. Zunächst funktioniert ein solcher ganz genauso wie ein HTTP-Aufruf - man verwendet einfach die HTTPS-URL. Zum Testen nehmen wir die HTTPS-URL des USGS (US Geological Survey), die bereits im ersten Community Tipp zum Thema REST Services und Application Express ein Thema war. select apex_web_service.make_rest_request( p_url => 'https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_day.geojson', p_http_method => 'GET' ) from dual; Der erste Versuch schlägt jedoch fast immer fehl ... Fehlerbericht - ORA-29273: HTTP-Anforderung nicht erfolgreich ORA-29024: Zertifikatvalidierung nicht erfolgreich ORA-06512: in "SYS.UTL_HTTP", Zeile 380 Blogs und Diskussionsforen sind voll von Fragen, was bei dieser Fehlermeldung zu tun ist - in diesem Tipp erfahren Sie es: Die Fehlermeldung besagt, dass die Datenbank das SSL-Zertifikat, mit dem sich der Webserver ausgewiesen hat, nicht validieren konnte. Für diese Validierung muss ein Oracle Wallet eingerichtet und dann in Application Express konfiguriert werden. Vorab jedoch ein paar einfache, erklärende Worte zum Thema SSL-Zertifikate (die Erklärungen sind sicherlich nicht vollständig, für das Verständnis dieses Tipps sollten sie jedoch hinreichend sein): Wenn Sie eine HTTPS-Verbindung zu einem Server aufbauen, weist dieser sich mit einem SSL-Zertifikat aus. Ein solches Zertifikat funktioniert wie eine Art Personalausweis: es weist aus, dass der Server, der auf die Anfrage antwortet, tatsächlich der ist, den man mit der URL angesprochen hat. Nun stellt sich natürlich die Frage, ob ein solcher "Ausweis" auch echt ist - und diese Echtheit wird von der Certificate Authority zertifiziert: das SSL-Zertifikat des Webservers wird mit einem Zertifikat der CA "unterschrieben". Das Zertifikat der CA kann nun wiederum von einer weiteren CA "unterschrieben" werden - womit das nächste Zertifikat ins Spiel kommt. Diese Kette (Certificate Chain) kann auch noch länger werden; irgendwo muss aber ein Ende sein - und das ist ein CA-Zertifikat, dem einfach vertraut wird, auch wenn es nicht "unterschrieben" ist (Trusted Certificate). In einem Web-Browser wie Firefox, Chrome oder anderen sind alle gängigen CA-Zertifikate vorinstalliert - so kann man bequem im Internet surfen. Die Oracle-Datenbank verwaltet solche Zertifikate in einem Wallet - dieses muss jedoch zuerst erzeugt werden, es ist zu Beginn leer und die nötigen CA-Zertifikate müssen hinzugefügt werden. Dieser Artikel beschreibt, wie man ein Oracle Wallet erzeugt, wie man die nötigen CA-Zertifikate in dieses lädt und schließlich, wie man das Wallet zentral in Application Express einrichtet, so dass es in allen Workspaces und Anwendungen genutzt werden kann (ganzen Artikel lesen).

Die Artikelreihe zum Thema "Application Express und REST Services" pausiert für eine Ausgabe - dafür schieben wir einen Tipp ein, der zumindest mit dem Thema verwandt ist. Es geht HTTPS-Requests einer...

APEX

REST und APEX 5.1: Pagination

Die Anforderung, mit REST Services zu arbeiten, haben auch Application Express-Entwickler mehr und mehr auf dem Schreibtisch. Zwar können Web Service Referenzen für REST Services in den Gemeinsamen Komponenten eingerichtet werden; die Unterstützung ist jedoch recht limitiert: So muss die JSON-Antwort, die der REST Service sendet, typischerweise manuell geparst und verarbeitet werden. Oracle Application Express enthält die Packaged Application REST Client Assistant, welche dem Entwickler dabei hilft, REST Services in die APEX-Anwendung zu integrieren.   Im ersten Teil des Artikels (REST Services und Application Express 5.1 - Teil 1) wurde vorgestellt, wie man mit dem REST Client Assistant PL/SQL und SQL Code erzeugen kann, so dass die Daten des REST Service in einer Application Express-Anwendung dargestellt oder verarbeitet werden können. In diesem Artikel machen wir den nächsten Schritt: Viele REST-Services liefern große Ergebnismengen nicht komplett, sondern seitenweise aus. Ein Beispiel für einen REST Service, der mit größeren Datenmengen arbeitet, lässt sich schnell in der eigenen Umgebung erstellen. Sie brauchen dazu lediglich eine Tabelle mit mehr als den 14 Zeilen, wie sie die wohlbekannte EMP Tabelle enthält. Einige hundert sollten es schon sein. Im Zweifelsfalle generieren Sie sich einfach eine solche. Den REST Service stellen Sie dann mit Hilfe von Oracle REST Data Services (ORDS) bereit. Lesen Sie in unserem aktuellen Tipp, wie Sie einen solchen REST Service in Ihre APEX-Anwendung nicht nur einfach integrieren, sondern auch das Vor- und Zurückblättern in der Ergebnismenge unterstützen können. Blättert der Endbenutzer auf der APEX-Seite vor, so wird die nächste Seite vom REST-Service abgerufen, blättert er zurück, so wird auch diese Seite vom REST Service geholt. Lassen Sie sich schließlich vom REST Client Assistant eine Table Function generieren, welche die gesamte Ergebnismenge für Sie abruft.  

Die Anforderung, mit REST Services zu arbeiten, haben auch Application Express-Entwickler mehr und mehr auf dem Schreibtisch. Zwar können Web Service Referenzen für REST Services in den Gemeinsamen Ko...

APEX

DOAG2016 und mehr: Events rund um Application Express

Ab dem 15. November trifft sich die deutschsprachige Oracle Community zur alljährlichen DOAG-Konferenz in Nürnberg. Application Express ist dort, wie in den Jahren zuvor, ein wichtiges Thema. Im Konferenzplaner bringt die einfache Suche nach APEX bereits 46 Treffer, darunter Vorträge aus dem APEX Entwicklerteam von Joel Kallman oder Patrick Wolf. Aber auch bekannte Sprecher wie Dietmar Aust, Denes Kubicek, Tobias Arnhold und andere sind dabei. In die DOAG-Konferenz eingebettet ist der Oracle Cloud Day, eine eintägige Oracle-Veranstaltung am 15. November rund um das Thema Cloud Computing - hier können APEX-Interessierte erfahren, wie sich Daten aller Art und aus unterschiedlichsten Quellen, mit APEX zusammenfassen und visualisieren lassen. Und das sowohl on Premise als auch in der Cloud.  Besonders zu erwähnen ist die 3. APEX Open Mic Night, die am Mittwoch, dem 16. November, im Rahmen der DOAG-Konferenz um 20:30 im Raum "Istanbul" stattfinden wird. Fünf Minuten Redezeit erhalten alle Teilnehmer, um der Gruppe eigene, spannende APEX-Projekte zu präsentieren und Erfahrungen auszutauschen. Bier und Fingerfood stehen bereit – einzige Voraussetzung: gute Laune. Auch nach der DOAG-Konferenz gibt es Gelegenheiten zum Informationsaustausch. So treffen sich die APEX-Meetup-Gruppen in München und Frankfurt am Freitag, dem 18. November. Das ist besonders für diejenigen, die nicht an der DOAG Konferenz teilnehmen können, eine gute Gelegenheit zum Austausch und zur Diskussion. Für 2017 kündigen sich die ersten Veranstaltungen bereits an. Im Januar 2017 gibt es eine Roadshow zum Thema Anwendungsmodernisierung mit Application Express.  Erfahren Sie von den Experten von Oracle und der MT AG in München, Frankfurt, Ratingen oder Hamburg, wie sich "Legacy" Anwendungen mit APEX modernisieren lassen. Dabei gibt es natürlich auch Informationen zum neuen Release 5.1 - die Veranstaltung ist kostenlos, also am besten gleich anmelden. Und schließlich steht für Mai 2017 die dritte APEX Connect Konferenz auf der Agenda. Wie im Vorjahr wird sie in Berlin stattfinden und das Treffen für alle (nicht nur) deutschsprachigen APEX-Entwickler sein. Derzeit läuft noch der Call for Papers. Bis zum 21. November können noch Vorträge eingereicht werden. Das lohnt sich: Vortragende haben freien Eintritt zur Konferenz.

Ab dem 15. Novembertrifft sich die deutschsprachige Oracle Community zur alljährlichen DOAG-Konferenz in Nürnberg. Application Express ist dort, wie in den Jahren zuvor, ein wichtiges Thema. Im...

APEX

Veranstaltungen (nicht nur) für APEX-Entwickler: Frühjahr 2016

Im Frühjahr 2016 finden einige, nicht nur für APEX-Entwickler interessante, Veranstaltungen rund um Entwicklerthemen statt. Am 7. und 8. März findet in Rotterdam die 7. APEX World Konferenz - mit einem umfangreichen englischen Vortragsprogramm statt. Die APEX World hat sich in den letzten Jahren zu einer recht großen Konferenz entwickelt - allein im letzten Jahr 2015 nahmen etwa 400 Personen teil. Auch das APEX-Entwicklerteam ist mit Marc Sewtz, Hilary Farrell und Anthony Rayner vor Ort.https://www.ogh.nl/apex/ Für die APEX Startup Workshops am 15. und 16. März in Ratingen und Dreieich sind noch einige Plätze frei. Diese Workshops richten sich an APEX-Einsteiger: An einem Tag erfährt man alles, was man zum Start mit APEX wissen muss. Vom 26. bis zum 28. April findet die zweite APEX Connect Konferenz statt. Nach dem großen Erfolg im letzten Jahr geht die APEX Connect dieses Mal über drei Tage - ein Tag davon steht ganz im Zeichen von PL/SQL. Die Agenda ist wiederum hochkarätig und mit Top-Sprechern aus der deutschen und internationalen APEX-Community besetzt. So kann man sich jetzt schon auf die Keynote von Mike Hichwa, dem "Vater" und APEX-Entwicker der ersten Stunde, freuen. Der Frühbuchertarif läuft bis zum 15. März - also am besten zeitnah anmelden.http://apex.doag.org Während der APEX Connect werden die Gewinner der APEX Dashboard Competition gekürt. In diesem Wettbewerb ist ein Dataset vorgegeben und die Aufgabe ist es, ein modernes, gut aussehendes und am besten auch "Cooles" Dashboard zu erstellen. Wer bis zum Einsendeschluß am 1. April ein APEX-Dashboard abgibt, hat die Chance auf ein Apple iPad Pro!http://apexcompetition.org Wer am 23. und 24. Februar Zeit hat, kann auf dem DOAG DevCamp an einer etwas anderen Entwicklerkonferenz teilnehmen. Eine festgelegte Agenda mit Vorträgen gibt es nicht - diese wird von den Teilnehmern zu Beginn der Veranstaltung spontan diskutiert und zusammengestellt. Da es dann auch keine vorbereiteten Folien gibt, ergeben sich meist sehr lebhafte und ergiebige Diskussionen und man erhält völlig neue Impulse und Eindrücke. Das DOAG Devcamp findet in Bonn statt - Anmeldungen sind noch möglich.http://barcamp.doag.org Nicht zu vergessen sei die Online-Reihe Modern Application Development mit Oracle-Technologie, die seit Januar und noch bis zum März läuft. Jede Woche wird eine neue Online-Session zu modernen Entwicklertechnologien wie JSON, REST, Node.js, Document Stores, NoSQL-Datenbanken und anderen freigegeben. Einige Sessions sind schon öffentlich - einfach ein wenig Zeit nehmen und reinschauen.

Im Frühjahr 2016 finden einige, nicht nur für APEX-Entwickler interessante, Veranstaltungen rund um Entwicklerthemen statt. Am 7. und 8. März findet in Rotterdam die 7. APEX World Konferenz - mit einem...

APEX

#apexsummer15 - Das wird ein Sommer!

APEX 5.0 ist freigegeben und steht zum Download bereit - und im Sommer 2015 haben wir eine Menge damit vor. Im Juni und Juli gibt es nicht nur die DOAG-Konferenz APEX Connect, sondern, zusätzlich noch, zwei besondere Leckerbissen: Im Workshop APEX Design 2015 mit Marc Sewtz und Shakeeb Rahman erfahrt Ihr alles, was man zum Universal Theme wissen muss. APEX Design findet, nach der DOAG APEX Connect, in Düsseldorf und München statt und ist eine der wenigen Gelegenheiten, direkten Kontakt zu den APEX-Entwicklern zu bekommen. Die Teilnahme ist kostenlos - die Anzahl der Plätze begrenzt. Meldet Euch also gleich an!http://tinyurl.com/orclapexdesign Im Juli findet die APEX 5.0 Summer School statt - in acht Webseminaren erfahren Sie von den bekannten Experten der deutschsprachigen APEX Community alles, was man über APEX 5.0 wissen muss. Peter Raganitsch wird den Page Designer vorstellen, Christian Rokitta das Universal Theme. Erleben Sie darüber hinaus Denes Kubicek, Dietmar Aust und andere. Die Teilnahme ist auch hier kostenlos - ein Muss für jeden APEX-Entwickler. http://tinyurl.com/orclapexsummerschool Und nicht vergessen: am 9. und 10. Juni gibt es zwei Tage lang "APEX Pur" in Düsseldorf: auf der DOAG APEX Connect 2015. Übrigens: am 8. Mai endet der Frühbuchertarif - mit der Anmeldung sollte man also nicht mehr allzu lange warten. Der APEX Sommer 2015 findet auch auf Twitter statt - benutzt bei Diskussionen den Hashtag #apexsummer15: Community lebt vom Austausch. ... der Sommer wird heiß - #letswreckthistogether.

APEX 5.0 ist freigegeben und steht zum Download bereit - und im Sommer 2015 haben wir eine Menge damit vor. Im Juni und Juli gibt es nicht nur die DOAG-Konferenz APEX Connect, sondern, zusätzlich...

APEX

Dynamic Actions 'by example'

Dynamic Actions erlauben dem APEX-Entwickler, Javascript-Aktivitäten deklarativ in der APEX-Anwendung zu hinterlegen. Wer Javascript in seiner APEX-Anwendung verwendet (und wer tut das nicht?), sollte auch Dynamic Actions nutzen - die Möglichkeiten sind vielfältig. Partial Page Refreshes Setzen von Formularelementen - ohne erneutes Laden der Seite Aktualisierung einer Seite im Hintergrund Client Side Validations Die Basis für das alles ist Javascript, was an sich gar nicht so schwer ist. Worauf es aber ankommt, ist die strukturierte und wartbare Implementierung des Javascript Code - und genau hier setzen die Dynamic Actions an. Jürgen Schuster hat eine Beispielanwendung für Dynamic Actions erstellt und diese online verfügbar (dynamic-actions.com) gemacht. Die Anwendung bietet unter anderem das Speichern von Eingaben ohne Page Submit, Hover-Effekte in interaktiven Berichten, Highlighting von Berichtszeilen nach einer Ändernung und vieles mehr. Schauen Sie einfach mal rein und erleben Sie, was möglich ist. Die Applikation ist frei zugänglich (Open Door Credentials). Die Schaltfläche What can I do on this page? (oben rechts) beschreibt, was die jeweilige Seite demonstriert. Wer interessiert ist, kann die Applikation von Jürgen Schuster bekommen.

Dynamic Actions erlauben dem APEX-Entwickler, Javascript-Aktivitäten deklarativ in der APEX-Anwendung zu hinterlegen. Wer Javascript in seiner APEX-Anwendung verwendet (und wer tut das nicht?), sollte...

APEX

APEX und PL/SQL auf der DOAG2014

Alle Jahre im November ist es wieder soweit: Die Oracle Community trifft sich in Nürnberg zur DOAG Jahreskonferenz - diesmal kurz DOAG2014. Auch die Themen APEX und PL/SQL sind wieder mit zahlreichen Vorträgen vertreten - so stehen fast 50 Vorträge zum Thema APEX im Programm. Mit dabei sind namhafte Sprecher wie Niels de Bruijn, Peter Raganitsch, Denes Kubicek, Tobias Arnhold, Dietmar Aust und einige mehr ... Die Auswahl ist eigentlich immer schwierig; für jeden Vortrag, den man sich aussucht, verpasst man typischerweise zwei oder drei mindestens ebenso interessante. Dennoch möchten wir hier eine kleine Auswahl interessanter Vorträge und Programmpunkte vorstellen. Wie in den Vorjahren ist das APEX Entwicklerteam auch dieses Jahr auf der DOAG Konferenz vertreten. So stellt Joel Kallman, der Leiter des APEX Entwicklerteams, die neue Version APEX 5.0 am 18. November um 13:00 vor. Um 10:00 Uhr am gleichen Tag bekommen Sie den neuen Page Designer  aus erster Hand von Patrick Wolf gezeigt. Um 16:00 Uhr schließlich erfahren Sie von Marc Sewtz alles Wissenswerte über das neue Universal Theme. All diese Vorträge finden im "APEX-Raum" Istanbul statt. Am 2. Konferenztag trifft sich die PL/SQL Community um 09:00 Uhr im Raum Hong Kong. Mit dabei ist Bryn Llewelyn, der Oracle-Produktmanager für PL/SQL. Hier findet kein Vortrag statt, vielmehr dient dieses Treffen dem Austausch innerhalb der Community. Die APEX Open Mic Night ist auf der KScope-Konferenz in den USA stets ein Highlight - und dieses Jahr findet sie zum ersten Mal auf der DOAG-Konferenz statt. Wie der Name schon sagt, ist das Mikrofon im APEX-Raum "Istanbul" zwischen 20:00 Uhr und 21:30 "offen" - jeder kann 10 Minuten lang sein APEX-Lieblingsfeature oder -Anwendung vorstellen. Das ganze findet mit Getränken in lockerer und offener Atmosphäre statt. Wer während der Konferenz eine "Folienpause" braucht, ist im Demo Kino sicherlich gut aufgehoben. In dieser Reihe, die im "Foyer Tokio" auf der obersten Etage stattfindet, stehen Live-Demonstrationen im Mittelpunkt. Powerpoint ist dort nur in homöopathischen Dosierungen erlaubt. Einen Blick über den Tellerrand können Sie im Vortrag APEX, NodeJs and HTML5: Magic! von Alan Arentsen und Alex Nuijten werfen. Am dritten Konferenztag um 16:00 erfahren Sie im Raum Istanbul, wie man APEX mit modernen Technologien wie node.js oder HTML5 kombiniert. Dafür lohnt es sich, bis zum letzten Vortrag zu bleiben. Und wie immer wird sich die Community sehr rege über Twitter austauschen. Unter dem Hashtag #doag2014 können Sie die Konferenz quasi live mitverfolgen. 

Alle Jahre im November ist es wieder soweit: Die Oracle Community trifft sich in Nürnberg zur DOAG Jahreskonferenz - diesmal kurz DOAG2014. Auch die Themen APEX und PL/SQL sind wieder mit...

APEX

APEX Meetup in Ihrer Stadt - und: Oracle Developer Monthly!

Basierend auf einer Idee von Dan McGhan haben sich in den USA, mit Hilfe der Seite Meetup.com, mehrere lokale "APEX Meetups" gegründet. Im deutschsprachigen Raum gibt es bereits vier: APEX Meetup Düsseldorf (Niels de Bruijn und Oliver Lemm) APEX Meetup München (Markus Dötsch und Jürgen Schuster) APEX Meetup Stuttgart (Dorin Schewe) APEX Meetup Wien (Peter Raganitsch) Ziel ist es, Events zum Thema APEX zu koordinieren und die Community über stattfindende zu informieren. Wenn Sie also in der Nähe von Düsseldorf, München oder Wien leben, melden Sie sich einfach an. Und wenn nicht, dann gründen Sie das APEX Meetup in Ihrer Stadt - und geben Sie uns Bescheid. Außerdem starten wir ab dem 6. Juni 2014 ein monatliches, deutschsprachiges Webseminar: Den Oracle Developer Monthly: Es beginnt immer um 09:00 Uhr und dauert etwa 30 Minuten. In diesem Webseminar, erhalten die Teilnehmer aktuelle Informationen rund um die Oracle-Datenbank: Das umfasst unter anderem jeweils aktuelle Releases, wichtige Patchsets, anstehende Termine, interessante Neuigkeiten aus der Blogosphere und dem Web 2.0 und natürlich auch ein Feature des Monats. Damit Sie planen können, stehen die Termine bis Dezember bereits fest (Mehr Informationen). Also: In den Kalender eintragen und zum Termin einwählen - wir freuen uns auf Sie.

Basierend auf einer Idee von Dan McGhan haben sich in den USA, mit Hilfe der Seite Meetup.com, mehrere lokale "APEX Meetups" gegründet. Im deutschsprachigen Raum gibt es bereits vier: APEX...

APEX

Los geht's: APEX 5.0 Early Adopter ist da!

Ab sofort steht APEX 5.0 Early Adopter 1 zum Testen auf apexea.oracle.com bereit. Das APEX Entwicklerteam hat viele, interessante neue Funktionen eingebaut (vollständige Übersicht) , die Sie nun ausprobieren können.Im Zentrum steht natürlich der neue Page Designer, der das Bearbeiten von APEX Seiten völlig neu definiert. Komponenten können per Drag & Drop angeordnet werden - die Eigenschaften lassen sich in Property Panes verändern; so wie man es von anderen Entwicklungsumgebungen her kennt: Aber APEX bleibt sich treu: Alles läuft im Browser! Aber nicht nur der Page Designer ist neu - das Early Adopter Release bringt noch weitere neue Funktionen mit, die einen näheren Blick wert sind: Single Sign On-Anmeldung am Application Builder: Sie können nun ganz auf die Verwaltung von APEX-Nutzerkonten verzichten Pivot-Funktion im Interaktiven Bericht. "Kippen" Sie die Daten eines interaktiven Berichts einfach per Mausklick. Statische Dateien können nun als ZIP hochgeladen und in Verzeichnisstrukturen organisiert werden. Also - am besten gleich den Workspace beantragen und loslegen. Wie immer, ist Feedback erwünscht. Was immer Ihnen auffällt: Klicken Sie rechts oben auf die "Sprechblase" und sagen Sie's uns ...

Ab sofort steht APEX 5.0 Early Adopter 1 zum Testen auf apexea.oracle.com bereit. Das APEX Entwicklerteam hat viele, interessante neue Funktionen eingebaut (vollständige Übersicht) , die Sie nun...

APEX

APEX-Patchset 4.2.4 und Events im Dezember und Januar

Seit dem 14. Dezember steht das APEX Patchset 4.2.4 zum Download bereit. Die Installation erfolgt wie immer: Nutzer der aktuellen APEX-Version 4.2 laden das Patchset als Patch Nr. 17607802 aus MyOracleSupport herunter und installieren es gemäß dem beiliegenden Readme-Dokument. Wer eine ältere oder noch gar keine APEX-Version in seiner Datenbank hat, lädt die APEX-Vollinstallation 4.2.4 aus dem OTN herunter und installiert sie ganz normal. Neben den Bugfixes enthält das Patchset auch eine neue Sample Application: den Standards Tracker, der dabei helfen kann, eigene Standards für APEX-Anwendungen zu verwalten. Das neue Jahr 2014 startet denn auch bereits im Januar wieder mit neuen Informationsveranstaltungen durch: Auf den Oracle Database Days 12c können Sie sich in Stuttgart (21.01.), Berlin (28.01.) und Düsseldorf (29.01.) ausführlich über die Neuerungen in Oracle12c für Entwickler informieren. Die Veranstaltung ist für Sie kostenlos, beginnt mittags, und gibt neben den Vorträgen ausreichend Zeit zur Diskussion und zum Networking. Melden Sie sich am besten gleich an. Im interaktiven Webseminar am 19. Februar 2014 können Sie "Geodaten Live erleben", denn während dieses Webseminars werden Sie eine eigene APEX-Anwendung erstellen, die mit Koordinaten umgehen und diese auf einer Karte darstellen kann. Es wird ausreichend Zeit zum eigenständigen Ausprobieren und für Fragen und Antworten geben - und das alles von Ihrem Büro aus. Die Teilnahme ist kostenlos und die Plätze sind begrenzt: Melden Sie sich daher gleich an. Die DOAG veranstaltet am 30. Januar das völlig neue Format eines "Developer Bar Camp". Das Dev Camp ist eine offene Tagung mit offenen Workshops, bei dem verschiedene Gruppen mit ähnlichem Interesse aufeinander treffen, um über unterschiedliche Themen zu referieren, diskutieren und sich untereinander auszutauschen. Mehr Information findet sich auf der DOAG-Veranstaltungsseite.

Seit dem 14. Dezember steht das APEX Patchset 4.2.4 zum Download bereit. Die Installation erfolgt wie immer: Nutzer der aktuellen APEX-Version 4.2 laden das Patchset als Patch Nr. 17607802 aus MyOracle...

APEX

Arbeiten mit Arrays in SQL, PL/SQL und APEX - Teil II

Die Arbeit mit Arrays ist für einen APEX oder PL/SQL Entwickler alltäglich - sie werden immer wieder gebraucht. Und doch erscheint der Umgang mit Arrays in der Oracle-Datenbank oft rätselhaft. Unser aktueller Tipp setzt den Einblick in die Arbeit mit Arrays in der Oracle Datenbank fort und stellt nun die assoziativen Arrays in PL/SQL vor. Im Herbst 2013 finden außerdem für APEX Entwickler interessante Webseminare statt. Sie benötigen nur eine Stunde Zeit, Ihren Computer und ein Telefon. Die Teilnahme ist kostenlos - melden Sie sich am besten gleich an. 30.10.2013: Neue Datenbanken per Mausklick: Mit Orace12c Multitenant und APEXEines der Highlights des neuen Datenbank Release 12c ist die Möglichkeit, Datenbanken nun auch als "Pluggable Database" (PDB) innerhalb sog. "Container Databases" zu verwalten. Und die neue APEX-Anwendung zum Self Service Provisioning macht es möglich, neue Datenbanken per Selbstbedienungsverfahren bereitzustellen. Erfahren Sie in diesem Webseminar, wie Sie diese Erweiterung herunterladen, installieren und innerhalb kürzester Zeit einen Datenbankservice in Ihrem Unternehmen bereitstellen können: Oracle12c Multitenant. 08.11.2013: APEX und HTML5: Anwendungen der nächsten GenerationHTML5 ist nicht umsonst eines der meistdiskutierten Themen in der Anwendungsentwicklung: Es eröffnet dem Anwendungsentwickler völlig neue Möglichkeiten zur Gestaltung von Web-Benutzeroberflächen. So ist es möglich, mit HTML5 auf das GPS eines mobilen Geräts zuzugreifen - aber das ist bei weitem nicht alles: Mit SVG und CANVAS-Objekten wird es möglich, frei auf der Browseroberfläche zu zeichnen - die FileReader API erlaubt es, vom Anwender ausgewählte Dateien noch vor dem Hochladen auszulesen. Diese Möglichkeiten erlauben es, völlig neue Anwendungen zu entwickeln - eben Anwendungen der nächsten Generation. 04.12.2013: Datenbankfunktionen für PL/SQL und APEX-Entwickler: Inkl. Oracle12c UpdateAls APEX-Entwickler wissen Sie, dass die Anwendung in der Datenbank selbst läuft. Aber nutzen Sie die Datenbank wirklich voll aus? In diesem Webseminar erfahren Sie, wie Sie durch die Nutzung vorhandener PL/SQL-Pakete, SQL-Funktionen oder spezieller Datentypen - in weniger Zeit - mehr Funktionen in Ihre Anwendung bringen.

Die Arbeit mit Arrays ist für einen APEX oder PL/SQL Entwickler alltäglich - sie werden immer wieder gebraucht. Und doch erscheint der Umgang mit Arrays in der Oracle-Datenbank oft rätselhaft. Unser...

APEX

APEX Patchset 4.2.3 erschienen - und: Was bringt APEX 5.0 ...?

Seit dem 18. September ist das neue APEX Patchset 4.2.3 verfügbar. Details dazu sind auf Joel Kallmans Blog verfügbar. Wie immer können Nutzer älterer APEX-Versionen (4.1 oder älter) die Vollversion APEX 4.2.3 aus dem Oracle Technet herunterladen und die vorhandene APEX-Installation upgraden. Nutzer von APEX 4.2.0, 4.2.1 oder 4.2.2 laden Patch Nr. 17347169 aus MyOracleSupport herunter und wenden diesen gemäß des beiliegenden README-Dokumentes an. Das Patchset bringt im wesentlichen die folgenden Änderungen mit: Volle Unterstützung für Oracle Multitenant in Oracle12c Zwei neue "Packaged Applications": Data Reporter und Opportunity Tracker Das Package APEX_LANG wurde erweitert, um Anwendungsübersetzungen auch außerhalb der Entwicklungsumgebung anpassen zu können. Und wie in jedem Patchset: Bug Fixes - die genaue Liste ist im README des Patchsets enthalten. Das kommende Release APEX 5.0 wirft auf Konferenzen wie der nun anstehenden Oracle Open World, der DOAG2013 oder der ODTUG APEXPosed, die letzte Woche endete, seine Schatten voraus: Auf Twitter wird APEX 5.0 bereits heiß diskutiert. Dem Autor der deutschsprachigen APEX und PL/SQL Community können Sie unter @cczarski ebenfalls auf Twitter folgen. Allgemeine Datenbank-News in deutscher Sprache gibt es unter @OracleBUDB.

Seit dem 18. September ist das neue APEX Patchset 4.2.3 verfügbar. Details dazu sind auf Joel Kallmans Blog verfügbar. Wie immer können Nutzer älterer APEX-Versionen (4.1 oder älter) die Vollversion...