2015-03-02 47 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')