2010-04-01 54 views
2

我已经做了很多XML PATH语句,但是这个逃避了我,甚至可能不会有多个不同的孩子。不同孩子的Sql XML路径

最终的结果应该是这样的

<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> 

下面是一些测试表和数据我有这么远。真正的问题是如何在同一个根目录下显示不同的子节点。我可以从列值中获取标签名称吗?

CREATE TABLE #TASK (
    TaskId INT IDENTITY(1,1) 
, ProcessId INT 
, TaskType VARCHAR(255) 
, TaskName VARCHAR(255) 
, ContainerKey VARCHAR(255) 
, ParentTaskId INT 
) 

CREATE TABLE #TASK_PARAMETERS 

(
    TaskId INT 
, Name VARCHAR(255) 
, Value VARCHAR(MAX) 
, [Type] VARCHAR(128) 
) 

CREATE TABLE #TASK_DETAILS 
(
    TaskId INT 
, DetailName VARCHAR(255) 
, DetailValue VARCHAR(MAX) 
) 

DECLARE @TaskId AS INT 
DECLARE @ParentTaskId AS INT 


INSERT INTO #TASK 
(
    ProcessId 
, TaskType 
, TaskName 
, ContainerKey 
, ParentTaskId 
) 
VALUES 
(
0 
, 'SqlTask' 
, 'Get Report Parameters' 
, NULL 
, NULL 
) 

SET @TaskId = @@IDENTITY 

INSERT INTO #TASK_DETAILS 
(
    TaskId 
, DetailName 
, DetailValue 
) 
VALUES 
(
    @TaskId 
, 'StoredProceName' 
, 'GetReportParamters' 
) 

INSERT INTO #TASK_DETAILS 
(
    TaskId 
, DetailName 
, DetailValue 
) 
VALUES 
(
    @TaskId 
, 'ConnectionName' 
, 'Local' 
) 

INSERT INTO #TASK_DETAILS 
(
    TaskId 
, DetailName 
, DetailValue 
) 
VALUES 
(
    @TaskId 
, 'DataTableName' 
, 'DistributionList' 
) 

INSERT INTO #TASK_PARAMETERS 

(
    TaskId 
, Name 
, Value 
, [Type] 
) 
VALUES 
(
    @TaskId 
, 'ReportName' 
, 'TheReprot' 
, 'String' 
) 


INSERT INTO #TASK 
(
    ProcessId 
, TaskType 
, TaskName 
, ContainerKey 
, ParentTaskId 
) 
VALUES 
(
0 
, 'LoopTask' 
, 'Loop Report Creation' 
, 'DistributionList' 
, NULL 
) 

SET @ParentTaskId = @@IDENTITY 


INSERT INTO #TASK 
(
    ProcessId 
, TaskType 
, TaskName 
, ContainerKey 
, ParentTaskId 
) 
VALUES 
(
0 
, 'ReportTask' 
, 'Report In Loop' 
, NULL 
, @ParentTaskId 
) 


INSERT INTO #TASK 
(
    ProcessId 
, TaskType 
, TaskName 
, ContainerKey 
, ParentTaskId 
) 
VALUES 
(
0 
, 'SqlTask' 
, 'Get Email Addresses' 
, NULL 
, NULL 
) 

SET @TaskId = @@IDENTITY 

INSERT INTO #TASK_DETAILS 
(
    TaskId 
, DetailName 
, DetailValue 
) 
VALUES 
(
    @TaskId 
, 'StoredProceName' 
, 'GetMailingAddress' 
) 

INSERT INTO #TASK_DETAILS 
(
    TaskId 
, DetailName 
, DetailValue 
) 
VALUES 
(
    @TaskId 
, 'ConnectionName' 
, 'Local' 
) 

INSERT INTO #TASK_DETAILS 
(
    TaskId 
, DetailName 
, DetailValue 
) 
VALUES 
(
    @TaskId 
, 'DataTableName' 
, 'EmailList' 
) 


INSERT INTO #TASK 
(
    ProcessId 
, TaskType 
, TaskName 
, ContainerKey 
, ParentTaskId 
) 
VALUES 
(
0 
, 'LoopTask' 
, 'Loop Mail Creation' 
, 'EmailList' 
, NULL 
) 

SET @ParentTaskId = @@IDENTITY 

INSERT INTO #TASK 
(
    ProcessId 
, TaskType 
, TaskName 
, ContainerKey 
, ParentTaskId 
) 
VALUES 
(
0 
, 'MailTask' 
, 'Send Email In Loop' 
, NULL 
, @ParentTaskId 
) 


SELECT * 
FROM #TASK 

SELECT * 
FROM #TASK_PARAMETERS 

SELECT * 
FROM #TASK_DETAILS 

回答

1

对,你有很多问题需要克服你的样品!

首先,我会给你答案,但要注意,为了正确处理分层结构,它必须是一个递归函数,所以你提供的测试数据必须在永久表中创建,而不是临时的(更简单) 然后我会指出一些我用它来解决问题的有用技巧。

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 

调用此函数是这样的:

SELECT dbo.GetTasks(NULL) 

正确的,我认为是值得一提的技术是:

a)你可以通过简单地从strings-构建他们手动创建XML节点如果节点名称在表中,这很有用。唯一需要注意的是,要在块的周围放置一个打开和关闭标记,您可能必须先将块转换为字符串,然后将标记转换为xml(零碎不会工作作为转换到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> 

获得“SomeRoot”出现在其周围,你可以这样做:

SELECT 
(
    SELECT 
     TaskName 
    FROM TASK t 
    FOR XML PATH(''),Type 
) 
FOR XML PATH('SomeRoot') 

如果节点名称是静态的(注意,XML PATH(“”)类型,基本上可以确保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的一个问题作为一个XML选择只是返回一个单一的XML对象。然后,您可以使用XML PATH将这些结果组合到树中。

例如

SELECT 
    (SELECT top 1 * FROM TASK FOR XML PATH(''),Type), 
    (SELECT top 1 * FROM TASK_DETAILS FOR XML PATH(''),Type) 

将返回单行两列,但如果你再申请XML PATH(“”)到整体,你在同一水平上

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 PATH转换为节点。属性非常简单,因为您只需为列指定一个别名,该别名就是适当的xsl路径 'MyNodeName \ @MyAttributeName'显然这排除了也是动态命名的属性。为此,在这个例子中,我再次从字符串中构建了xml。这就是为什么动态节点名称是一个坏主意 - 你基本上允许你的例程通过表中的数据创建新的属性名称和节点名称......这意味着你不能为你的例程创建一个像样的模式,就像你不提前知道哪些数据可能会在表...

谈完:)

因此,考虑到这些积木,做最简单的事情是最深层次的工作,并建立它一块一块,然后像上面那样结合。

我为你的查询做了这个,并最终意识到为了让它工作在层次上(即n层嵌套层次),我不得不写作一个返回XML的函数,该函数称为将父节点传递给它(以便函数知道如何过滤结果集)。如果你的等级不合格并且是循环的,那么这将会导致可怕的死亡。

好的,希望有一些东西可以在那里使用。这纯粹是面向XML PATH()的解决方案 - 在不同情况下可以使用替代的XML方法。