当前位置: 首页 > 面试题库 >

具有不同子级的SQL XML路径

云捷
2023-03-14
问题内容

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

问题答案:

正确,您的样本有许多问题要克服!

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

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节点-
如果节点名称在表中,这将非常有用。您唯一需要了解的是,要在一个块周围放置一个打开和关闭标签,您可能必须先将该块转换为字符串,然后将其固定在标签上,然后再将整个内容转换为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)关于让不同的子级标签出现在同一级别上的问题,这是微不足道的,您只需要记住,多层select的常见问题不再是xml的问题,因为xml
select仅返回一个单个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方法,这些方法在不同情况下很有用。



 类似资料:
  • 我是XML和JAXB的新手。我读了很多关于XML、名称空间声明等的文章,但我目前面临着一个无法解决的JAXB问题。EPO上有一个关于响应文档的XSD文件。我可以与客户端一起调用EPO web服务并获取返回文档。我将显示这样一个返回文档的第一行。 正如您所看到的,定义了多个名称空间,但在文档正文中使用的名称空间只有两个,即ns2和NS3。我的问题是解组ns3:register-documents实体

  • 我正在尝试绘制一个只有线条但笔画宽度不同的对象。这可能吗? 我的尝试: 不成功,最后一个笔画宽度用于整个< code >路径。

  • 我正在用spring security saml扩展实现一个多租户应用程序。 我为每个租户提供了一个服务提供商(SP)。所有SP都运行在与SP特定的第二级域公开的同一服务器上: sp1。myapp。com/myapi/1/ 在每个SP元数据文件中,我都配置了特定于租户的AssertionConsumerService。 当我测试SSO登录时,当SP端收到身份提供者(IDP)的响应时,我会得到一个K

  • 问题内容: 我有一个清单清单。每个子列表的长度在1到100之间。每个子列表在一组数据中的不同时间包含一个粒子ID。我想在给定的时间形成所有粒子ID的列表。为此,我可以使用以下方法: list2将包含list中每个子列表的首个元素。我不仅要对第一个元素执行此操作,还要对1到100之间的每个元素执行此操作。我的问题是,每个子列表都不存在元素号100(或66或77或其他)。 有什么方法可以创建列表列表,

  • 我试图从下面的论文(http://dx.doi.org/10.1016/j.envsci.2011.08.004)中复制图表图1的概念: 它是一个直方图,其箱子宽度取决于的值,其高度取决于变量。图表中的精确值并不重要,而是要了解如何复制它。 以下代码为每个创建了一个具有两个特征的数据帧(and measure的宽度是减量,

  • 下面,我有一个全局应用的日志回溯配置。 我想全局使日志记录级别错误,除了一个类。对于一个特定的类,我想使日志记录级别 INFO。我该怎么做?