Tôi có một triển khai đang hoạt động, nơi tôi thực hiện mọi thứ bên trong PostgreSQL mà không có thư viện bổ sung.
Chức năng phân tích cú pháp phụ trợ
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;
Xử lý nhiều giá trị
Việc triển khai ở trên không xử lý nhiều thuộc tính tại một xpath. Đây là phiên bản quá tải của f_xml_extract_val()
cho điều đó. Với tham số thứ 3, bạn có thể chọn one
(đầu tiên), all
hoặc dist
(khác biệt) giá trị. Nhiều giá trị được tổng hợp thành một chuỗi được phân tách bằng dấu phẩy.
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'
Gọi:
SELECT f_xml_extract_val('//city', x, 'dist');
Phần chính
Tên của bảng mục tiêu:tbl
; nghiêm trang. khóa: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;
Các lưu ý quan trọng
-
Việc triển khai này kiểm tra khóa chính nếu hàng được chèn đã tồn tại và cập nhật trong trường hợp này. Chỉ các hàng mới được chèn.
-
Tôi sử dụng bảng tạm thời để tăng tốc quy trình.
-
Đã kiểm tra với Postgres 8.4 , 9.0 và 9.1 .
-
XML phải được định dạng tốt.
-
pg_read_file()
có những hạn chế đối với nó. Hướng dẫn sử dụng:Việc sử dụng các chức năng này bị hạn chế đối với người dùng siêu cấp.
Và:
Chỉ các tệp trong thư mục cụm cơ sở dữ liệu và
log_directory
có thể được truy cập.
Vì vậy, bạn phải đặt tệp nguồn của mình ở đó - hoặc tạo một liên kết tượng trưng đến tệp / thư mục thực của bạn.
Hoặc bạn có thể cung cấp tệp qua Java trong trường hợp của mình (tôi đã làm tất cả trong Postgres).
Hoặc bạn có thể nhập dữ liệu vào 1 cột trong 1 hàng của bảng tạm thời và lấy dữ liệu từ đó.
Hoặc bạn có thể sử dụng lo_import
như được trình bày trong câu trả lời liên quan này trên dba.SE.
- SQL để đọc XML từ tệp vào cơ sở dữ liệu PostgreSQL
Bài đăng trên blog này của Scott Bailey đã giúp tôi.