2

我之前编写过简单的 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

……等等。想法?我一直在尝试拼凑一个查询,但到目前为止我的轮子还在转动。

4

2 回答 2

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中的类似示例。

于 2014-08-29T10:25:13.293 回答
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;
  /
于 2014-08-29T14:55:28.327 回答