すべてを実行する実装が機能しています。 追加のライブラリなしで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;
複数のを処理する 値
上記の実装は複数の属性を処理しません 1つのxpathで。これは、f_xml_extract_val()
のオーバーロードされたバージョンです。 そのために。 3番目のパラメーターを使用すると、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のこの関連する回答で示されているように。
- XMLをファイルからPostgreSQLデータベースに読み込むSQL
スコットベイリーによるこのブログ投稿は私を助けてくれました。