web-dev-qa-db-fra.com

Comment analyser une chaîne JSON dans PL/SQL?

Je souhaite analyser une chaîne JSON qui se trouve dans la colonne CLOB de la table Tests_1 et l'insérer dans une autre table (Test_2).

Comment puis-je faire cela en PL/SQL sans utiliser aucune bibliothèque JSON?

create table Tests_1
(
  value   CLOB
)
create table Test_2 (a date,b date,c number,d number, e number)

INSERT INTO Tests_1
  (value)
VALUES
  ('{ 
"a":"01/01/2015",
"b":"31/12/2015",
"c":"11111111111",
"d":"1111111111",
"e":"1234567890"
}');
7
Onur Cete

Oracle 12c prend en charge JSON

si vous avez une table existante faites simplement

ALTER TABLE table1 ADD CONSTRAINT constraint_name CHECK (your_column IS json);
SELECT t.your_column.id FROM table1 t;

Notez que pour quelque raison que ce soit t nickname est nécessaire ici

Ou exemple complet:

CREATE TABLE json_documents (
  id    RAW(16) NOT NULL,
  data  CLOB,
  CONSTRAINT json_documents_pk PRIMARY KEY (id),
  CONSTRAINT json_documents_json_chk CHECK (data IS JSON)
);

INSERT INTO json_documents (id, data)
VALUES (SYS_GUID(),
        '{
          "FirstName"      : "John",
          "LastName"       : "Doe",
          "Job"            : "Clerk",
          "Address"        : {
                              "Street"   : "99 My Street",
                              "City"     : "My City",
                              "Country"  : "UK",
                              "Postcode" : "A12 34B"
                             },
          "ContactDetails" : {
                              "Email"    : "[email protected]",
                              "Phone"    : "44 123 123456",
                              "Twitter"  : "@johndoe"
                             },
          "DateOfBirth"    : "01-JAN-1980",
          "Active"         : true
         }');




SELECT a.data.FirstName,
       a.data.LastName,
       a.data.Address.Postcode AS Postcode,
       a.data.ContactDetails.Email AS Email
FROM   json_documents a;


FIRSTNAME       LASTNAME        POSTCODE   EMAIL
--------------- --------------- ---------- -------------------------
Jayne           Doe             A12 34B    [email protected]
John            Doe             A12 34B    [email protected]

2 rows selected.

Plus d'informations 

3
Toolkit

Avec la version 11.0.4 (il n'y a pas de version 11.0.4, bien sûr) vous avez au moins deux choix (à part écrire un analyseur vous-même):

En fonction de la version du SGBDR que vous utilisez, voici quelques options: 

Premièrement: pour Oracle 11.1.0.7 et plus, installez Apex 5 et utilisez le paquetage apex_json:

-- here I have 12.1.0.1 version with version 5 of apex installed

column ora_version format a21;
column apex_version format a21;


select (select version from v$instance) as ora_version
     , (select version_no from apex_release) as apex_version
  from dual;

--drop table test_2;
/* our test table */  
create table test_2(
  c_a date,
  c_b date,
  c_c number,
  c_d number,
  c_e number
);

select * from test_2;

declare
  l_json_doc clob; 
begin
  dbms_output.put_line('Parsing json...');
  l_json_doc := '{"a":"01/01/2015","b":"31/12/2015",
                  "c":"11111111111","d":"1111111111",
                  "e":"1234567890"}';
  apex_json.parse(l_json_doc);
  insert into test_2(c_a, c_b, c_c, c_d, c_e)
    values(apex_json.get_date(p_path=>'a', p_format=>'dd/mm/yyyy'),
           apex_json.get_date(p_path=>'b', p_format=>'dd/mm/yyyy'),
           to_number(apex_json.get_varchar2(p_path=>'c')),
           to_number(apex_json.get_varchar2(p_path=>'d')),
           to_number(apex_json.get_varchar2(p_path=>'e')));
  commit;
  dbms_output.put_line('Done!');
end;
/

column c_c format 99999999999;
select to_char(c_a, 'dd/mm/yyyy') as c_a
     , to_char(c_b, 'dd/mm/yyyy') as c_b
     , c_c
     , c_d
     , c_e 
  from test_2;

Résultat:

ORA_VERSION           APEX_VERSION         
--------------------- ---------------------
12.1.0.1.0            5.0.2.00.07          

1 row selected.

Table created.

no rows selected.

Parsing json...
Done!
PL/SQL procedure successfully completed.


C_A        C_B                 C_C        C_D        C_E
---------- ---------- ------------ ---------- ----------
01/01/2015 31/12/2015  11111111111 1111111111 1234567890

1 row selected.

Deuxièmement: Utilisez opensource PL/JSON . Jamais utilisé auparavant, alors je saisis cette occasion pour l'essayer. C'est assez similaire à apex_json.

declare
  l_json      json;  --json object
  l_json_doc  clob;
begin
  dbms_output.put_line('Parsing json...');

  -- parsing is done upon object instantiation

  l_json_doc := '{"a":"01/01/2015","b":"31/12/2015",
                  "c":"11111111111","d":"1111111111",
                  "e":"1234567890"}';
  l_json := json(l_json_doc);


  insert into test_2(c_a, c_b, c_c, c_d, c_e)
    values(to_date(l_json.get('a').get_string, 'dd-mm-yyyy'),
           to_date(l_json.get('b').get_string, 'dd-mm-yyyy'),
           to_number(l_json.get('c').get_string),
           to_number(l_json.get('d').get_string),
           to_number(l_json.get('e').get_string));
  commit;
  dbms_output.put_line('Done!');
end;

column c_c format 99999999999;
select to_char(c_a, 'dd/mm/yyyy') as c_a
     , to_char(c_b, 'dd/mm/yyyy') as c_b
     , c_c
     , c_d
     , c_e 
  from test_2;

Résultat:

C_A        C_B                 C_C        C_D        C_E
---------- ---------- ------------ ---------- ----------
01/01/2015 31/12/2015  11111111111 1111111111 1234567890
01/01/2015 31/12/2015  11111111111 1111111111 1234567890

2 rows selected.

L’introduction de json_table() dans la version 12.1.0.2 simplifie l’analyse JSON (pour des raisons de démonstration):

insert into test_2
  select to_date(c_a, 'dd-mm-yyyy')
       , to_date(c_b, 'dd-mm-yyyy')
       , c_c
       , c_d
       , c_e
    from json_table('{"a":"01/01/2015",
                      "b":"31/12/2015",
                      "c":"11111111111",
                      "d":"1111111111",
                      "e":"1234567890"}'
                    , '$' 
                    columns ( 
                       c_a varchar2(21) path '$.a',
                       c_b varchar2(21) path '$.b',
                       c_c varchar2(21) path '$.c',
                       c_d varchar2(21) path '$.d',
                       c_e varchar2(21) path '$.e'
                    )) ;

résultat:

 select *
   from test_2;


C_A         C_B                C_C        C_D        C_E
----------- ----------- ---------- ---------- ----------
1/1/2015    12/31/2015  1111111111 1111111111 1234567890
5
Nick Krasnov

Puisque vous avez spécifié que vous ne souhaitiez utiliser aucune bibliothèque JSON, si le format est corrigé, vous pouvez le convertir en quelque chose que vous pouvez analyser en tant que XML, en commençant par supprimer les accolades, en remplaçant les deux points par des signes égal, puis en supprimant le double. citations de la première partie de chaque paire nom/valeur:

select regexp_replace(regexp_replace(value, '(^{|}$)'),
  '^"(.*)":(".*")($|,)', '\1=\2', 1, 0, 'm')
from tests_1;

REGEXP_REPLACE(REGEXP_REPLACE(VALUE,'(^{|}$)'),'^"(.*)":(".*")($|,)','\1=\2',1,0
--------------------------------------------------------------------------------

a="01/01/2015"
b="31/12/2015"
c="11111111111"
d="1111111111"
e="1234567890"

que vous pouvez utiliser comme attributs d’un nœud XML factice; convertissez cela en XMLType et vous pouvez utiliser XMLTable pour extraire les attributs:

select x.a, x.b, x.c, x.d, x.e
from tests_1 t
cross join xmltable('/tmp'
  passing xmltype('<tmp ' ||regexp_replace(regexp_replace(value, '(^{|}$)'),
    '^"(.*)":(".*")($|,)', '\1=\2', 1, 0, 'm') || ' />')
  columns a varchar2(10) path '@a',
    b varchar2(10) path '@b',
    c number path '@c',
    d number path '@d',
    e number path '@e'
) x;

A          B                      C             D             E
---------- ---------- ------------- ------------- -------------
01/01/2015 31/12/2015   11111111111    1111111111    1234567890

Ensuite, vous pouvez convertir les chaînes en dates lors de l'insertion:

insert into test_2 (a, b, c, d, e)
select to_date(x.a, 'DD/MM/YYYY'), to_date(x.b, 'DD/MM/YYYY'), x.c, x.d, x.e
from tests_1 t
cross join xmltable('/tmp'
  passing xmltype('<tmp ' || regexp_replace(regexp_replace(value, '(^{|}$)'),
    '^"(.*)":(".*")($|,)', '\1=\2', 1, 0, 'm') || ' />')
  columns a varchar2(10) path '@a',
    b varchar2(10) path '@b',
    c number path '@c',
    d number path '@d',
    e number path '@e'
) x;

select * from test_2;

A          B                      C             D             E
---------- ---------- ------------- ------------- -------------
2015-01-01 2015-12-31   11111111111    1111111111    1234567890

Cela permettra d'éviter que certaines paires nom/valeur ne soient présentes et vous obtiendrez des valeurs NULL si cela se produit.

Si toutes les paires sont toujours présentes, vous pouvez simplement marquer la chaîne et extraire les parties appropriées:

select to_date(regexp_substr(value, '[^"]+', 1, 4), 'DD/MM/YYYY') as a,
  to_date(regexp_substr(value, '[^"]+', 1, 8), 'DD/MM/YYYY') as b,
  to_number(regexp_substr(value, '[^"]+', 1, 12)) as c,
  to_number(regexp_substr(value, '[^"]+', 1, 16)) as d,
  to_number(regexp_substr(value, '[^"]+', 1, 20)) as e
from tests_1;

A          B                      C             D             E
---------- ---------- ------------- ------------- -------------
2015-01-01 2015-12-31   11111111111    1111111111    1234567890
4
Alex Poole

De Oracle 18c vous pouvez utiliser opérateur TREAT AS JSON :

Améliorations SQL pour JSON

  • Vous pouvez spécifier qu'une expression SQL donnée renvoie des données JSON en utilisant TREAT (... AS JSON).

  • TREAT (... AS JSON) vous permet de spécifier que la valeur de retour d'une expression SQL donnée doit être traitée comme une donnée JSON. De telles expressions peuvent inclure des appels de fonction PL/SQL et des colonnes spécifiées par une clause SQL WITH. Les nouvelles vues de guide de données facilitent l'accès aux informations de chemin et de type pour les champs JSON, qui sont enregistrées pour les guides de données sauvegardés par index. Le renvoi de données JSON générées et interrogées dans des instances LOB élargit la portée de l'utilisation des données relationnelles.


Cet opérateur fournit un moyen d'informer la base de données que le contenu d'un VARCHAR2, BLOB, CLOB doit être traité comme contenant du JSON. Ceci active un certain nombre de fonctionnalités utiles, notamment la possibilité d'utiliser la "syntaxe simplifiée" sur des objets de base de données dépourvus de contrainte "IS JSON".

Et dans votre exemple:

create table Test_1(val CLOB);
create table Test_2(a date,b date,c number,d number, e number);

INSERT INTO Test_1(val)
VALUES('{ 
"a":"01/01/2015",
"b":"31/12/2015",
"c":"11111111111",
"d":"1111111111",
"e":"1234567890"
}');

INSERT INTO Test_2(a,b,c,d,e)
SELECT sub.val_as_json.a,
       sub.val_as_json.b,
       sub.val_as_json.c,
       sub.val_as_json.d,
       sub.val_as_json.e
FROM (SELECT TREAT(val as JSON) val_as_json
      FROM Test_1) sub;
COMMIT;

db <> fiddle demo

1
Lukasz Szozda