मेरे पास एक कार्यशील कार्यान्वयन है जहां मैं सब कुछ करता हूं अतिरिक्त पुस्तकालयों के बिना 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;
महत्वपूर्ण नोट
-
यह कार्यान्वयन प्राथमिक कुंजी पर जांच करता है कि क्या सम्मिलित पंक्ति पहले से मौजूद है और अपडेट इस मामले में। केवल नई पंक्तियाँ डाली जाती हैं।
-
मैं प्रक्रिया को तेज करने के लिए एक अस्थायी स्टेजिंग टेबल का उपयोग करता हूं।
-
पोस्टग्रेज के साथ परीक्षण किया गया 8.4 , 9.0 और 9.1 ।
-
एक्सएमएल अच्छी तरह से गठित होना चाहिए।
-
pg_read_file()
उस पर प्रतिबंध हैं। मैनुअल:इन कार्यों का उपयोग सुपरयूज़र तक ही सीमित है।
और:
<ब्लॉककोट>केवल डेटाबेस क्लस्टर निर्देशिका में फ़ाइलें और
log_directory
पहुँचा जा सकता है।
तो आपको अपनी स्रोत फ़ाइल वहां रखनी होगी - या अपनी वास्तविक फ़ाइल/निर्देशिका के लिए एक प्रतीकात्मक लिंक बनाना होगा।
या आप अपने मामले में जावा के माध्यम से फ़ाइल प्रदान कर सकते हैं (मैंने यह सब पोस्टग्रेज़ के अंदर किया था)।
या आप डेटा को एक अस्थायी तालिका की 1 पंक्ति के 1 कॉलम में आयात कर सकते हैं और इसे वहां से ले सकते हैं।
या आप lo_import
. का उपयोग कर सकते हैं जैसा कि dba.SE पर इस संबंधित उत्तर में दिखाया गया है।
- SQL फ़ाइल से PostgreSQL डेटाबेस में XML को पढ़ने के लिए
स्कॉट बेली की इस ब्लॉग पोस्ट ने मेरी मदद की।