SQL和XML以及复杂的JOIN

时间:2013-03-15 21:45:01

标签: sql sql-server xml

我正在尝试构建一个SQL查询或过程来从多个表构建复杂的XML结果。这是在SQL Server 2008 R2中。

所以我想要返回的XML是

<requests>
  <request>
    <field one/>
    <field two/>
    <person>
      <personinfo/>
      <roleinfo/>
      <addrinfo/>
      <!-- don't want <item> elements here -->
    </person>
    <person>
      <personinfo/>
      <roleinfo/>
      <addrinfo/>
      <!-- don't want <item> elements here -->
    </person>
    <item><iteminfo/></item> <- i want <item> elements to show up here
    <item><iteminfo/></item>
    <item><iteminfo/></item>
  </request>
  <request>
    ...
  </request>
<requests>

我可以让它在没有<item/>行的情况下工作,但是当我尝试将<item/>行加入到最终结果中时,它会将它们连接到<person/>元素而不是<request/>元素。这可能是我理解或使用连接的一个问题,但此时我正在踩水。

创建没有<item/>元素的XML的SQL看起来如下所示。

select actor-fields, person-fields, role-fields, address-fields
into #tempperson from actor
left join person on actor.personid = person.personid
left join address on address.personid = person.personid
left join role on role.personid = person.personid
order by personid

select request-fields
into #tempreq from request
order by requestid

select *
from #tempreq as request
left join #tempperson as person on person.requestid = request.requestid
order by request.requestid
for xml auto, root('requests'), elements;

我需要的是将返回所需XML的SQL。如果我向上面的SQL添加另一个连接,它会在<item/>节点内添加<person/>个节点,这不是我想要的。

我已阅读有关XML AUTO的文章,但这些示例不会返回这样的嵌套结构。也许我说这一切都错了。对此有何指导?

2 个答案:

答案 0 :(得分:0)

问题在于,如何在XML中呈现数据,使其在建模时具有与常规SQL不同的行为。要记住的是,您可以使用“For xml path ...”自定位节点。但是如果您要加入一个引用了具有显示子项关系的连接的表。当您不需要时,SQL可能会尝试强制使用XML元素。如果数据很复杂,您可以通过几种方式处理此嵌套问题:

  1. 将其转储到临时表,从而使MOOT任何SQL假设关于您的数据的任何ASSUMED关系。现在它认为它是一个完整的数据集,并且可以随心所欲地使用它。你不关心这些关系,你把它们扔掉了,并决定手动建模。

  2. 嵌套选择范围扩大到更大的主体。您也可以将嵌套选择放在更大的xml体中。我也用过这个。

  3. 试试这个自解压示例:

    declare @Person Table ( personID int identity, person varchar(8));
    
    insert into @Person values ('Brett'),('Sean'),('Chad'),('Michael'),('Ray'),('Erik'),('Queyn');
    
    declare @Orders table ( OrderID int identity, PersonID int, Desciption varchar(32), Amount int);
    
    insert into @Orders values (1, 'Shirt', 20),(1, 'Shoes', 50),(2, 'Shirt', 22),(2, 'Shoes', 52),(3, 'Shirt', 20),(3, 'Shoes', 50),(3, 'Hat', 20),(4, 'Shirt', 20),(5, 'Shirt', 20),(5, 'Pants', 30),
    (6, 'Shirt', 20),(6, 'RunningShoes', 70),(7, 'Shirt', 22),(7, 'Shoes', 40),(7, 'Coat', 80)
    
    Select 
        p.person as "@Person"
    ,   o.Desciption as "Order/@Description"
    ,   o.Amount as "Order/*"
    from @Person p
        join @Orders o on p.personID = o.PersonID
    for xml path('Person'), root('People')
    -- this will present my data nice but you may have more complex data than this
    
    if object_id('tempdb..#Temp') is not null
        drop table #temp
    
    Select 
        p.person 
    ,   o.Desciption
    ,   o.Amount
    into #Temp
    from @Person p
        join @Orders o on p.personID = o.PersonID
    
    Select
        person as "@Person"
    ,   Desciption as "Order/@Description"
    ,   Amount as "Orders"
    from #Temp
    for xml path('Person'), root('People')
    

    对于更复杂的嵌套选择原则,在更大的xml生成体内执行xml。我不记得为什么但是'TYPE'语句基本上告诉SQL引擎它可以嵌套。如果你忘记它并在其他xml中嵌入一个xml选项,它可能看起来像垃圾。

    -- Maybe I need to nest XMl inside of OTHER XML
    Select 
        p.person as "@Person"
    ,   (
        select
            o.Desciption 
        ,   o.Amount
        from @Orders o 
        where o.PersonID = p.personID
        for xml path('Order'), root('Orders'), type
        ) 
    from @Person p 
    for xml path('Person'), root('People')
    

答案 1 :(得分:0)

实际上,在这种情况下,似乎最有效的方法是使用 XML AUTO, TYPE 的变体。作为一个非常简单的例子,没有JOINsWHEREs似乎在这里做了诀窍),如下所示。 (老实说,在我正在处理的实际问题中,我有这个加上JOINs工作正常,但JOIN的微妙之处仍然困扰着我,特别是当与这类问题结合时。)< / p>

CREATE TABLE #Person ( PerID int identity, FullName varchar(20))
CREATE TABLE #Orders ( OrdID int identity, PerID int, OrdDt varchar(32))
CREATE TABLE #Items ( ItemID int identity, OrdID int, IDesc varchar(32), Amt int)
CREATE TABLE #Addresses ( AddrID int identity, PerID int, FullAddr varchar(50))

insert into #Person values ('Brett Doe')
insert into #Person values ('Sandy Sue')
insert into #Orders values (1, '1/1')
insert into #Orders values (1, '3/1')
insert into #Orders values (2, '1/1')
insert into #Orders values (2, '2/1')
insert into #Items values (1, 'Horse', 1000)
insert into #Items values (2, 'Hat', 10)
insert into #Items values (2, 'Boots', 100)
insert into #Items values (3, 'Belt', 20)
insert into #Items values (4, 'Pistol', 500)
insert into #Items values (4, 'Ammo', 100)
insert into #Addresses values (1, '123 Main, Somewhere, TX')
insert into #Addresses values (2, '345 Oak, Somewhere Else, TX')

SELECT Person.PerID
    , FullName
    , (SELECT OrdDt
        , (SELECT IDesc
            , Amt, Items.OrdID
            FROM #Items as Items
            WHERE Items.OrdID = Orders.OrdID
            FOR XML AUTO, TYPE
            )
        FROM #Orders AS Orders
        WHERE Person.PerID = Orders.PerID
        FOR XML AUTO, TYPE, ELEMENTS
    )
    , (SELECT FullAddr
        FROM #Addresses AS Addresses
        WHERE Person.PerID = Addresses.PerID
        FOR XML AUTO, TYPE, ELEMENTS
    )
FROM #Person AS Person
FOR XML AUTO, TYPE, ELEMENTS

DROP TABLE #Person
DROP TABLE #Orders
DROP TABLE #Items
DROP TABLE #Addresses

输出

<Person>
  <PerID>1</PerID>
  <FullName>Brett Doe</FullName>
  <Orders>
    <OrdDt>1/1</OrdDt>
    <Items IDesc="Horse" Amt="1000" OrdID="1" />
  </Orders>
  <Orders>
    <OrdDt>3/1</OrdDt>
    <Items IDesc="Hat" Amt="10" OrdID="2" />
    <Items IDesc="Boots" Amt="100" OrdID="2" />
  </Orders>
  <Addresses>
    <FullAddr>123 Main, Somewhere, TX</FullAddr>
  </Addresses>
</Person>
<Person>
  <PerID>2</PerID>
  <FullName>Sandy Sue</FullName>
  <Orders>
    <OrdDt>1/1</OrdDt>
    <Items IDesc="Belt" Amt="20" OrdID="3" />
  </Orders>
  <Orders>
    <OrdDt>2/1</OrdDt>
    <Items IDesc="Pistol" Amt="500" OrdID="4" />
    <Items IDesc="Ammo" Amt="100" OrdID="4" />
  </Orders>
  <Addresses>
    <FullAddr>345 Oak, Somewhere Else, TX</FullAddr>
  </Addresses>
</Person>

我通过在此处搜索关联或相关问题来找到此信息,例如FOR XML EXPLICIT

链接的MSDN文章FOR XML Query Compared to Nested FOR XML Query很有帮助。