XML-данные в базу данных PostgreSQL
каким будет лучший способ вставить XML-данные (которые я получаю с веб-страницы) в базу данных PostgreSQL?
Я использую Java и мне нужна небольшая помощь в поиске хорошего способа чтения этих данных в базу данных.
3 ответов
Postgres имеет (спасибо Даниэлю Лайонсу за указание) собственная поддержка XML который вы можете использовать для хранения таблицы. Однако если требуется вручную измельчить XML-данные, существуют различные возможности представления XML-данных в базе данных. Первый вопрос должен быть, если вы хотите очень общее решение, которое сможет хранить любой XML-документ или тот, который специфичен для вашего домена (т. е. разрешает только XML-документы определенной структуры). В зависимости от этого, вы будет иметь очень гибкое, универсальное представление, которое, однако, сложнее запросить (необходимый SQL будет довольно сложным). Если у вас есть более конкретный подход, запросы будут проще, но вам нужно будет создавать новые таблицы или добавлять новые атрибуты в существующие talbes каждый раз, когда вы хотите сохранить другой тип документа или добавить поле в существующий документ; поэтому изменение схемы будет сложнее (что является одним из основных преимуществ 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
тег будет запишите в таблицу и соответствующие под-теги атрибуты. Типы данных, которые я собрал из ваших данных, могут быть неправильными. Основная проблема в том, ИМО, что нет естественного первичного ключа, поэтому я добавил автогенерированный.
у меня есть рабочая реализация, где я делаю все внутри PostgreSQL без дополнительных библиотек.
вспомогательная функция анализа
CREATE OR REPLACE FUNCTION f_xml_extract_val(text, xml)
RETURNS text AS
$func$
SELECT CASE
WHEN ~ '@[[:alnum:]_]+$' THEN
(xpath(, ))[1]
WHEN ~* '/text()$' THEN
(xpath(, ))[1]
WHEN LIKE '%/' THEN
(xpath( || 'text()', ))[1]
ELSE
(xpath( || '/text()', ))[1]
END;
$func$ LANGUAGE sql IMMUTABLE STRICT;
дескриптор несколько значения
вышеуказанная реализация не обрабатывает несколько атрибутов на одном xpath. Вот это перегружен версия f_xml_extract_val()
для этого. С 3-м параметром вы можете выбрать one
(первый), all
или dist
(distinct) значения. Несколько значений агрегируются в строку, разделенную запятыми.
CREATE OR REPLACE FUNCTION f_xml_extract_val(_path text, _node xml, _mode text)
RETURNS text AS
$func$
DECLARE
_xpath text := CASE
WHEN ~~ '%/' THEN || 'text()'
WHEN lower() ~~ '%/text()' THEN
WHEN ~ '@\w+$' THEN
ELSE || '/text()'
END;
BEGIN
-- fetch one, all or distinct values
CASE
WHEN 'one' THEN RETURN (xpath(_xpath, ))[1]::text;
WHEN 'all' THEN RETURN array_to_string(xpath(_xpath, ), ', ');
WHEN 'dist' THEN RETURN array_to_string(ARRAY(
SELECT DISTINCT unnest(xpath(_xpath, ))::text ORDER BY 1), ', ');
ELSE RAISE EXCEPTION
'Invalid : >>%<<', ;
END CASE;
END
$func$ LANGUAGE plpgsql IMMUTABLE STRICT;
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(..)
.. mode is one of: one | all | dist'
звоните:
SELECT f_xml_extract_val('//city', x, 'dist');
основная часть
имя целевой таблицы: tbl
; prim. ключ: 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 max.
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 VOLATILE;
важные моменты:
эта реализация проверяет первичный ключ, если вставленная строка уже существует и обновления в этом случае. Только новые строки вставленный.
я использую временную промежуточную таблицу для ускорения процедуры.
-
pg_read_file()
имеет ограничений. Цитирую руководство:использование этих функций сводится к администратору.
и:
только файлы в каталоге кластера базы данных и log_directory могут быть доступный.
Так вы должны поставить там свой исходный файл или создать символическую ссылку на файл/каталог.
или вы можете предоставить файл через Java в вашем случае (я сделал все это внутри Postgres).
или вы можете импортировать данные в 1 столбец 1 строки временной таблицы и взять его оттуда.
или можно использовать lo_import
как показано в этом связанный ответ на dba.SE.
протестировано с Postgres 8.4, 9.0 и 9.1.
XML должен быть хорошо сформирован.
этот блог сообщение Скотта Бейли помог мне.
PostgreSQL имеет XML тип данных. Есть много специфические функции XML вы можете использовать для запроса и изменения данных, например, с xpath.
со стороны Java вы можете притвориться, что вы просто работаете со строками, но знаете, что данные хорошо сформированы на выходе, и это не позволит вам хранить плохо сформированные данные.