Данные XML в базу данных PostgreSQL - PullRequest
11 голосов
/ 21 сентября 2011

Как лучше всего вставить данные XML (которые я получаю с веб-страницы) в базу данных PostgreSQL?
Я использую Java и мне нужна небольшая помощь в поиске хорошего способа считывания этих данных в базу данных.

Ответы [ 3 ]

19 голосов
/ 02 октября 2011

У меня есть рабочая реализация, где я делаю все внутри PostgreSQL без дополнительных библиотек.

Вспомогательная функция синтаксического анализа

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;

Дескриптор несколько значений

Приведенная выше реализация не обрабатывает несколько атрибутов в одном xpath. Вот для этого перегруженная версия f_xml_extract_val(). С помощью третьего параметра вы можете выбрать значения one (первый), all или dist (разные). Несколько значений объединяются в строку через запятую.

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'

Звоните:

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

Основная часть

Название целевой таблицы: tbl; чопорная. ключ: 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;

Важные примечания

  • Эта реализация проверяет первичный ключ, если вставленная строка уже существует, и обновляет в этом случае. Вставляются только новые строки.

  • Я использую временный промежуточный стол для ускорения процедуры.

  • Протестировано с Postgres 8.4 , 9.0 и 9.1 .

  • XML должен быть правильно сформирован.

  • pg_read_file() имеет ограничения на это. Инструкция :

    Использование этих функций разрешено только суперпользователям.

    И

    Доступны только файлы в каталоге кластера базы данных и log_directory.

Таким образом, вы должны поместить туда свой исходный файл - или создать символическую ссылку на ваш фактический файл / каталог.

или вы можете предоставить файл через Java в вашем случае (я сделал все это в Postgres).

Или Вы можете импортировать данные в 1 столбец 1 строки временной таблицы и получать их оттуда.

или вы можете использовать lo_import, как показано в этом ответе на dba.SE .

Этот блог пост Скотта Бэйли помог мне.

9 голосов
/ 21 сентября 2011

В Postgres есть (спасибо Дэниелу Лайонсу за указание на это) собственная поддержка XML , которую вы можете использовать для хранения своей таблицы.Однако если вы хотите уничтожить данные XML вручную, существуют разные возможности представления данных XML в базе данных.Первый вопрос должен быть, если вы хотите очень универсальное решение, которое сможет хранить любой XML-документ или документ, относящийся к вашему домену (т.е. разрешает только XML-документы определенной структуры).В зависимости от этого у вас будет очень гибкое универсальное представление, которое, однако, сложнее запрашивать (необходимый SQL будет довольно сложным).Если у вас более конкретный подход, запросы будут проще, но вам нужно будет создавать новые таблицы или добавлять новые атрибуты к существующим тальбам каждый раз, когда вы хотите сохранить документ другого типа или добавить поле в существующий документ;поэтому изменить схему будет сложнее (что является одним из основных преимуществ XML). Эта презентация должна дать вам несколько идей о том, каковы различные возможности.

Кроме того, вы можете подумать о переключении на какую-нибудь БД, поддерживающую Xquery, например DB2 .Возможность собственных запросов с использованием XQuery, языка, предназначенного для обработки XML, значительно упростит задачу.

ОБНОВЛЕНИЕ: учитывая ваш комментарий, ваши данные XML (которые вы связали с ) идеальнореляционная.Он может быть сопоставлен 1: 1 со следующей таблицей:

CREATE TABLE mynt (
    ID          SERIAL     ,
    myntnafn    CHAR(3)    ,
    myntheiti   Varchar(255) ,
    kaupgengi   Decimal(15,2) ,
    midgengi    Decimal(15,2) ,
    solugengi   Decimal(15,2) ,
    dagsetning  TimeStamp      
)

Таким образом, любой тег mynt будет записью в таблице и соответствующими вложенными тегами атрибутов.Типы данных, которые я собрал из ваших данных, могут быть неверными.Основная проблема, IMO, в том, что нет естественного первичного ключа, поэтому я добавил автоматически сгенерированный.

6 голосов
/ 21 сентября 2011

PostgreSQL имеет тип данных XML .Существует множество специфических для XML функций , которые можно использовать для запроса и изменения данных, например, с помощью xpath.

Со стороны Java вы можете делать вид, что работаете только со строками,но знайте, что данные корректно формируются на выходе, и это не позволит вам хранить не правильно сформированные данные.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...