PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

come posso creare un nuovo file XML dal database esistente nel database PostgreSQL usando java

Ho un'implementazione funzionante in cui faccio tutto all'interno di PostgreSQL senza librerie aggiuntive.

Funzione di analisi ausiliaria

CREATE OR REPLACE FUNCTION f_xml_extract_val(text, xml)
  RETURNS text AS
$func$
SELECT CASE
        WHEN $1 ~ '@[[:alnum:]_]+$' THEN
           (xpath($1, $2))[1]
        WHEN $1 ~* '/text()$' THEN
           (xpath($1, $2))[1]
        WHEN $1 LIKE '%/' THEN
           (xpath($1 || 'text()', $2))[1]
        ELSE
           (xpath($1 || '/text()', $2))[1]
       END;
$func$  LANGUAGE sql IMMUTABLE;

Gestisci multipli valori

L'implementazione di cui sopra non gestisce più attributi in un xpath. Ecco una versione sovraccaricata di f_xml_extract_val() per quello. Con il 3° parametro puoi scegliere one (il primo), all o dist valori (distinti). Più valori vengono aggregati in una stringa separata da virgole.

CREATE OR REPLACE FUNCTION f_xml_extract_val(_path text, _node xml, _mode text)
  RETURNS text AS
$func$
DECLARE
   _xpath text := CASE
                   WHEN $1 ~~ '%/'              THEN $1 || 'text()'
                   WHEN lower($1) ~~ '%/text()' THEN $1
                   WHEN $1 ~ '@\w+$'            THEN $1
                   ELSE                              $1 || '/text()'
                  END;
BEGIN
   -- fetch one, all or distinct values
   CASE $3
       WHEN 'one'  THEN RETURN (xpath(_xpath, $2))[1]::text;
       WHEN 'all'  THEN RETURN array_to_string(xpath(_xpath, $2), ', ');
       WHEN 'dist' THEN RETURN array_to_string(ARRAY(
            SELECT DISTINCT unnest(xpath(_xpath, $2))::text ORDER BY 1), ', ');
       ELSE RAISE EXCEPTION
          'Invalid $3: >>%<<', $3;
   END CASE;
END
$func$  LANGUAGE plpgsql;

COMMENT ON FUNCTION f_xml_extract_val(text, xml, text) IS '
Extract element of an xpath from XML document
Overloaded function to f_xml_extract_val(..)
$3 .. mode is one of: one | all | dist'

Chiama:

SELECT f_xml_extract_val('//city', x, 'dist');

Parte principale

Nome della tabella di destinazione:tbl; primitivo. chiave:id :

CREATE OR REPLACE FUNCTION f_sync_from_xml()
  RETURNS boolean AS
$func$
DECLARE
   datafile text := 'path/to/my_file.xml';  -- only relative path in db dir
   myxml    xml  := pg_read_file(datafile, 0, 100000000); -- arbitrary 100 MB
BEGIN
   -- demonstrating 4 variants of how to fetch values for educational purposes
   CREATE TEMP TABLE tmp ON COMMIT DROP AS
   SELECT (xpath('//some_id/text()', x))[1]::text AS id   -- id is unique  
        , f_xml_extract_val('//col1', x)          AS col1 -- one value
        , f_xml_extract_val('//col2/', x, 'all')  AS col2 -- all values incl. dupes
        , f_xml_extract_val('//col3/', x, 'dist') AS col3 -- distinct values
   FROM   unnest(xpath('/xml/path/to/datum', myxml)) x;

   -- 1.) DELETE?

   -- 2.) UPDATE
   UPDATE tbl t
   SET   (  col_1,   col2,   col3) =
         (i.col_1, i.col2, i.col3)
   FROM   tmp i
   WHERE  t.id = i.id
   AND   (t.col_1, t.col2, t.col3) IS DISTINCT FROM
         (i.col_1, i.col2, i.col3);

   -- 3.) INSERT NEW
   INSERT INTO tbl
   SELECT i.*
   FROM   tmp i
   WHERE  NOT EXISTS (SELECT 1 FROM tbl WHERE id = i.id);
END
$func$  LANGUAGE plpgsql;

Note importanti

  • Questa implementazione verifica su una chiave primaria se la riga inserita esiste già e aggiornamenti in questo caso. Vengono inserite solo nuove righe.

  • Uso una tabella di staging temporanea per velocizzare la procedura.

  • Testato con Postgres 8.4 , 9,0 e 9.1 .

  • XML deve essere ben formato.

  • pg_read_file() ha delle restrizioni. Il manuale:

    L'uso di queste funzioni è riservato ai superutenti.

    E:

    Solo file all'interno della directory del cluster di database e della log_directory si può accedere.

Quindi devi inserire il tuo file sorgente lì - o creare un collegamento simbolico al tuo file/directory attuale.

Oppure puoi fornire il file tramite Java nel tuo caso (ho fatto tutto all'interno di Postgres).

Oppure puoi importare i dati in 1 colonna di 1 riga di una tabella temporanea e prenderli da lì.

Oppure puoi usare lo_import come dimostrato in questa risposta correlata su dba.SE.

  • SQL per leggere XML dal file nel database PostgreSQL

Questo post sul blog di Scott Bailey mi ha aiutato.