2010-04-01 65 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方法。