Sql XML Path с разными потомками - PullRequest
2 голосов
/ 01 апреля 2010

Я сделал много операторов 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

1 Ответ

1 голос
/ 14 июля 2010

Правильно, вам нужно решить множество проблем с вашим образцом!

Во-первых, я дам вам ответ, хотя учтите, что для правильной иерархической обработки это должна была быть рекурсивная функция,поэтому предоставленные вами тестовые данные должны были быть созданы в постоянных таблицах, а не во временных (более простых), а затем я укажу несколько полезных методов, которые я использовал в них для решения проблемы.

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 (piecemeal).не будет работать, так как функция 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 ('') , тип , который в основном гарантирует, что путь 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 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, которые могут быть полезны в различных ситуациях.

...