SQLcl 19.4 e break on report

Scrivo un breve post per segnalare una cosa bizzarra che mi è capitata, un bug su SQLcl che mi perseguitava da diverso tempo ma che solo recentemente ho analizzato e individuato. Già qualche settimana fa, dopo che ero convintamente passato a utilizzare SQLcl al posto di SQLPlus mi sono accorto che alcuni miei script non funzionavano bene. Alla fine ho trovato un attimo per approfondire e cercare di riprodurre il problema, ho fatto un test su un database interno e fatto un trace della sessione per vedere cosa accadeva. Sono così riuscito a circoscrivere il problema e creare un banale “test case” che ho sottoposto via twitteral gruppo di sviluppo di SQLcl. Il codice è molto semplice:

create table t1(a number);
insert into t1 values (1);
insert into t1 values (2);
break on report
compute sum of uno on report
select t1a.a uno from t1 t1a join t1 t1b on (t1a.a=t1b.a);

Il problema si verifica quando si usa la direttiva “break on report” in associazione a una query di join. Per gestire questa direttiva a quanto pare la query viene riscritta e qui nasce il problema, perché dalla query riscritta spariscono le condizioni di join. Infatti uno dei miei script andava su viste di sistema, ci metteva una vita a dare il risultato che poi era anche sbagliato. La mia query veniva trasformata in un prodotto cartesiano….

Nel mio caso di test anche facendo un prodotto la query restituirebbe solo due record, quindi non sarebbe un problema.  In realtà l’unica query che vedo eseguita risulta essere:

SELECT 
sum(t1a.a) AS uno, 'sum' uno_CMPFN, COUNT(*) AS ROW_CNT, 'report' AS BRKCOLKEY FROM t1 t1a, t1 t1b;

che restituisce un solo record ma qualcosa su SQLcl sembra vada in loop.

Sono ansioso di vedere se dopo la mia segnalazione al prossimo rilascio il problema sarà corretto

Oracle Program Global Area (PGA)

Premessa

Questo post è un esperimento di importazione da un documento scritto su google docs. Ho cominciato a raccogliere un po’ di appunti per fare un po’ di ordine sull’argomento nella mia testa, poi, visto che sono riuscito a scrivere qualcosa che mi sembra accettabile ho deciso di pubblicarlo, come ho fatto molte volte in passato. Ci sono sicuramente alcuni problemi di formattazione, da tempo ho alcune difficoltà con la piattaforma wordpress ma non ho tempo ne’ voglia di approfondire e risolvere questi dettagli.

In questo articolo farò una panoramica sulla componente “Program Global Area”, riferita normalmente con la sigla PGA. La versione del database a cui faccio riferimento è la 12.1 e quindi anche i riferimenti ai manuali puntano a quella versione.

La Program Global Area in Oracle è un’area di memoria di cui riporto direttamente la definizione dal manuale “Administrators Guide” tradotta alla meglio.

Una Program Global Area (PGA) è una area di memoria che contiene dati e informazioni di controllo per un processo server. Essa è una memoria non condivisa creata dal Database Oracle quando un processo server viene avviato. L’accesso alla PGA è esclusivo per quel processo, c’è una PGA per ogni processo server. Anche in processi di background allocano la loro PGA. La PGA allocata per i processi di background e i processi server attaccati a un Database Oracle vengono chiamati “total instance PGA memory” mentre l’insieme di tutte le PGA individuali è chiamato “total instance PGA” o solo “instance PGA”

Per la verità le ultime definizioni mi sembrano un po’ ambigue ma è un dettaglio irrilevante.

Riporto poi dal manuale “Performance Tuning Guide

Un esempio di informazioni scritte nella PGA per un processo è la “run-time area” di un cursore. Ogni volta che un cursore viene eseguito una nuova “run-time area” viene creata per quel cursore nella area di memoria PGA del processo server che sta eseguendo quel cursore.

Per query complesse una grossa porzione della area “run-time” è dedicata a “work area” allocate da operatori che fanno uso intensivo di memoria, inclusi ad esempio ORDER BY, GROUP BY, ROLLUP, HASH JOIN. Un operatore di ordinamento (sort operator) usa una “work area” (la “sort area”) per eseguire l’ordinamento in memoria di un insieme di righe. Analogamente , un operatore HASH JOIN usa la “work area” (hash area) per costruire una tabella hash dal suo input (left input).

 

Viste le definizioni generiche di PGA andiamo a vedere un po’ nel dettaglio come viene gestita la PGA.

Dimensioni delle “Work Area”

Oracle permette il controllo e la regolazione delle dimensioni delle “work area”. Generalmente “work area” più grandi possono migliorare significativamente le prestazioni di un particolare operatore al costo di un più alto consumo di memoria. Le dimensioni di “work area” possono essere classificate in :

  • Optimal – quando la dimensione è sufficientemente grande da contenere i dati di ingresso e le strutture di memoria ausiliarie allocate dall’operatore SQL associato. Questa è la dimensione ideale
  • One-pass – quando la dimensione della “work area” è al di sotto di quella ottimale e richiede un passaggio in più sui dati di ingresso
  • Multi-pass – quando la “work area” non è sufficiente e occorrono più passaggi sui dati di ingresso

Tunable e Untunable

Oracle classifica, o almeno espone questa classificazione della PGA in sei categorie:

  • SQL
  • PL/SQL
  • Java
  • Olap
  • Other
  • Freeable

La parte dedicata alle work area è quella denominata “SQL”, viene anche definita “tunable” in contrapposizione alle altre che nel complesso sono definite “untunable”.

Dimensionamento della Program Global Area

Abilitando la gestione automatica della PGA (tramite il parametro PGA_AGGREGATE_TARGET con un valore maggiore di zero) Oracle dimensiona automaticamente la PGA correggendo dinamicamente la porzione di PGA dedicata alle “work area”. Se non specificato il valore di default per PGA_AGGREGATE_TARGET è il 20% delle dimensione della SGA. Il valore minimo è 10 MB. Il dimensionamento automatico gestito sulla base del parametro PGA_AGGREGATE_TARGET riguarda solo la parte definita “tunable”, quindi solo quella dedicata alle work area che in un certo senso è quella più dinamica

PGA_AGGREGATE_LIMIT

Dalla versione 12c Oracle ha introdotto il nuovo parametro PGA_AGGREGATE_LIMIT che permette di limitare l’allocazione globale di memoria per la PGA. Per default il valore di questo parametro è il valore più grande fra:

  • 2 GB
  • 2 * PGA_AGGREGATE_TARGET
  • 3MB*PROCESSES

Può essere inferiore al 2* PGA_AGGREGATE_TARGET se tale valore supera il 90% della memoria ram totale disponibile meno la dimensione della SGA. Oracle raccomanda di non impostare PGA_AGGREATE_TARGET a un valore inferiore al suo default e per farlo l’unico modo è attraverso pfile o spfile (PGA_AGGREGATE_LIMIT). Su questa affermazione ho delle perplessità, infatti su una installazione con Oracle 12.1.0.2.0 ho fatto il seguente test:

SYSTEM@svil121 > show parameter pga
NAME TYPE VALUE
-------------------- ----------- -----
pga_aggregate_limit big integer 2856M
pga_aggregate_target big integer 2700M
SYSTEM@svil121 > alter system set pga_aggregate_limit=2900 m;
System SET modificato.
SYSTEM@svil121 > alter system set pga_aggregate_limit=2800 m;Errore con inizio alla riga : 1 nel comando -
alter system set pga_aggregate_limit=2800 m
Report error -
ORA-02097: impossibile modificare il parametro perchÚ il valore specificato non Þ valido
ORA-00093: pga_aggregate_limit deve essere compreso tra 2856M e 100000G
02097. 00000 - "parameter cannot be modified because specified value is invalid"
*Cause: Though the initialization parameter is modifiable, the modified
value is not acceptable to the parameter.
*Action: Check the DBA guide for range of acceptable values for this
parameter.
SYSTEM@svil121 > show parameter processes
NAME TYPE VALUE
------------------------- ------- -----
processes integer 1200

La RAM totale del sistema è 8100 MB e la SGA 4096 MB, quindi il valore di default dovrebbe essere almeno 3600 MB (che rientra nel 90% della memoria del sistema meno la SGA)

Sospetto possa essere un bug (di cui non ho trovato traccia sul supporto) in quanto su una installazione 18.3 con parametro processes a 1000, target a 512 MB il messaggio mi dice che il valore deve essere tra 3000 M e 100000 G, dove 3000 M è coerente con l’indicazione 3MB*PROCESSES.

Nella documentazione relativa al parametro PGA_AGGREGATE_LIMIT si dice che nel caso la dimensione della PGA superi il valore impostato dal parametro le sessioni che stanno usando la maggior quantità di “untunable memory” avranno le chiamate annullate (ORA-04036). Se la PGA sarà ancora sopra al limite tali sessioni verranno terminate.

Sul manuale Performance Tuning vengono riportate tutte le informazioni, c’è però una piccola differenza in quanto si afferma che il valore di PGA_AGGREGATE_LIMIT non supererà il 120% della dimensione della RAM disponibile meno la dimensione totale della SGA.

La nota del supporto Oracle “Limiting process size with database parameter PGA_AGGREGATE_LIMIT” (Doc id 1520324.1) riporta qualche dettaglio in più. Fra l’altro riporta che per Oracle 12.1 c’è il limite del 120% sulla dimensione della memoria fisica meno la SGA mentre per Oracle 12.2 spiega che se il parametro viene impostato a zero il suo valore sarà pari al 90% della dimensione della memoria fisica meno la dimensione della SGA.

A controllare il rispetto del parametro PGA_AGGREGATE_LIMIT ci pensa il processo CKPT ogni 3 secondi. Viene spiegato come nell’intervallo di tempo di tre secondi fra un controllo e l’altro di CKPT può essere superato il limite e se la sessione finisce la chiamata prima non ci sono conseguenze o azioni da parte di CKPT.

Controllo dell’uso e allocazione della PGA

Per controllare quanta PGA è stata allocata e come viene usata si possono usare diverse viste di sistema che possono essere interrogate via SQL, le descriverò sinteticamente una ad una.

V$PGASTAT

Questa vista mostra le statistiche globali sulla PGA, su 12.1 conto 19 righe. Viene riportato il valore di PGA_AGGREGATE_TARGET e poi, una statistica chiamata “aggregate PGA auto target”, questo è il valore derivato da PGA_AGGREGATE_TARGET che indica lo spazio PGA che oracle cerca di dedicare alle work area. Questo valore tendenzialmente è molto vicino alla differenza tra il target e la quantità di PGA “untunable” (PL/SQL ecc).

Abbiamo poi i valori di “total PGA allocated”, “total PGA inuse” e “maximum PGA allocated” che indicano, come è facile comprendere, la dimensione totale di PGA attualmente allocata, quella effettivamente in uso e quella massima allocata dall’avvio dell’istanza.

Può essere interessante il valore di “maximum PGA used for auto workareas” che indica il massimo volume di PGA consumato in una volta da work area. Vi sono poi altre statistiche che permettono di avere una indicazione dell’efficienza della PGA per poter capire se il dimensionamento è adeguato.

V$PROCESS

Se si vuole avere il dettaglio di utilizzo della PGA da parte di un processo (e quindi relativa sessione, supponendo di usare “dedicated servers”) questa vista riporta le quattro colonne:

  • PGA_USED_MEMORY
  • PGA_ALLOC_MEMORY
  • PGA_FREEABLE_MEMORY
  • PGA_MAX_MEM

Per esempio posso usare la query:

select s.sid, s.username,
pga_used_mem/1024/1024 pga_used_mem,
pga_alloc_mem/1024/1024 pga_alloc_mem,
pga_freeable_mem/1024/1024 pga_freeable_mem,
pga_max_mem/1024/1024 pga_max_mem,
row_number() over (order by pga_max_mem) rn
from
v$session s
join v$process p on (p.addr=s.paddr)
order by 6

Per ricavare la lista delle sessioni con relativo uso di PGA. In una applicazione che usa un POOL di connessioni questo dettaglio non è secondo me molto utile, occorre aggregare per avere valori medi, massimi e totali.

V$PROCESS_MEMORY

Per scendere ulteriormente nel dettaglio abbiamo a disposizione questa vista che per ciascun processo riporta il dettaglio di uso di PGA diviso nelle categorie:

  • Java
  • PL/SQL
  • OLAP
  • SQL
  • Freeable
  • Other

Non sempre sono tutte presenti, nel mio caso non ho mai righe relative a Java e OLAP. Se non ho capito male nella categoria SQL ricadono le work area, il resto dovrebbe essere tutto “untunable”. Riporto un esempio:

USER@prod > select category,sum(allocated)/1024/1024 a_mb,sum(used)/1024/1024 u_mb, max(max_allocated)/1024/1024 ma_mb from V$process_memory group by category;
CATEGORY A_MB U_MB MA_MB
--------------- ---------- ---------- ----------
Freeable 606,5 0
PL/SQL 64,0559082 21,6882935 5,46675873
Other 7871,91492 502,54974
SQL 14,4066696 2,82536316 1554,26163

Anche in questo caso ho fatto un’aggregazione

V$SESSTAT

La cito solo per completezza perché non mi sembra che dia informazioni che non possiamo avere già con le viste descritte in precedenza. Questa vista di sistema mostra per ogni sessione i valori per una lunga lista di statistiche, le due che ci possono interessare qui sono:

  • session pga memory
  • session pga memory max

La descrizione sul manuale spiega come queste statistiche abbiano senso solo sulla V$SESSTAT e non sulla V$SYSSTAT dove evidentemente non viene riportato il dato aggregato

DBA_HIST_ACTIVE_SESS_HISTORY

Premesso che questa tabella fa parte di AWR e quindi è soggetta a licenza del “tuning e diagnostics” Pack che si paga come extra sulla Enterprise Edition, riporto questa tabella non perché io la usi di frequente ma perché mi è stata indicata come fonte per individuare query che hanno richiesto più PGA. In effetti, dall’esempio che ho riportato sopra per la V$PROCESS_MEMORY si può vedere che risulta un processo che ha occupato oltre 1500 MB di work area. A meno di vedere in tempo reale le viste V$SESSION e V$PROCESS (o catturare a intervalli regolari in stile ASH il loro contenuto) non ho trovato modi per ricavare per statement/query l’utilizzo di PGA. Una alternativa è V$SQL_WORKAREA ma questa ha solo il campo LAST_MEMORY_USED, non c’è un MAX_MEMORY_USED. Per cui sembrerebbe che facendo un raggruppamento per SQL_ID, SQL_EXEC_ID (sul quale invito alla lettura di questo) si possa avere una indicazione degli statement (sql_id) che hanno richiesto maggiore PGA in un dato momento. Facendo query su snapshot limitati (se si va oltre un certo intervallo temporale il SQL_EXEC_ID potrebbe essere lo stesso ma non necessariamente l’esecuzione, ad esempio fra un giorno e l’altro…) in effetti ASH fotografa la situazione delle sessioni attive, per ciascuna sessione ricava sia la PGA_ALLOCATED che il SQL_ID dello statement in esecuzione, quindi lo statement dovrebbe essere quello che causa l’allocazione di PGA.

select sql_id,sql_exec_id,count(*),max(pga_alloc_mem) from dba_hist_active_sess_history where snap_id>=x and snap_id<=y group by sql_id,sql_exec_id;

Conclusioni

Partendo dall’esigenza di analizzare l’utilizzo di PGA in una ambiente di produzione e di individuare i “colpevoli” di elevato uso di PGA ho fatto un ripasso di tutti gli strumenti/mezzi che Oracle mette a disposizione facendo una piccola panoramica su cosa è la PGA, su come e perché viene utilizzata e infine su dove visualizzarne le informazioni e le statistiche di uso.

Riferimenti:

  1. https://support.oracle.com/epmos/faces/DocumentDisplay?_afrLoop=20851480822242&id=1520324.1&_afrWindowMode=0&_adf.ctrl-state=rze9i5k0x_4
  2. https://weidongzhou.wordpress.com/2016/02/25/pga_aggregate_target-vs-pga_aggregate_limit/
  3. https://docs.oracle.com/database/121/REFRN/GUID-E364D0E5-19F2-4081-B55E-131DF09CFDB3.htm#REFRN10328
  4. https://blog.tanelpoder.com/2011/10/24/what-the-heck-is-the-sql-execution-id-sql_exec_id/
  5. https://oracle-base.com/articles/12c/resource-manager-per-process-pga-limits-12cr2
  6. https://fritshoogland.wordpress.com/2014/12/15/oracle-database-operating-system-memory-allocation-management-for-pga/

Oracle SQLcl

Anche se la mia diffidenza mi spinge ad essere ancora prudente e a tenermi pronto per un piano di ritorno, sto passando da SQL*Plus a SQLcl. Il client a linea di comando alternativo a SQL*Plus facente parte di una suite che comprende tra gli altri il client grafico SQLDeveloper e SQL Data Modeler, sembra adesso abbastanza maturo ed compatibile quasi al 100% con SQL*Plus, del quale però estende le funzionalità e l’usabilità. Fra l’altro il pacchetto è anche più leggero. Rimane qualche minuscolo dettaglio che mi crea qualche turbamento ma direi che è ora di fare questo salto. Una delle cose che mi ha turbato è stata il problema del set autotrace di cui ho parlato nel post precedente, problema però che è comune a SQL*Plus, quindi è un falso problema. Trovo poi comodo la modalità di output “ansiconsole”, attivabile con il comando “set sqlformat ansiconsole”. Questa modalità ha il difetto che non fa funzionare le direttive “compute” o “break on”. Volevo aggirare il problema aggiungendo negli script il comando di salvataggio delle impostazioni su file (con il comando “store”), il cambiamento di impostazione per sqlformat e infine il caricamento delle impostazioni salvate. Purtroppo però con il comando STORE non mi viene salvata l’impostazione di sqlformat, quindi il trucco non funziona.

Per il resto il prodotto sviluppato da Jeff Smith e dai suoi collaboratori sembra proprio valido

Riferimenti:

SET AUTOTRACE

Si vede che sono un po’ fuori allenamento e dimentico le cose, quelle che imparai anni fa con tanto impegno e che registrai su questo blog proprio per evitare che ciò accadesse. E’ il caso di quanto scrissi in questo post,  l’unica scusante è che si tratta di un post di 12 anni fa; non posso ammetterla come scusante ma non posso neppure accettare di dimenticare cose che mi servono ancora nel mio lavoro.

Nei giorni scorsi stavo analizzando una query sottopostami da un collega, per mia sventura ho pensato di analizzare il piano di esecuzione usando l’opzione “set autotrace” di sqlplus, per fare più velocemente. Poi in realtà ho provato un passaggio a SQLcl che sembra un prodotto decisamente maturo e in grado di superare molti limiti di SQL*Plus anche se ho trovato qualche problemino con alcuni vecchi script. Premetto che il db su cui stavo facendo i test è un Oracle 12.1. A un certo punto noto una anomalia, il piano di esecuzione rimaneva lo stesso ma le statistiche, in particolare i “consistent gets” diminuivano molto dopo la prima esecuzione. A quel punto sono tornato sui miei passi ed ho utilizzato esplicitamente la cara vecchia “dbms_xplan.display_cursor” e mi sono accorto che in effetti la query aveva la particolarità di ottenere, grazie alle funzionalità di “adaptive query optimization”, un piano diverso alla seconda esecuzione.  Chiaramente il fatto di utilizzare un “explain plan” per calcolare il piano di esecuzione tagliava fuori questa casistica confondendomi. Per la verità quando mi sono accorto dell’inghippo stavo utilizzando SQLcl e quindi ho fatto una breve ricerca, imbattendomi in questo “twitt“; a questo punto ho fatto un test, anche se su un database su cui non c’erano gli stessi dati e la particolarità del cambio dati non si ripresenta. Ho abilitato il trace della sessione, abilitato il set autotrace, ho eseguito la query, fermato il trace e analizzato il trace. Dentro il trace ci ho trovato la solita “explain plan for” per cui ho chiesto informazioni commentando il twitt. Anche con l’ultima versione, 19.2 di SQLcl io registro lo stesso comportamento, quindi continuerò ad utilizzare la cara vecchia “dbms_xplan.display_cursor”

External Table Preprocessor e l’importanza di leggere bene

In questi giorni ho avuto la necessità di fare un sistema semplice di monitoraggio di un database server. Una delle cose che volevo verificare era lo spazio su disco e il server era con sistema operativo winzoz. Da una rapida ricerca su come fare un monitoraggio direttamente via SQL sono capitato sul solito AskTom con questa discusssione.  La fortuna mi aveva sorriso perché li c’erano due tecniche che mi permettevano di risolvere il problema in maniera brillante; la prima è quella di usare la direttiva “PREPROCESSOR” delle External Tables; la seconda era una script per powershell che estrae i dati sui dischi della macchina.  Mi sono gingillato un po’ con powershell, che è sì una strumento utile e potente ma continuo a pensare sia un po’ complesso per le normali esigenze; forse è solo perché io non sono un gran programmatore e non mi sono mai impegnato per studiare bene l’argomento.

Il punto però è che mi sono incagliato per un paio d’ore perché non riuscivo a far funzionare l’interrogazione sulla external table con la direttiva PREPROCESSOR. Ottenevo sempre un misterioso errore di permessi sul file di testo su cui la external table doveva accedere. Alla fine sono capitato sulla nota del supporto Oracle “Database External Tables Fails with KUP-04001 On Windows (Doc ID 2046669.1)”. La nota spiega come l’errore che io continuavo ad avere era causato da un baco della versione 12.1.0.2 su windows e come sia sufficiente aggiungere la direttiva “DISABLE_DIRECTORY_LINK_CHECK”. A quel punto mi è venuta voglia di sbattere la testa sul muro. In effetti sulla discussione di AskTom avevo visto quella strana direttiva, avevo però letto molto in fretta. Poi mi ero perso con l’adattamento dello script per powershell e così mi ero dimenticato di quella particolarità. Aggiunta la direttiva il trucco della external table che chiama lo script funziona benissimo. Se avessi letto con maggiore attenzione la discussione originale avrei risparmiato tempo.

Export datapump ORA-12899 con parametro VERSION

Facendo un export datapump  su un Oracle 12.1.0.2 con il parametro VERSION=11.2 ho avuto questo errore:

ORA-39126: Error fatal inesperado de worker en KUPW$WORKER.FIXUP_MASTER_TABLE_EXPORT [TABLE_DATA:”SCHEMANAME”.”SYS_EXPORT_SCHEMA_02″]
ORA-12899: el valor es demasiado grande para la columna “SYS”.”KU$_DATAPUMP_MASTER_11_2″.”PROCESSING_STATUS” (real: 3, maximo: 1)

Una rapida ricerca mi ha portato alla nota del supporto Oracle “ORA-12899 During Expdp With VERSION parameter In 12.1.0.2 (Doc ID 2051535.1)”

La nota riporta come il problema sia riconducibile a un bug identificato e per il quale è disponibile una patch. Il fattore scatenante pare siano le impostazioni internazionali, infatti nel caso riportato nella nota il linguaggio è tedesco. Nel mio caso è spagnolo ma l’errore è quasi identico. La cosa che mi ha lasciato veramente perplesso è che nella nota riportano tre possibili soluzioni:

  1. installazione della patch;
  2. upgrade a oracle 12.2;
  3. impostazione del parametro VERSION=12.1

Ora, avrei da ridire sulla 3. nel mio caso ho utilizzato il parametro version=11.2 non per vezzo ma per necessità di importare il dump su una versione 11.2

Il fatto però è che in altri casi non ho avuto questo problema; se la causa è il settaggio di NLS_LANG, ho pensato, proviamo a impostarla a un valore comune, quindi ho provato a impostare NLS_LANG=AMERICAN_AMERICA, ho rilanciato l’export e l’operazione è andata a buon fine. Non mi risulta che la cosa possa creare problemi sul dump e quindi mi sfugge come mai questa soluzione non venga proposta come “work-around”

 

JSON in Oracle: Introduzione

JSON è la sigla per JavaScript Object Notation. Si tratta di un formato per lo scambio dati molto semplice, basato su un sottoinsieme del linguaggio di programmazione JavaScript. E’ un formato facile da leggere e capire per gli umani e per le macchine. Oserei dire che in questo aspetto è un ottimo concorrente di XML. Qui viene riportato un confronto tra le due rappresentazioni: https://docs.oracle.com/en/database/oracle/oracle-database/18/adjsn/json-data.html#GUID-C347AC02-31E4-49CE-9F74-C7C0F339D68E.
L’idea che al momento mi sono fatto è che diventa un’ottima alternativa a XML in caso di dati con struttura molto semplice. La definizione del formato JSON è tanto semplice da stare in una paginetta abbastanza corta: https://www.json.org/
Negli anni JSON pare essere diventanto sempre più popolare e diffusamente usato, assieme ai cosiddetti database NoSQL. Questo formato è talmente diffuso che anche Oracle ne ha gradualmente introdotta la gestione sul suo database relazionale. L’introduzione è avvenuta con la versione 12cR1, penso come estensione delle funzionalità XML DB, o perlomeno ne ha aggiunto la documentazione al relativo manuale. (https://docs.oracle.com/database/121/ADXDB/json.htm#ADXDB6246). Un’ottima sintesi del manuale Oracle viene fornita da Tim Hall: https://oracle-base.com/articles/12c/json-support-in-oracle-database-12cr1

Apro una piccola parentesi con le impressioni e l’idea che mi sono fatto al momento, da novizio di JSON. Presumo che JSON si sia diffuso molto in contesti particolari, dove c’era l’esigenza di gestire dati con strutture molto semplici, assieme a questo formato mi sembra si sia diffuso l’uso di database NoSQL che hanno la capacità di gestire in modo più efficente questi dati e che in alcuni casi al costo di non soddisfare i requisiti di un database relazionali (ad esempio transazioni ACID) riescono ad avere migliori prestazioni e migliore “scalabilità”. Si tratta quindi di sistemi che sono in grado di gestire un carico crescente di lavoro senza andare in sofferenza. Ora, quello che si fa con JSON si può benissimo fare con un database relazionale, anzi, si tratta di implementare schemi molto semplici, per fare interrogazioni analoghe a quelle tipiche che si fanno su dati in formato JSON, su una analoga struttura relazionale bastano query SQL molto banali, non parliamo di join, raggruppamenti ecc. Il fatto di essere “schemaless” è un falso vantaggio o problema, anche in un database relazionale posso aggiungere a piacere colonne al volo a una tabella, senza problemi; è tutto nel come si scrive le applicazioni, se la mia applicazione è scritta come una volta si raccomandava, il fatto che in una tabella da un momento all’altro ci sia un campo in più non fa nessuna differenza. Ciò che un database come Oracle forse non può fare bene è gestire questi dati con prestazioni elavate, perché Oracle è appunto un database relazionale vero, ne rispetta tutte le regole, la visione dei dati è sempre consistente e in più Oracle ha una infrastruttura di monitoraggio e manutenzione. Per andare incontro al mercato e all’esigenza di interfacciarsi con il mondo anche Oracle si è adeguata; certo, trovo buffo dover fare su un database relazionale manovre per estrarre e manipolare dei dati quanto si possono usare dei semplici comandi sql, alla portata di qualunque programmatore degno di questo nome. E’ normale che Oracle abbia aggiunto il supporto a questo formato, per facilitare lo sviluppo di funzionalità di scambio dati con sistemi esterni senza dover forzare continue trasformazioni da un modello JSON a un modello relazionale e viceversa. Chiusa per ora la parentesi.

Alla base del modello dati JSON ci sono insiemi di coppie “chiave”-“valore”, dove la chiave è un’etichetta, il nome di una variabile, un identificativo a cui è associato appunto un valore. Il valore può essere un tipo base e in JSON sono previsti solo numeri, stringhe, valori booleani (true o false) o null. Altrimenti il valore può essere ricorsivamente un altro insieme di coppie chiave-valore oppure un array che si identifica usando come delimitatori dei suoi elementi le parentesi quadre. Un oggetto base, invece è delimitato dalle parentesi graffe. Quindi ecco un primo esempio di oggetto JSON:

{“nome” : “cristian”}

Se ci sono più coppie vengono separate da virgole:

{“nome” : “cristian”, “matricola” : 1234}

L’etichetta, come tutte le stringhe, anche quando sono valori, vanno racchiuse tra apici doppi. Ampliando e applicando ricorsivamente la struttura possiamo arrivare a un esempio più complesso (tratto dal manuale Oracle):

{“PONumber” : 1600,
“Reference” : “ABULL-20140421”,
“Requestor” : “Alexis Bull”,
“User” : “ABULL”,
“CostCenter” : “A50”,
“ShippingInstructions” : {…},
“Special Instructions” : null,
“AllowPartialShipment” : true,
“LineItems” : […]}

Per gestire il tipo dato “JSON” in Oracle non hanno introdotto un nuovo tipo dato, gli oggetti JSON possono essere messi in campi con tipo dato VARCHAR2, CLOB o BLOB. E’ stato introdotto un vincolo “IS JSON” applicabile alla colonna destinata a contenere i documenti JSON (https://docs.oracle.com/en/database/oracle/oracle-database/18/adjsn/json-in-oracle-database.html#GUID-F6282E67-CBDF-442E-946F-5F781BC14F33). Il vincolo fa si che oracle verifichi che all’interno della colonna venga inserito un documento di tipo JSON valido. Il vincolo può essere rafforzato con la specifica “STRICT” che fa rende il vincolo più rigoroso (https://docs.oracle.com/database/121/ADXDB/json.htm#GUID-951A61D5-EDC2-4E30-A20C-AE2AE7605C77)

Con questo concludo questo post introduttivo, ne seguiranno altri con esempi e approfondimenti.