【问题标题】:XML Hierarchy in Oracle with a Parent Node from Another TableOracle 中的 XML 层次结构与另一个表中的父节点
【发布时间】:2012-12-28 20:28:59
【问题描述】:

我有以下表格:

CREATE TABLE DBO.FACT_SERVICES (
    SVC_ID              INTEGER             NOT NULL,
    SVC_NAME            VARCHAR(50)         NOT NULL,
    SVC_DESC            VARCHAR(2000)       NULL,
    STATUS              VARCHAR(50)         NULL
);

CREATE TABLE DBO.FACT_TYPES (
    FACT_ID             INTEGER             NOT NULL,
    FACT_NAME           VARCHAR(100)        NOT NULL,
    FACT_DESC           VARCHAR(2000)       NULL,
    PARENT_FACT_ID      INTEGER             NULL,
);

CREATE TABLE DBO.FACT_TYPE_SVC (
    FACT_ID             INTEGER             NOT NULL,
    SVC_ID              INTEGER             NOT NULL,
    STATUS              VARCHAR(50)         NULL
);

我创建了以下查询来获取 XML 层次结构:

SELECT
  XMLElement("services", 
    XMLAgg(
      XMLElement("service",
        XMLAttributes(svc.svc_id as "id", svc.svc_name as "name"),
        XMLElement("facts",
          (SELECT
          XMLAgg(
            XMLElement("fact", 
              XMLAttributes(FT.FACT_ID as "id", FT.FACT_NAME AS "name"),
              (SELECT
                XMLAgg(
                  XMLElement("fact",   
                    XMLAttributes(FT2.FACT_ID as "id", FT2.FACT_NAME as "name")
                  )
                )
                FROM
                  DBO.FACT_TYPES FT2 
                WHERE
                  FT2.PARENT_FACT_ID = FT.FACT_ID
              )                             
            )
          )
          FROM
            DBO.FACT_TYPES FT 
            INNER JOIN DBO.FACT_TYPE_SVC FTS ON FTS.FACT_ID = FT.FACT_ID
          WHERE
            FTS.SVC_ID = SVC.SVC_ID AND
            LOWER(FTS.STATUS) = 'active' AND
            PARENT_FACT_ID IS NULL
          )
        )
      )
    )
  )
FROM
  DBO.FACT_SERVICES SVC

这会创建以下 XML:

<services>
    <service id="1" name="ABC Plan">
        <facts>
            <fact id="77" name="Basic Service">
                <fact id="71" name="Form XYZ Schedules"></fact>
                <fact id="101" name="Per Participant Fee"></fact>
                <fact id="103" name="Base Fee"></fact>
            </fact>
        </facts>
    </service>
</services>

但是,我想编写查询,以便我可以拥有 FACT_TYPE 的无限子级。我正在使用 Oracle 中的 dbms_xmlgen.getXmlType(dbms_xmlgen.newContextFromHierarchy()) 函数,但我似乎无法让它与服务部分一起使用。我可以让它直接从 DBO.FACT_TYPES 表中工作:

SELECT 
  XMLElement("facts",
  (select dbms_xmlgen.getXmlType(dbms_xmlgen.newContextFromHierarchy('
        select level, 
                XMLElement("fact", 
                  XMLAttributes(ft.fact_id as "id", ft.fact_name as "name")
                )
        from
          dbo.fact_types ft
        start with parent_fact_id is null
        connect by prior fact_id = parent_fact_id
        order siblings by fact_name
  ')) from SYS.DUAL
  )
)AS XML_DATA 
FROM
  SYS.DUAL

返回:

<facts>
    <fact id="77" name="Basic Administrative Service">
        <fact id="103" name="Base Fee"/>
        <fact id="71" name="Form 5500+ Schedules"/>
        <fact id="101" name="Per Participant Fee"/>
    </fact>
</facts>

所以我尝试创建一个传递服务 ID 的函数,它返回应该进入服务层次结构的内部 XML:

功能

create or replace
FUNCTION     dbo.FACT_GET_FACTS_XML(SERVICE_ID INT) 
RETURN XMLTYPE AS
INNER_SELECT DBMS_XMLGEN.ctxhandle;
MY_RESULT XMLTYPE;
BEGIN
INNER_SELECT := DBMS_XMLGEN.newcontextfromhierarchy(
        'select level, 
            XMLElement("fact", 
              XMLAttributes(ft.fact_id as "id", ft.fact_name as "name")
            )
          from
            dbo.fact_types ft
            inner join dbo.fact_type_svc fts on fts.fact_id = ft.fact_id
          where 
            fts.svc_id = ' || to_char(SERVICE_ID) || ' 
          start with ft.parent_fact_id is null
          connect by prior ft.fact_id = ft.parent_fact_id
          order siblings by ft.fact_name');

MY_RESULT := DBMS_XMLGEN.getxmltype(INNER_SELECT);
DBMS_XMLGEN.closeContext(INNER_SELECT);

RETURN MY_RESULT;

END;

使用这个父查询:

SELECT 
  XMLElement("services", 
    XMLAgg(
      XMLElement("service",
        XMLAttributes(svc.svc_id as "id", svc.svc_name as "name"),
        (SELECT 
          XMLElement("facts",
          DBO.FACT_GET_FACTS_XML(SVC.SVC_ID))
        FROM
          SYS.DUAL)
      )
    )
  )
FROM
  DBO.FACT_SERVICES SVC

现在我得到了这个结果:

<services>
    <service id="1" name="ABC Plan">
        <facts>
            <fact id="77" name="Basic Service">
                <fact id="103" name="Base Fee"/>
                <fact id="71" name="Form XYZ Schedules"/>
                <fact id="101" name="Per Participant Fee"/>
                <fact id="103" name="Base Fee"/>
                <fact id="71" name="Form XYZ Schedules"/>
                <fact id="101" name="Per Participant Fee"/>
            </fact>
        </facts>
    </service>
    <service id="4" name="Health Retirement Account">
        <facts>
            <fact id="71" name="Form XYZ Schedules"/>
            <fact id="71" name="Form XYZ Schedules"/>
        </facts>
    </service>
</services>

我想要的 XML 如下:

<services>
    <service id="1" name="ABC Plan">
        <facts>
            <fact id="77" name="Basic Service">
                <fact id="71" name="Form XYZ Schedules"></fact>
                <fact id="101" name="Per Participant Fee"></fact>
                <fact id="103" name="Base Fee"></fact>
            </fact>
        </facts>
    </service>
    <service id="4" name="Health Retirement Account">
      <facts></facts>
    </service>
</services>

但是可以将孩子添加到 fact id="71" 并让它显示在下面。

编辑 这是我目前在表格中的数据

INSERT INTO DBO.FACT_TYPES (FACT_ID, FACT_NAME, FACT_DESC, PARENT_FACT_ID) VALUES (71, 'Form 5500+ Schedules', '', 77);
INSERT INTO DBO.FACT_TYPES (FACT_ID, FACT_NAME, FACT_DESC, PARENT_FACT_ID) VALUES (77, 'Basic Administrative Service', 'Master Group', );
INSERT INTO DBO.FACT_TYPES (FACT_ID, FACT_NAME, FACT_DESC, PARENT_FACT_ID) VALUES (101, 'Per Participant Fee', '', 77);
INSERT INTO DBO.FACT_TYPES (FACT_ID, FACT_NAME, FACT_DESC, PARENT_FACT_ID) VALUES (103, 'Base Fee', '', 77);

INSERT INTO DBO.FACT_TYPE_SVC (FACT_ID, SVC_ID, STATUS) VALUES (77, 1, 'ACTIVE');
INSERT INTO DBO.FACT_TYPE_SVC (FACT_ID, SVC_ID, STATUS) VALUES (77, 3, 'ACTIVE');
INSERT INTO DBO.FACT_TYPE_SVC (FACT_ID, SVC_ID, STATUS) VALUES (71, 1, 'ACTIVE');
INSERT INTO DBO.FACT_TYPE_SVC (FACT_ID, SVC_ID, STATUS) VALUES (71, 3, 'ACTIVE');
INSERT INTO DBO.FACT_TYPE_SVC (FACT_ID, SVC_ID, STATUS) VALUES (71, 4, 'ACTIVE');
INSERT INTO DBO.FACT_TYPE_SVC (FACT_ID, SVC_ID, STATUS) VALUES (101, 1, 'ACTIVE');
INSERT INTO DBO.FACT_TYPE_SVC (FACT_ID, SVC_ID, STATUS) VALUES (101, 3, 'ACTIVE');
INSERT INTO DBO.FACT_TYPE_SVC (FACT_ID, SVC_ID, STATUS) VALUES (101, 21, 'ACTIVE');
INSERT INTO DBO.FACT_TYPE_SVC (FACT_ID, SVC_ID, STATUS) VALUES (103, 1, 'ACTIVE');
INSERT INTO DBO.FACT_TYPE_SVC (FACT_ID, SVC_ID, STATUS) VALUES (103, 3, 'ACTIVE');

INSERT INTO DBO.FACT_SERVICES (SVC_ID, SVC_NAME, SVC_DESC, STATUS) VALUES (1, '401(k) Plan', '', 'Active');
INSERT INTO DBO.FACT_SERVICES (SVC_ID, SVC_NAME, SVC_DESC, STATUS) VALUES (4, 'Health Retirement Account', '', 'Active');
INSERT INTO DBO.FACT_SERVICES (SVC_ID, SVC_NAME, SVC_DESC, STATUS) VALUES (5, 'Profit Sharing Plan', '', 'Active');
INSERT INTO DBO.FACT_SERVICES (SVC_ID, SVC_NAME, SVC_DESC, STATUS) VALUES (2, 'Plan Money Purchase', '', 'Active');
INSERT INTO DBO.FACT_SERVICES (SVC_ID, SVC_NAME, SVC_DESC, STATUS) VALUES (3, '403(b) Plan', '', 'Active');
INSERT INTO DBO.FACT_SERVICES (SVC_ID, SVC_NAME, SVC_DESC, STATUS) VALUES (21, 'ESOP Plan', '', 'Active');
INSERT INTO DBO.FACT_SERVICES (SVC_ID, SVC_NAME, SVC_DESC, STATUS) VALUES (22, 'Health Savings Account', '', 'Active');
INSERT INTO DBO.FACT_SERVICES (SVC_ID, SVC_NAME, SVC_DESC, STATUS) VALUES (23, '457 Plan', '', 'Active');
INSERT INTO DBO.FACT_SERVICES (SVC_ID, SVC_NAME, SVC_DESC, STATUS) VALUES (24, 'Simple Plan ', '', 'Active');
INSERT INTO DBO.FACT_SERVICES (SVC_ID, SVC_NAME, SVC_DESC, STATUS) VALUES (25, '503c Plan', '', 'Active');
INSERT INTO DBO.FACT_SERVICES (SVC_ID, SVC_NAME, SVC_DESC, STATUS) VALUES (26, 'Cafeteria Plan', '', 'Active');

【问题讨论】:

    标签: xml oracle oracle11g hierarchy


    【解决方案1】:

    我想你想要的可能就是这个?

    select XMLElement("services",
               XMLAgg(
                 XMLElement("service",
                   XMLAttributes(svc.svc_id as "id", svc.svc_name as "name"),
                  XMLElement("facts",
                   (select dbms_xmlgen.getXmlType(dbms_xmlgen.newContextFromHierarchy('
                       select level,
                              XMLElement("fact",
                                XMLAttributes(ft.fact_id as "id", ft.fact_name as "name")
                              )
                        from fact_types ft
                             inner join fact_type_svc fts
                                     on fts.fact_id = ft.fact_id
                       where fts.SVC_ID = ' || svc.svc_id || '
                       start with ft.parent_fact_id is null
                     connect by prior ft.fact_id = ft.parent_fact_id
                       order siblings by ft.fact_name
                          '))
                     from dual
                    )
                  )
                )
              )
            ) a
       from fact_services svc;
    

    例如带有一些示例数据(忽略额外的 transform() 部分..我添加它只是为了强制缩进以提高可读性):

    SQL> insert into FACT_TYPES
      2  select 77, 'Basic Service', '', null from dual union all
      3  select 101, 'Per Participant Fee', '', 77 from dual union all
      4  select 103, 'Base Fee', '', 77 from dual union all
      5  select 71, 'Form XYZ Schedules', '', null from dual union all
      6  select 72, 'Form XYZ Schedules 2', '', 71 from dual union all
      7  select 200, 'Test', '', 103 from dual  union all
      8  select 201, 'Another child', '', 200 from dual  union all
      9  select 202, 'FooBar', '', 200 from dual;
    
    8 rows created.
    
    SQL> insert into FACT_SERVICES
      2  select 1, 'ABC Plan', '', 'ACTIVE' from dual union all
      3  select 4, 'Health Retirement Account', '', 'ACTIVE' from dual;
    
    2 rows created.
    
    SQL> insert into FACT_TYPE_SVC
      2  select 77, 1, 'ACTIVE' from dual union all
      3  select 101, 1, 'ACTIVE' from dual union all
      4  select 103, 1, 'ACTIVE' from dual union all
      5  select 200, 1, 'ACTIVE' from dual union all
      6  select 201, 1, 'ACTIVE' from dual union all
      7  select 202, 1, 'ACTIVE' from dual union all
      8  select 71, 4, 'ACTIVE' from dual union all
      9  select 72, 4, 'ACTIVE' from dual;
    
    8 rows created.
    
    SQL> commit;
    
    Commit complete.
    
    SQL> select XMLElement("services",
      2             XMLAgg(
      3               XMLElement("service",
      4                 XMLAttributes(svc.svc_id as "id", svc.svc_name as "name"),
      5                XMLElement("facts",
      6                 (select dbms_xmlgen.getXmlType(dbms_xmlgen.newContextFromHierarchy('
      7                     select level,
      8                            XMLElement("fact",
      9                              XMLAttributes(ft.fact_id as "id", ft.fact_name as "name")
     10                            )
     11                      from fact_types ft
     12                           inner join fact_type_svc fts
     13                                   on fts.fact_id = ft.fact_id
     14                     where fts.SVC_ID = ' || svc.svc_id || '
     15                     start with ft.parent_fact_id is null
     16                   connect by prior ft.fact_id = ft.parent_fact_id
     17                     order siblings by ft.fact_name
     18                        '))
     19                   from dual
     20                  )
     21                )
     22              )
     23            )
     24          ).transform(xmltype('<xsl:stylesheet version="1.0"
     25   xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     26   <xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
     27   <xsl:template match="node()|@*">
     28    <xsl:copy>
     29     <xsl:apply-templates select="node()|@*"/>
     30    </xsl:copy>
     31   </xsl:template>
     32  </xsl:stylesheet>')) a
     33     from fact_services svc;
    
    A
    --------------------------------------------------------------------------------
    
    <services>
     <service id="1" name="ABC Plan">
      <facts>
       <fact id="77" name="Basic Service">
        <fact id="103" name="Base Fee">
         <fact id="200" name="Test">
          <fact id="201" name="Another child">
          </fact>
          <fact id="202" name="FooBar">
          </fact>
         </fact>
        </fact>
        <fact id="101" name="Per Participant Fee">
        </fact>
       </fact>
      </facts>
     </service>
     <service id="4" name="Health Retirement Account">
      <facts>
       <fact id="71" name="Form XYZ Schedules">
        <fact id="72" name="Form XYZ Schedules 2">
        </fact>
       </fact>
      </facts>
     </service>
    </services>
    

    【讨论】:

    • 好吧,这很接近,但这与我编写的函数遇到的问题相同。由于该事实与服务相关,因此我在服务中得到了重复的事实。
    • @Matthew 你有样本数据吗?因为我可以看到重复的唯一原因是FACT_TYPE_SVC 中有多行相同的FACT_ID + SVC_ID 组合。
    【解决方案2】:

    在您提供的查询中:

    select 
        XMLElement("services",
            XMLAgg(
                XMLElement("service",
                    XMLAttributes(svc.svc_id as "id", svc.svc_name as "name"),
                    XMLElement("facts",
                        (select dbms_xmlgen.getXmlType(dbms_xmlgen.newContextFromHierarchy('
                            select level,
                                XMLElement("fact",
                                    XMLAttributes(ft.fact_id as "id", ft.fact_name as "name")
                                )
                            from 
                                dbo.fact_types ft
                                inner join dbo.fact_type_svc fts on fts.fact_id = ft.fact_id
                            where 
                                fts.SVC_ID = ' || svc.svc_id || '
                            start with ft.parent_fact_id is null
                            connect by prior ft.fact_id = ft.parent_fact_id
                            order siblings by ft.fact_name
                        ')) from sys.dual
                        )
                    )
                )
            )
        ) a
    from 
        dbo.fact_services svc;
    

    我得到以下结果:

    <services>
        <service id="1" name="401(k) Plan">
            <facts>
                <fact id="77" name="Basic Administrative Service">
                    <fact id="103" name="Base Fee" />
                    <fact id="71" name="Form 5500+ Schedules" />
                    <fact id="101" name="Per Participant Fee" />
                    <fact id="103" name="Base Fee" />
                    <fact id="71" name="Form 5500+ Schedules" />
                    <fact id="101" name="Per Participant Fee" />
                </fact>
            </facts>
        </service>
        <service id="4" name="Health Retirement Account">
            <facts>
                <fact id="71" name="Form 5500+ Schedules" />
                <fact id="71" name="Form 5500+ Schedules" />
            </facts>
        </service>
        <service id="5" name="Profit Sharing Plan ">
            <facts />
        </service>
        <service id="2" name="Plan Money Purchase">
            <facts />
        </service>
        <service id="3" name="403(b) Plan">
            <facts>
                <fact id="103" name="Base Fee" />
                <fact id="71" name="Form 5500+ Schedules" />
                <fact id="101" name="Per Participant Fee" />
                <fact id="77" name="Basic Administrative Service">
                    <fact id="103" name="Base Fee" />
                    <fact id="71" name="Form 5500+ Schedules" />
                    <fact id="101" name="Per Participant Fee" />
                </fact>
            </facts>
        </service>
        <service id="21" name="ESOP Plan ">
            <facts>
                <fact id="101" name="Per Participant Fee" />
                <fact id="101" name="Per Participant Fee" />
            </facts>
        </service>
        <service id="22" name="Health Savings Account">
            <facts />
        </service>
        <service id="23" name="457 Plan ">
            <facts />
        </service>
        <service id="24" name="Simple Plan ">
            <facts />
        </service>
        <service id="25" name="503c Plan">
            <facts />
        </service>
        <service id="26" name="Cafeteria Plan ">
            <facts />
        </service>
    </services>
    

    但如果我将svc.svc_idwhere 移动到inner join

    select 
        XMLElement("services",
            XMLAgg(
                XMLElement("service",
                    XMLAttributes(svc.svc_id as "id", svc.svc_name as "name"),
                    XMLElement("facts",
                        (select dbms_xmlgen.getXmlType(dbms_xmlgen.newContextFromHierarchy('
                            select level,
                                XMLElement("fact",
                                    XMLAttributes(ft.fact_id as "id", ft.fact_name as "name")
                                )
                            from 
                                dbo.fact_types ft
                                inner join dbo.fact_type_svc fts on fts.fact_id = ft.fact_id AND fts.SVC_ID = ' || svc.svc_id || '
                            start with ft.parent_fact_id is null
                            connect by prior ft.fact_id = ft.parent_fact_id
                            order siblings by ft.fact_name
                        ')) from sys.dual
                        )
                    )
                )
            )
        ) a
    from 
        dbo.fact_services svc;
    

    我得到了正确的 XML 结构:

    <services>
        <service id="1" name="401(k) Plan">
            <facts>
                <fact id="77" name="Basic Administrative Service">
                    <fact id="103" name="Base Fee" />
                    <fact id="71" name="Form 5500+ Schedules" />
                    <fact id="101" name="Per Participant Fee" />
                </fact>
            </facts>
        </service>
        <service id="4" name="Health Retirement Account">
            <facts />
        </service>
        <service id="5" name="Profit Sharing Plan ">
            <facts />
        </service>
        <service id="2" name="Plan Money Purchase">
            <facts />
        </service>
        <service id="3" name="403(b) Plan">
            <facts>
                <fact id="77" name="Basic Administrative Service">
                    <fact id="103" name="Base Fee" />
                    <fact id="71" name="Form 5500+ Schedules" />
                    <fact id="101" name="Per Participant Fee" />
                </fact>
            </facts>
        </service>
        <service id="21" name="ESOP Plan ">
            <facts />
        </service>
        <service id="22" name="Health Savings Account">
            <facts />
        </service>
        <service id="23" name="457 Plan ">
            <facts />
        </service>
        <service id="24" name="Simple Plan ">
            <facts />
        </service>
        <service id="25" name="503c Plan">
            <facts />
        </service>
        <service id="26" name="Cafeteria Plan ">
            <facts />
        </service>
    </services>
    

    就像 Oracle 在处理完第一个层次结构后忽略了 where 条件。谢谢您的帮助。

    【讨论】:

      猜你喜欢
      • 2019-07-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-06
      • 2019-11-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多