PostgreSQL的XML函数使用指南

xmlparse() 必须指定 DOCUMENT 或 CONTENT 模式:DOCUMENT 要求完整 XML(单根节点),CONTENT 允许片段;xpath() 需 xml 类型输入并正确处理命名空间;xmlserialize() 注意编码兼容性与空白控制;高频查询应避免在 WHERE 中直接用 xpath(),改用生成列或表达式索引。

xmlparse() 解析字符串为 XML 值时,必须指定 DOCUMENT 或 CONTENT

PostgreSQL 不允许直接把字符串当 XML 用,xmlparse() 是唯一入口,且强制要求声明解析模式。漏写 DOCUMENTCONTENT 会报错:ERROR: xmlparse requires a document or content keyword

区别在于:DOCUMENT 要求输入是完整、有且仅有一个根节点的 XML(如 ),而 CONTENT 允许片段(如 )。

  • 存整份配置文件?用 xmlparse(DOCUMENT 'x')
  • 只提取一段标签内容?用 xmlparse(CONTENT 'xy')
  • 若误用 DOCUMENT 解析片段,会报:ERROR: invalid XML document: invalid root element

xpath() 提取节点前,确保输入是 xml 类型且命名空间处理得当

xpath() 不接受 text 或 varchar,传入非 xml 值会直接报错:ERROR: function xpath(unknown, xml) does not exist。常见错误是忘了套一层 xmlparse()

更隐蔽的问题是命名空间。如果 XML 带 xmlns,XPath 表达式里不声明前缀,结果永远为空。

SELECT xpath('/root/item/text()', 
             xmlparse(DOCUMENT 'abc'));

上面返回空数组。正确做法是用 ARRAY[ARRAY['x', 'http://example.com']] 注册命名空间:

SELECT xpath('/x:root/x:item/text()', 
             xmlparse(DOCUMENT 'abc'),
                      ARRAY[ARRAY['x', 'http://examp

le.com']]);

xmlserialize() 输出时注意 encoding 和 whitespace 控制

xmlserialize()xml 值转成字符串,默认编码是 UTF-8,但如果你显式指定 DOCUMENT ENCODING 'ISO-8859-1',而内容含中文,就会报错:ERROR: character with byte sequence 0xe4 0xb8 0xad in encoding "UTF8" has no equivalent in encoding "ISO_8859_1"

另外,它默认保留原始换行与缩进;若用于生成 API 响应或比对哈希,建议加 INDENT 或手动 REPLACE() 清理空白:

  • 要压缩输出:先 xmlserialize(CONTENT x AS text),再用 regexp_replace(... , E'\\s+', ' ', 'g')
  • 要带缩进的可读格式:加 INDENT 子句,如 xmlserialize(CONTENT x AS text INDENT)
  • 别在 xmlserialize() 中混用 DOCUMENTCONTENT 模式去匹配原始解析方式,否则可能多出/少掉根节点

性能敏感场景下,避免在 WHERE 或 JOIN 条件中反复调用 xpath()

xpath() 是纯函数,无法走索引,每次调用都要全量解析 XML 文本并执行 XPath 引擎。如果表里有上万行 XML 字段,又在 WHERE xpath(...) = 'value' 中使用,查询会极慢。

可行解法只有两个:

  • 提前把关键字段抽出来,用生成列(PostgreSQL 12+)固化:ALTER TABLE docs ADD COLUMN title text GENERATED ALWAYS AS (xpath('//title/text()', content)::text[]) [1] STORED
  • 或建表达式索引:CREATE INDEX idx_docs_title ON docs USING btree ((xpath('//title/text()', content)::text[])[1]))
  • 注意:xpath() 返回的是 xml[] 数组,取值必须用下标(如 [1])并显式类型转换,否则索引无效

XML 字段本身不是为高频查询设计的,真要按内容检索,优先考虑 JSONB + GIN 索引,除非你明确需要 XML Schema 验证或 XSLT 处理能力。