具有不同子级的Sql XML路径

具有不同子级的Sql XML路径,sql,tsql,sqlxml,Sql,Tsql,Sqlxml,我已经做了很多XML PATH语句,但这一条我没有意识到,甚至可能不可能有多个不同的子级 最终结果应该是这样的 <Process> <TaskList> <SqlTask Name="Get Report Parameters"> <StoredProcName>GetReportParameters</StoredProcName> <ConnectionName>Local</Connect

我已经做了很多XML PATH语句,但这一条我没有意识到,甚至可能不可能有多个不同的子级

最终结果应该是这样的

<Process>
<TaskList>
<SqlTask Name="Get Report Parameters">
    <StoredProcName>GetReportParameters</StoredProcName>
        <ConnectionName>Local</ConnectionName>
        <DataTableName>DistributionList</DataTableName>
        <Parameters>
              <Parameter>
            <Name>ReportName</Name>
            <Value>TheReprot</Value>
            <Type>String</Type>
              </Parameter>
        </Parameters>
  </SqlTask>
  <LoopTask Name="Loop Report Creation" ContainerKey="DistributionList">
  <TaskList>
        <ReportTask Name="Report In Loop">   
    </ReportTask>
</TaskList>
  </LoopTask>
  <SqlTask Name="Get Email Addresses">
    <StoredProcName>GetMailingAddress</StoredProcName>
        <ConnectionName>Local</ConnectionName>
        <DataTableName>EmailList</DataTableName>

  </SqlTask>
  <LoopTask Name="Loop Mail Creation" ContainerKey="EmailList">
<TaskList>
        <MailTask Name="Send Email In Loop">       
        </MailTask>
</TaskList>
  </LoopTask>
</TaskList>
</Process>

是的,你有很多问题要用你的样品来解决

首先,我将给出答案,不过请注意,为了正确地进行分层处理,它必须是一个递归函数,因此您提供的测试数据必须在永久表中创建,而不是在临时表中创建(更简单) 然后我会指出一些有用的技术,我用在它来解决这个问题

ALTER FUNCTION GetTasks (@ParentId varchar(255)= NULL) 
RETURNS
XML
BEGIN
DECLARE @ReturnXML XML

SELECT @ReturnXML = 
(
    SELECT
    (
        SELECT 
            CONVERT(XML,
                --Main task start tag
                '<'+master_t.TaskType+' Name="'+master_t.TaskName+'">'+ 
                    CONVERT(VARCHAR(MAX),
                        (

                            SELECT
                            dbo.GetTasks(master_t.TaskId),
                            (
                                SELECT 
                                    CONVERT(XML,'<'+DetailName+'>'+DetailValue+'</'+DetailName+'>')
                                FROM
                                    TASK_DETAILS t 
                                WHERE
                                    TaskId = master_t.TaskId
                                FOR XML PATH(''),Type
                            ),
                            (
                                SELECT Name,Value,Type FROM TASK_PARAMETERS t 
                                WHERE TaskId=master_t.TaskId
                                FOR XML PATH('Parameter'),Type
                            ) 'Parameters'
                            FOR XML PATH(''),Type 
                        )
                    )
                    +
                --Main task end tag
                '</'+master_t.TaskType+'>'
            )
        FROM 
            TASK master_t
        WHERE 
            --Effectively ignore the parentId field if it is not passed.
            ISNULL(ParentTaskId,'') = CASE WHEN @ParentId IS NULL THEN '' ELSE @ParentId END


        FOR XML PATH(''),Type
    ) 'TaskList'  FOR XML PATH(''),Type
) 

RETURN @ReturnXML
END
GO
对,我认为值得注意的技术有:

a) 您可以通过简单地从字符串构建xml节点来手动创建xml节点——如果节点名称在表中,这非常有用。您需要注意的唯一一点是,要在块周围放置一个打开和关闭标记,您可能必须首先将块转换为字符串,固定标记,然后将整个内容转换为xml(零碎的将无法工作,因为convert to xml函数希望您提供格式良好的xml)

b) 有时,您必须将内容嵌套在括号中,以便在所有子标记周围实现标记。。。 举个例子可以更清楚地说明这一点:

 SELECT 
    TaskName
    FROM TASK t
    FOR XML PATH('SomeRoot')
将产生:

<SomeRoot>
  <TaskName>Get Report Parameters</TaskName>
</SomeRoot>
<SomeRoot>
  <TaskName>Loop Report Creation</TaskName>
</SomeRoot>
<SomeRoot>
  <TaskName>Report In Loop</TaskName>
</SomeRoot>
<SomeRoot>
  <TaskName>Get Email Addresses</TaskName>
</SomeRoot>
<SomeRoot>
  <TaskName>Loop Mail Creation</TaskName>
</SomeRoot>
<SomeRoot>
  <TaskName>Send Email In Loop</TaskName>
</SomeRoot>
如果节点名称是静态的(请注意XML路径(“”,Type,这基本上确保了XML路径返回XML类型数据以供进一步处理,并且不会转义)

如果节点名称不是静态的,那么您将不得不这样做,需要在字符串之间进行转换以使其正常工作

SELECT 
    CONVERT(XML,
        '<'+DynamicName+'>' + 
        CONVERT(VARCHAR(MAX),
                (
                    SELECT 
                        TaskName
                    FROM TASK t
                    FOR XML PATH(''),Type
                )
            ) +
            '</'+DynamicName+'>'  
    )
FROM
    (SELECT 'Test' as DynamicName) a
将返回包含两列的单行,但如果随后将XML路径(“”)应用于整个行,则将它们组合在同一级别上

SELECT 
    (SELECT top 1 * FROM TASK FOR XML PATH(''),Type),
    (SELECT top 1 * FROM TASK_DETAILS FOR XML PATH(''),Type)
FOR XML PATH('Root')    
d) 列名通过XML路径转换为节点。属性非常简单,因为您只需为列提供一个别名,即适当的xsl路径 e、 “MyNodeName\@MyAttributeName”显然这排除了也动态命名的属性。因此,在本例中,我只是再次从字符串构建xml。顺便说一句,这就是为什么动态节点名称是一个如此糟糕的主意——您基本上允许例程通过表中的数据创建新的属性名称和节点名称。。。这意味着您无法为例程创建一个合适的模式,因为您事先不知道表中可能有哪些数据

继续:)

因此,考虑到这些构建块,最简单的方法是从最深层次开始工作,然后逐块构建,然后像上面那样组合

我为您的查询这样做了,并最终意识到,要使其分层工作(即n嵌套级别),我必须编写一个返回XML的函数,该函数称为将parentnode传递给它(以便该函数知道过滤结果集的目的)。如果你的等级结构是病态的和循环的,这将是一个可怕的死亡

好的-希望里面有你可以合作的东西。这是一个纯粹面向XML PATH()的解决方案——有一些可供选择的XML方法在不同的情况下很有用

SELECT 
(
    SELECT 
        TaskName
    FROM TASK t
    FOR XML PATH(''),Type
) 
FOR XML PATH('SomeRoot')
SELECT 
    CONVERT(XML,
        '<'+DynamicName+'>' + 
        CONVERT(VARCHAR(MAX),
                (
                    SELECT 
                        TaskName
                    FROM TASK t
                    FOR XML PATH(''),Type
                )
            ) +
            '</'+DynamicName+'>'  
    )
FROM
    (SELECT 'Test' as DynamicName) a
SELECT 
    (SELECT top 1 * FROM TASK FOR XML PATH(''),Type),
    (SELECT top 1 * FROM TASK_DETAILS FOR XML PATH(''),Type)
SELECT 
    (SELECT top 1 * FROM TASK FOR XML PATH(''),Type),
    (SELECT top 1 * FROM TASK_DETAILS FOR XML PATH(''),Type)
FOR XML PATH('Root')