PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

como posso criar um novo arquivo XML do banco de dados existente no banco de dados PostgreSQL usando java


Tenho uma implementação funcional onde faço tudo dentro do PostgreSQL sem bibliotecas adicionais.

Função de análise auxiliar

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;

Lidar com múltiplos valores


A implementação acima não lida com vários atributos em um xpath. Aqui está uma versão sobrecarregada de f_xml_extract_val() por isso. Com o 3º parâmetro você pode escolher one (o primeiro), all ou dist valores (distintos). Vários valores são agregados a uma string separada por vírgulas.
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'

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

Parte principal


Nome da tabela de destino:tbl; primo. chave: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;

Observações importantes


  • Esta implementação verifica em uma chave primária se a linha inserida já existe e atualiza nesse caso. Apenas novas linhas são inseridas.

  • Eu uso uma tabela de preparação temporária para acelerar o procedimento.

  • Testado com Postgres 8.4 , 9.0 e 9.1 .

  • XML tem que ser bem formado.

  • pg_read_file() tem restrições para isso. O manual:

    O uso dessas funções é restrito a superusuários.

    E:

    Somente arquivos dentro do diretório do cluster de banco de dados e do log_directory Pode ser acessado.

Então você tem que colocar seu arquivo fonte lá - ou criar um link simbólico para seu arquivo/diretório real.

Ou você pode fornecer o arquivo via Java no seu caso (eu fiz tudo dentro do Postgres).

Ou você pode importar os dados para 1 coluna de 1 linha de uma tabela temporária e levá-los de lá.

Ou você pode usar lo_import como demonstrado nesta resposta relacionada no dba.SE.
  • SQL para ler XML do arquivo no banco de dados PostgreSQL

Esta postagem no blog de Scott Bailey me ajudou.