【问题标题】:Sql XML Path with different children具有不同子项的 Sql XML 路径
【发布时间】:2010-04-01 14:18:04
【问题描述】:

我已经完成了很多 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>

以下是我目前掌握的一些测试表和数据。问题实际上是如何在同一根目录下显示不同的子节点。我可以从列值派生标签名称吗?

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

【问题讨论】:

    标签: sql tsql sqlxml


    【解决方案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) 您可以通过简单地从字符串构建它们来手动创建 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>
    

    要让“SomeRoot”出现在它周围,您可以这样做:

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

    如果节点名是静态的(注意XML PATH(''),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
    

    c) 关于让不同子标签出现在同一级别上的问题,这很简单,您只需要记住多层选择的常见问题不再是 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 方法可以在不同的情况下使用。

    【讨论】:

      猜你喜欢
      • 2013-12-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多