从复杂的Oracle XMLTYPE数据中进行选择和插入

时间:2014-08-27 15:28:19

标签: xml oracle xmltype xmltable

我之前编写过简单的Oracle查询来提取XMLTYPE数据,但这种XML不同 - 我需要从属性,子元素及其各自的属性中提取信息。我也想写一个INSERT语句(最好是能够找到最高选项值并添加1的语句)。请考虑以下XML:

<metadata>
    <fields>
        <field name="cusInt01" label="Reference point">
            <option value="1">CB</option>
            <option value="2">CF</option>
            <option value="3">DF</option>
            <option value="4">EKB</option>
            <option value="5">ES</option>
            <option value="6">GL</option>
            <option value="7">GR</option>
            <option value="8">KB</option>
            <option value="9">KBE</option>
            <option value="10">MSL</option>
            <option value="11">PT</option>
            <option value="12">RB</option>
            <option value="13">RF</option>
            <option value="14">RT</option>
            <option value="15">UN</option>
            <option value="16">UNK</option>
        </field>
    </fields>
</metadata>

例如,我可以编写一个查询来提取所有字段名称:

select 
  field_names.*
FROM
  metadata m,
  XMLTABLE('/metadata/fields/field'
       PASSING xmltype(m.xml_string)
       COLUMNS field_name VARCHAR(32) PATH '@name') field_names;

如何编写可以以表格形式提取所有不同信息的查询?例如,我如何将其显示为:

field_name   |   field_label       |   option_value   |   option_label
cusInt01         Reference point       1                  CB
cusInt01         Reference point       2                  CF
cusInt01         Reference point       2                  DF

......等等?我一直试图拼凑一个查询,但到目前为止旋转我的车轮。

2 个答案:

答案 0 :(得分:2)

在您的示例中,数据处于多个级别。每个field都可以有多个option。 因此,您必须使用XMLTable分解field以及option元素。 首先,您应该中断field元素,其中option元素被映射为XMLType。然后将其传递给第二个XMLTable以进一步分解它。

数据设置:

CREATE TABLE metadata (xml_string VARCHAR2 (2000));

INSERT INTO metadata
     VALUES ('<metadata>
    <fields>
        <field name="cusInt01" label="Reference point">
            <option value="1">CB</option>
            <option value="2">CF</option>
            <option value="3">DF</option>
            <option value="4">EKB</option>
            <option value="5">ES</option>
            <option value="6">GL</option>
            <option value="7">GR</option>
            <option value="8">KB</option>
            <option value="9">KBE</option>
            <option value="10">MSL</option>
            <option value="11">PT</option>
            <option value="12">RB</option>
            <option value="13">RF</option>
            <option value="14">RT</option>
            <option value="15">UN</option>
            <option value="16">UNK</option>
        </field>
    </fields>
</metadata>');

COMMIT;

查询:

SELECT field.field_name,
       field.field_label,
       options.option_value,
       options.option_label
  FROM metadata m,
       XMLTABLE (
          'metadata/fields/field'
          PASSING xmltype (m.xml_string)
          COLUMNS field_name VARCHAR2 (32) PATH '@name',
                  field_label VARCHAR2 (32) PATH '@label',
                  field_options XMLTYPE PATH 'option') field,
       XMLTABLE (
          'option'
          PASSING field.field_options
          COLUMNS option_value NUMBER PATH '@value',
                  option_label VARCHAR2 (10) PATH '/') options;

结果:

FIELD_NAME    FIELD_LABEL        OPTION_VALUE    OPTION_LABEL
-------------------------------------------------------------
cusInt01      Reference point    1               CB           
cusInt01      Reference point    2               CF           
cusInt01      Reference point    3               DF           
cusInt01      Reference point    4               EKB          
cusInt01      Reference point    5               ES           
cusInt01      Reference point    6               GL           
cusInt01      Reference point    7               GR           
cusInt01      Reference point    8               KB           
cusInt01      Reference point    9               KBE          
cusInt01      Reference point    10              MSL          
cusInt01      Reference point    11              PT           
cusInt01      Reference point    12              RB           
cusInt01      Reference point    13              RF           
cusInt01      Reference point    14              RT           
cusInt01      Reference point    15              UN           
cusInt01      Reference point    16              UNK 

Oracle XML DB Developer's Guide处的类似示例。

答案 1 :(得分:1)

除了Eat A Peach的回答,我开发了一些INSTEAD OF查询来插入和更新数据。它们仍在开发中,所以有点粗糙,但我想我会把它们发布在这里,万一有人可能会觉得它们很方便。

  CREATE OR REPLACE VIEW FIELD_OPTIONS AS
  select 
    s.id as space_id, 
    s.prefix_code,
    fields.name AS FIELD_NAME,
    fields.label AS FIELD_LABEL,
    options.value as OPTION_VALUE,
    options.label AS OPTIONS_LABEL,
    options.mapping AS OPTIONS_MAPPING
  FROM
    metadata m,
    spaces s,
    XMLTABLE('/metadata/fields/field'
         PASSING xmltype(m.xml_string)
         COLUMNS name VARCHAR(32) PATH '@name',
                 label VARCHAR(64) PATH '@label',
                 options XMLTYPE PATH 'option') fields,
    XMLTABLE('/option'
        PASSING fields.options
        COLUMNS value NUMBER PATH '@value',
                label VARCHAR(64) PATH '.',
                mapping VARCHAR(64) PATH '@mapping') options
  where
    s.metadata_id = m.id;


  CREATE OR REPLACE TRIGGER INSERT_FIELD_OPTIONS
        INSTEAD OF INSERT ON FIELD_OPTIONS
        FOR EACH ROW
  DECLARE
    field_name VARCHAR(32);
    space_prefix_code VARCHAR(32);
    new_option_num NUMBER;
    new_option_label VARCHAR(64);
    new_option_mapping VARCHAR(64);
  BEGIN
    space_prefix_code:=:NEW.prefix_code;
    field_name:=:NEW.field_name;
    new_option_label:=:NEW.options_label;
    new_option_mapping:=:NEW.options_mapping;

    select --TODO: find out if options.value = 0
      max(options.value)+1 INTO new_option_num
    FROM
      metadata m,
      spaces s,
      XMLTABLE('/metadata/fields/field'
           PASSING xmltype(m.xml_string)
           COLUMNS name VARCHAR(32) PATH '@name',
                   label VARCHAR(64) PATH '@label',
                   options XMLTYPE PATH 'option') fields,
      XMLTABLE('/option'
          PASSING fields.options
          COLUMNS value NUMBER PATH '@value',
                  label VARCHAR(64) PATH '.') options
    where
      s.metadata_id = m.id AND
      s.prefix_code = space_prefix_code AND
      fields.name=field_name;

    update metadata set 
      xml_string=(INSERTCHILDXML(XMLTYPE(xml_string),
        '/metadata/fields/field[@name=''' || field_name || ''']',
        'option',
        XMLTYPE('<option mapping="' || new_option_mapping || '" value="' || TO_CHAR(new_option_num) || '">' || new_option_label || '</option>'))).getclobval() --must convert to clob as it is stored in the database that way
    WHERE
      id =(select metadata_id from spaces where prefix_code = space_prefix_code);
    DBMS_OUTPUT.put_line(CHR(9) || TO_CHAR(SQL%ROWCOUNT)||' rows updated');
  END;
  /

  CREATE OR REPLACE TRIGGER UPDATE_FIELD_OPTIONS
        INSTEAD OF UPDATE ON FIELD_OPTIONS
        FOR EACH ROW
  DECLARE
    field_name VARCHAR(32);
    space_prefix_code VARCHAR(32);
    new_options_label VARCHAR(64);
    new_option_mapping VARCHAR(64);
    new_option_value VARCHAR(64);
  BEGIN
    space_prefix_code:=:NEW.prefix_code;
    field_name:=:NEW.field_name;
    new_options_label:=:NEW.options_label;
    --new_option_mapping:=:NEW.options_mapping; --TODO: add this feature later
    new_option_value:=:NEW.option_value;

    update metadata set 
      xml_string=(UPDATEXML(XMLTYPE(xml_string),
        '/metadata/fields/field[@name=''' || field_name || ''']/option[@value=''' || new_option_value || ''']/text()',
        new_options_label)).getclobval() --must convert to clob as it's stored in the database that way
    WHERE
      id =(select metadata_id from spaces where prefix_code = space_prefix_code);
    DBMS_OUTPUT.put_line(CHR(9) || TO_CHAR(SQL%ROWCOUNT)||' rows updated');
  END;
  /