PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

comment puis-je créer un nouveau fichier XML à partir d'une base de données existante dans la base de données PostgreSQL en utilisant java

J'ai une implémentation fonctionnelle où je fais tout dans PostgreSQL sans bibliothèques supplémentaires.

Fonction d'analyse auxiliaire

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;

Gérer plusieurs valeurs

L'implémentation ci-dessus ne gère pas les attributs multiples à un XPath. Voici une version surchargée de f_xml_extract_val() pour ça. Avec le 3ème paramètre, vous pouvez en choisir one (le premier), all ou dist valeurs (distinctes). Plusieurs valeurs sont agrégées dans une chaîne séparée par des virgules.

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'

Appel :

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

Partie principale

Nom de la table cible :tbl; prim. clé :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;

Remarques importantes

  • Cette implémentation vérifie sur une clé primaire si la ligne insérée existe déjà et met à jour dans ce cas. Seules les nouvelles lignes sont insérées.

  • J'utilise une table intermédiaire temporaire pour accélérer la procédure.

  • Testé avec Postgres 8.4 , 9.0 et 9.1 .

  • XML doit être bien formé.

  • pg_read_file() a des restrictions à cela. Le manuel :

    L'utilisation de ces fonctions est réservée aux superutilisateurs.

    Et :

    Uniquement les fichiers dans le répertoire du cluster de base de données et le log_directory est accessible.

Vous devez donc y mettre votre fichier source - ou créer un lien symbolique vers votre fichier/répertoire actuel.

Ou vous pouvez fournir le fichier via Java dans votre cas (j'ai tout fait dans Postgres).

Ou vous pouvez importer les données dans 1 colonne de 1 ligne d'une table temporaire et les prendre à partir de là.

Ou vous pouvez utiliser lo_import comme démontré dans cette réponse connexe sur dba.SE.

  • SQL pour lire le XML du fichier dans la base de données PostgreSQL

Ce billet de blog de Scott Bailey m'a aidé.