2015-03-02 87 views
0

我正在使用SQL Server 2012,並試圖從包含數據的XML字符串中將數據插入到多個表中。問題和困惑源於包含多個節點的XML,因此它不僅僅是一次記錄。TSQL Query從xPath插入數據

由於這個原因,我使用輸出方法將數據與標識一起插入,以便我知道它插入的每條記錄的結果。

我的問題是由於XML字符串的結構,它沒有插入所有需要的數據。

下面是代碼,我用一個SQL小提琴一起工作的塊:

小提琴:http://sqlfiddle.com/#!6/d41d8/24236

DECLARE @xml xml = '<root> 
    <trainingEventID>572</trainingEventID> 
    <segment> 
     <segmentDate>03/03/2015</segmentDate> 
     <hours>4</hours> 
     <details> 
     <locale>653</locale> 
     <teammates>3</teammates> 
     <leaders>4</leaders> 
     </details> 
     <details> 
     <locale>655</locale> 
     <teammates>44</teammates> 
     <leaders>55</leaders> 
     </details> 
     <details> 
     <locale>657</locale> 
     <teammates>55</teammates> 
     <leaders>66</leaders> 
     </details> 
     <trainers> 
     <trainer> 
      <empID>User12341</empID> 
     </trainer> 
     </trainers> 
    </segment> 
    <segment> 
     <segmentDate>03/04/2015</segmentDate> 
     <hours>4</hours> 
     <details> 
     <locale>653</locale> 
     <teammates>3</teammates> 
     <leaders>4</leaders> 
     </details> 
     <details> 
     <locale>655</locale> 
     <teammates>44</teammates> 
     <leaders>55</leaders> 
     </details> 
     <details> 
     <locale>657</locale> 
     <teammates>55</teammates> 
     <leaders>66</leaders> 
     </details> 
     <trainers> 
     <trainer> 
      <empID>User1234</empID> 
     </trainer> 
     </trainers> 
    </segment> 
    <segment> 
     <segmentDate>03/13/2015</segmentDate> 
     <hours>4</hours> 
     <details> 
     <locale>653</locale> 
     <teammates>3</teammates> 
     <leaders>4</leaders> 
     </details> 
     <details> 
     <locale>655</locale> 
     <teammates>44</teammates> 
     <leaders>55</leaders> 
     </details> 
     <details> 
     <locale>657</locale> 
     <teammates>55</teammates> 
     <leaders>66</leaders> 
     </details> 
     <trainers> 
     <trainer> 
      <empID>User1234</empID> 
     </trainer> 
     </trainers> 
    </segment> 
</root>' 

-- Declare our temp tables 
DECLARE @tmpSeg TABLE (teSegmentID INT, trainingEventID INT, segmentDate DATE, nonProdHrs int); 
DECLARE @tmpEvents TABLE (teSegmentID INT IDENTITY(1,1), trainingEventID INT, segmentDate DATE, nonProdHrs INT); 

-- First, Insert the main segments 
INSERT INTO @tmpEvents(trainingEventID, segmentDate, nonProdHrs) 
OUTPUT Inserted.teSegmentID, Inserted.trainingEventID, Inserted.segmentDate, Inserted.nonProdHrs INTO @tmpSeg 
SELECT ParamValues.x1.value('../trainingEventID[1]', 'INT'), 
     ParamValues.x1.value('(segmentDate/text())[1]', 'DATE'), 
     ParamValues.x1.value('(hours/text())[1]', 'INT') 
FROM @xml.nodes('/root/segment') AS ParamValues(x1); 

SELECT * FROM @tmpSeg 

-- Now, we join on our temp table and insert the Segment Details 
SELECT s.teSegmentID, 
ParamValues.x1.value('(details/locale/text())[1]', 'INT') AS localeID, 
ParamValues.x1.value('(details/teammates/text())[1]', 'INT') AS teammates, 
ParamValues.x1.value('(details/leaders/text())[1]', 'INT') AS leaders, 
ParamValues.x1.value('(../trainingEventID/text())[1]', 'INT') AS eventID, 
ParamValues.x1.value('(segmentDate/text())[1]', 'DATE') AS date, 
ParamValues.x1.value('(hours/text())[1]', 'INT') AS hours 
FROM @tmpSeg AS s 
INNER JOIN @xml.nodes('/root/segment') AS ParamValues(x1) 
ON s.trainingEventID = ParamValues.x1.value('(../trainingEventID/text())[1]', 'INT') 
AND s.segmentDate = ParamValues.x1.value('(segmentDate/text())[1]', 'DATE') 
AND s.nonProdHrs = ParamValues.x1.value('(hours/text())[1]', 'INT') 

你可以從XML結構看,它被分解成幾部分。有一個segment,然後在該段內可以有多個Details節點。

查詢的第一步是創建所有看起來工作正常的段。每個段都被創建並且標識存儲在輸出的臨時表中。

接下來,我需要使用其父段的標識爲每個詳細信息節點創建記錄。我通過從輸出中加入一些臨時表來獲得所需的詳細信息。

此問題與多個details節點有關,它只訪問第一個節點並存儲其數據。

使用此示例的最後一條語句中的輸出應該包含9條記錄。每個分段有3個details節點,總共有3個分段。

不知道如何做到這一點,但它使我瘋狂。

感謝您的任何幫助。

+0

除非我遺漏了一些東西,否則您會從返回的Xpath時間中選擇第一個值[1]。你期望得到什麼?[2]? – TheNorthWes 2015-03-02 16:44:01

+0

在查詢的第二部分中,我只是選擇輸出並加入tmpTable,它只訪問第一個細節節點,其中每個節段總共有3個節點。這個問題來自於這段代碼,我相信'INNER JOIN @ xml.nodes('/ root/segment')AS ParamValues(x1)'作爲它在細分層級上的連接,然後查找細節。但是,它只能看到第一個細節節點,即使存在多個節點也是如此。 – SBB 2015-03-02 16:58:02

+0

最終的結果應該是創建3個分段(目前正在這樣做),但是在一個單獨的表中,9個「細節」記錄在分段表中引用其父分段的標識。 – SBB 2015-03-02 17:05:44

回答

1

您需要詳細情況的另一層面,首先,但也存在培訓者的問題。我在這裏解決了一些問題,所以隨時根據需要進行修改。

DECLARE @xml xml = '<root> 
    <trainingEventID>572</trainingEventID> 
    <segment> 
     <segmentDate>03/03/2015</segmentDate> 
     <hours>4</hours> 
     <details> 
     <locale>653</locale> 
     <teammates>3</teammates> 
     <leaders>4</leaders> 
     </details> 
     <details> 
     <locale>655</locale> 
     <teammates>44</teammates> 
     <leaders>55</leaders> 
     </details> 
     <details> 
     <locale>657</locale> 
     <teammates>55</teammates> 
     <leaders>66</leaders> 
     </details> 
     <trainers> 
     <trainer> 
      <empID>User12341</empID> 
     </trainer> 
     </trainers> 
    </segment> 
    <segment> 
     <segmentDate>03/04/2015</segmentDate> 
     <hours>4</hours> 
     <details> 
     <locale>653</locale> 
     <teammates>3</teammates> 
     <leaders>4</leaders> 
     </details> 
     <details> 
     <locale>655</locale> 
     <teammates>44</teammates> 
     <leaders>55</leaders> 
     </details> 
     <details> 
     <locale>657</locale> 
     <teammates>55</teammates> 
     <leaders>66</leaders> 
     </details> 
     <trainers> 
     <trainer> 
      <empID>User1234</empID> 
     </trainer> 
     </trainers> 
    </segment> 
    <segment> 
     <segmentDate>03/13/2015</segmentDate> 
     <hours>4</hours> 
     <details> 
     <locale>653</locale> 
     <teammates>3</teammates> 
     <leaders>4</leaders> 
     </details> 
     <details> 
     <locale>655</locale> 
     <teammates>44</teammates> 
     <leaders>55</leaders> 
     </details> 
     <details> 
     <locale>657</locale> 
     <teammates>55</teammates> 
     <leaders>66</leaders> 
     </details> 
     <trainers> 
     <trainer> 
      <empID>User1234</empID> 
     </trainer> 
     </trainers> 
    </segment> 
</root>' 

-- Declare temp tables 
DECLARE @tmpSeg TABLE (teSegmentID INT IDENTITY(1,1), trainingEventID INT, segmentDate DATE, nonProdHrs INT, trainer varchar(30)); 
DECLARE @tmpLocales TABLE (teSegmentID INT, trainingEventID INT/*, segmentDate DATE, nonProdHrs int*/, locale int, teammates int, leaders int); 
DECLARE @tmpTrainers TABLE (teSegmentID INT, trainingEventID INT, empID VARCHAR(30)); 

-- Get Segment info 
INSERT INTO @tmpSeg(trainingEventID, segmentDate, nonProdHrs, trainer) 
SELECT 
     ParamValues.x1.value('../trainingEventID[1]', 'INT') 
    , ParamValues.x1.value('segmentDate[1]', 'DATE') 
    , ParamValues.x1.value('hours[1]', 'INT') 
    , ParamValues.x1.value('trainers[1]/trainer[1]/empID[1]', 'VARCHAR(30)') 
FROM @xml.nodes('/root/segment') AS ParamValues(x1); 

SELECT * FROM @tmpSeg 

-- Get Segment-dependent trainer info 
INSERT INTO @tmpTrainers(teSegmentID, trainingEventID, empID) 
SELECT 
     S.teSegmentID 
     , D.trainingEventID 
     , D.empID 
FROM (
    SELECT 
      ParamValues.x1.value('empID[1]', 'VARCHAR(30)') AS empID 
     , ParamValues.x1.value('../../../trainingEventID[1]', 'INT') AS trainingEventID 
     , ParamValues.x1.value('../../segmentDate[1]', 'DATE') AS segmentDate 
     , ParamValues.x1.value('../../hours[1]', 'INT') AS nonProdHours 
    FROM @xml.nodes('/root/segment/trainers/trainer') AS ParamValues(x1) 
    ) D 
    INNER JOIN @tmpSeg S ON D.trainingEventID = S.trainingEventID 
     AND D.segmentDate = S.segmentDate 
     AND D.nonProdHours = S.nonProdHrs 

SELECT * FROM @tmpTrainers 

-- Get segment-dependent locale info 
INSERT INTO @tmpLocales 
SELECT 
     S.teSegmentID 
     , D.trainingEventID 
     , D.locale 
     , D.teammates 
     , D.leaders 
FROM (
    SELECT 
      ParamValues.x1.value('locale[1]', 'INT') AS locale 
     , ParamValues.x1.value('teammates[1]', 'INT') AS teammates 
     , ParamValues.x1.value('leaders[1]', 'INT') AS leaders 
     , ParamValues.x1.value('../../trainingEventID[1]', 'INT') AS trainingEventID 
     , ParamValues.x1.value('../segmentDate[1]', 'DATE') AS segmentDate 
     , ParamValues.x1.value('../hours[1]', 'INT') AS nonProdHours 
    FROM @xml.nodes('/root/segment/details') AS ParamValues(x1) 
    ) D 
    INNER JOIN @tmpSeg S ON D.trainingEventID = S.trainingEventID 
     AND D.segmentDate = S.segmentDate 
     AND D.nonProdHours = S.nonProdHrs 

SELECT * 
FROM @tmpLocales 
+0

這工作完美!感謝您對培訓師的更多想法,這也正是我前進的路線。 – SBB 2015-03-02 20:08:35

0

你最後SELECT仍然通過<segment>節點迭代,其中有3只,您必須使用另一種CROSS APPLY它脫落到<details>級別:

-- Now, we join on our temp table and insert the Segment Details 
SELECT s.teSegmentID, 
D.Detail.value('locale[1]', 'INT') AS localeID, 
D.Detail.value('teammates[1]', 'INT') AS teammates, 
D.Detail.value('leaders[1]', 'INT') AS leaders, 
ParamValues.x1.value('(../trainingEventID/text())[1]', 'INT') AS eventID, 
ParamValues.x1.value('(segmentDate/text())[1]', 'DATE') AS date, 
ParamValues.x1.value('(hours/text())[1]', 'INT') AS hours 
FROM @tmpSeg AS s 
INNER JOIN @xml.nodes('/root/segment') AS ParamValues(x1) 
CROSS APPLY ParamValues.x1.nodes('details') AS D(Detail) 
ON s.trainingEventID = ParamValues.x1.value('(../trainingEventID/text())[1]', 'INT') 
AND s.segmentDate = ParamValues.x1.value('(segmentDate/text())[1]', 'DATE') 
AND s.nonProdHrs = ParamValues.x1.value('(hours/text())[1]', 'INT')