2015-03-02 3 views
0

Я использую SQL Server 2012 и пытаюсь вставить данные в несколько таблиц из XML-строки, содержащей данные. Проблема и путаница проистекают из XML, содержащего несколько узлов, поэтому это не только одна запись за раз.TSQL Query Вставка данных из xPath

В связи с этим, я использую метод вывода для вставки данных вместе с идентификатором, поэтому я знаю результат каждой из вставленных записей.

Моя проблема связана со структурой строки XML, она не вставляет все необходимые ему данные.

Вот блок кода я работаю вместе с SQL Fiddle:

Fiddle: 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 Узлы.

Первым шагом в запросе является создание всех сегментов, которые работают нормально. Каждый сегмент создается, а Identity хранится в таблице temp с выхода.

Далее, мне нужно создать записи для каждого узла данных, используя Identity его родительского сегмента. Я делаю это, объединяя временную таблицу с выходом некоторых своих данных, чтобы получить необходимую информацию.

Проблема с этим связана с несколькими узлами details, он имеет доступ только к первому и сохраняет свои данные.

Результат в последнем утверждении с использованием этого примера должен содержать 9 записей. На каждом сегменте есть 3 details узлов и всего 3 сегмента.

Не знаете, как это осуществить, но это сводит меня с ума.

Спасибо за любую помощь.

+0

Если я чего-то не упускаю, вы выбираете первое значение из возвращаемого времени Xpath, которое вы делаете [1]. Где бы вы ожидали получить [2]? – TheNorthWes

+0

Во второй части запроса, где я просто выбираю вывод и присоединяюсь к tmpTable, он получает доступ только к первому узлу с данными, где всего 3 сегмента. Проблема исходит из этого фрагмента кода. Я считаю, что 'INNER JOIN @ xml.nodes ('/ root/segment') AS ParamValues ​​(x1)' как его соединение на уровне сегмента, а затем поиск в 'details'. Тем не менее, его единственный поиск в первом узле деталей, который он находит, хотя есть более одного. – SBB

+0

Конечным результатом должно быть создание 3 сегментов (в настоящее время это делается), а затем в отдельной таблице 9 записей «детали», которые ссылаются на идентификатор их родительского сегмента в таблице сегментов. – SBB

ответ

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

0

Ваш последний SELECT еще перебором <segment> узлов, из которых есть только 3. Вы должны пролить его на <details> уровне, используя другой CROSS APPLY:

-- 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')