PostgreSQL Xpath选择元素及其子属性为两列

时间:2016-01-12 14:10:40

标签: xml postgresql xpath

我在postgreSQL中有一个表 history(id int,content xml)。其中一个id的XML内容如下

<history-data>
      <history recorded-date="20110601">
        <assignees>
          <assignee>
             <last-name>CIENA LUXEMBOURG</last-name>
          </assignee>
        </assignees>
        <assignors>
          <assignor execution-date="20110517">
              <last-name>NORTEL NETWORKS LIMITED</last-name>
          </assignor>
        </assignors>
      </history>
      <history recorded-date="20110601">
        <assignees>
          <assignee>
              <last-name>CIENA CORPORATION</last-name>
          </assignee>
        </assignees>
        <assignors>
          <assignor execution-date="20110527">
              <last-name>CIENA LUXEMBOURG</last-name>
          </assignor>
        </assignors>
      </history>
      <history recorded-date="20090430">
        <assignees>
          <assignee>
             <last-name>NORTEL NETWORKS</last-name> 
          </assignee>
        </assignees>
        <assignors>
          <assignor execution-date="20090424">
              <last-name>MAK, GARY</last-name>
          </assignor>
          <assignor execution-date="20090424">
              <last-name>VELEZ, EDGAR</last-name>
          </assignor>
        </assignors>
      </history>
    </history-data>

在这里,我希望获得姓氏&amp;它的相应执行日期。对于上面的例子,我想要以下输出

last-name                   execution-date
================            ==============
CIENA LUXEMBOURG              20110517
CIENA CORPORATION             20110527
NORTEL NETWORKS               20090424

我能够使用以下SQL查询生成所有可能的组合,但无法获得如上所述的输出

SELECT id, unnest(CAST(xpath('/history-data/history/assignees/assignee/last-name/text()',content) AS text)::text[]) AS last-name,
unnest(CAST(xpath('/history-data/history/assignors/assignor/@execution-date',content) AS text)::text[]) AS execution-date
FROM history
WHERE id = 10

有关如何做到这一点的任何建议?

2 个答案:

答案 0 :(得分:3)

您需要遍历所有history个节点,并使用xpath()函数获取相应的元素。默认情况下,xpath提取的结果返回 xml array ,这就是我们需要使用数组索引(...)[1]获取实际值的原因;示例查询可能如下:

SELECT
  (xpath('//assignee/last-name/text()',xml_element))[1] AS "last-name",
  (xpath('//assignor/@execution-date',xml_element))[1] AS "execution-date"
FROM (
  SELECT unnest(xpath('//history',content)) AS xml_element FROM history
  WHERE id = 10
) t;

结果是:

     last-name     | execution-date 
-------------------+----------------
 CIENA LUXEMBOURG  | 20110517
 CIENA CORPORATION | 20110527
 NORTEL NETWORKS   | 20090424
(3 rows)

assignees有多个assagnee个节点时,查询应使用unnest()来获取所有数组元素:

SELECT
  unnest(xpath('//assignee/last-name/text()',xml_element)) AS "last-name",
  unnest(xpath('//assignor/@execution-date',xml_element)) AS "execution-date"
FROM (
  SELECT unnest(xpath('//history',content)) AS xml_element FROM history
  WHERE id = 10
) t;

答案 1 :(得分:2)

您的要求是找到所有受让人,并且独立地找到所有执行日期,并返回笛卡尔积,这可能不是您真正想要的。

你想要的是:

  • 找到所有history元素
  • 然后为每个history元素找到您感兴趣的文字/属性。

这意味着使用子查询:

SELECT
    unnest(xpath('./assignees/assignee/last-name/text()',item))::text,
    unnest(xpath('./assignors/assignor/@execution-date',item))::text
FROM (
    SELECT
        unnest(xpath('/history-data/history',content)) AS item
    FROM history
    WHERE id = 10
    ) s
GROUP BY 1,2;

请注意,如果您在单个assignee元素中有多个history,则可能会得到奇怪的结果。此外,不确定您是想要所有execution-date,还是只需要第一个,最后一个,或者......

修改

要获取所有assignee,但只列出第一个execution-date

SELECT
    unnest(xpath('./assignees/assignee/last-name/text()',item))::text,
    (xpath('./assignors/assignor/@execution-date',item))[1]::text
FROM (
    SELECT
        unnest(xpath('/history-data/history',content)) AS item
    FROM history
    WHERE id = 10
    ) s
GROUP BY 1,2;