将XML数据读入分层表

时间:2017-08-25 14:17:03

标签: sql-server tsql sql-server-2012 xquery sqlxml

我有用于存储测验结果的XML数据。我需要将它转换成两个表,一个包含问题,另一个包含答案,但最重要的是,它们之间存在关系。

目前这种关系仅存在于XML结构中(没有ID值等)。

经过一天的研究和测试不同的方法,我已经提取了两个部分,但无法弄清楚如何创建层次结构:

declare @xml xml = N'<quizresult>
  <question>
    <questionText>Which fire extinguisher is most suitable for a waste paper basket fire?</questionText>
    <answer number="0" value="0" chosen="0" imageURL="">Powder</answer>
    <answer number="1" value="0" chosen="0" imageURL="">Carbon Dioxide (CO2)</answer>
    <answer number="2" value="1" chosen="1" imageURL="">Water (H2O)</answer>
    <answer number="3" value="0" chosen="0" imageURL="">Foam</answer>
    <result>Correct</result>
  </question>
  <question>
    <questionText>What should your immediate action be on hearing a fire alarm?</questionText>
    <answer number="0" value="0" chosen="0" imageURL="">Find all of your colleagues before making a speedy exit together</answer>
    <answer number="1" value="0" chosen="0" imageURL="">Collect all your valuables before making a speedy exit</answer>
    <answer number="2" value="0" chosen="0" imageURL="">Check the weather to see if you need your coat before leaving</answer>
    <answer number="3" value="1" chosen="1" imageURL="">Leave the building by the nearest exit, closing doors behind you if the rooms are empty</answer>
    <result>Correct</result>
  </question>
  <question>
    <questionText>Which is the most suitable extinguisher for a Computer which is on fire?</questionText>
    <answer number="0" value="0" chosen="1" imageURL="">Water (H2O)</answer>
    <answer number="1" value="0" chosen="0" imageURL="">Powder</answer>
    <answer number="2" value="1" chosen="0" imageURL="">Carbon Dioxide (CO2)</answer>
    <result>Incorrect</result>
  </question>
</quizresult>';

-- Get questions only

DECLARE @questions TABLE (questionText nvarchar(max), result nvarchar(50));
INSERT INTO @questions (questionText, result)
SELECT
    n.q.value('(./questionText)[1]', 'nvarchar(max)') AS questionText,
    n.q.value('(./result)[1]', 'nvarchar(50)') AS result
FROM
    @xml.nodes('/quizresult/question') AS n (q);

-- Get answers only

DECLARE @answers TABLE (answer nvarchar(max), number int, val int, chosen bit);
INSERT INTO @answers (answer, number, val, chosen)
SELECT
    n.q.value('.[1]', 'nvarchar(max)') AS answer,
    n.q.value('@number', 'int') AS number,
    n.q.value('@value', 'int') AS val,
    n.q.value('@chosen', 'bit') AS chosen
FROM
    @xml.nodes('/quizresult/question/answer') AS n (q);

任何人都可以请教我是否可以创建ID / GUID(或其他东西)来创建尊重XML文件的父/子层次结构?   我应该补充一点,实际上这是一个XML列,数据将被集中转换。我只是使用变量,直到找出基本方法。

3 个答案:

答案 0 :(得分:3)

我们可以(ab)使用ROW_NUMBER()在XQuery之外生成ID。序言:

WITH questions AS (
    SELECT
        ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS ID,
        n.q.value('(./questionText)[1]', 'nvarchar(max)') AS questionText,
        n.q.value('(./result)[1]', 'nvarchar(50)') AS result,
        n.q.query('answer') AS answers
    FROM
        @xml.nodes('/quizresult/question') AS n (q)
), questions_and_answers AS (
    SELECT ID, questionText, result, answer.query('.') AS answer
    FROM questions
    CROSS APPLY answers.nodes('answer') AS a(answer)
)

现在用

检索问题
SELECT ID, questionText, result 
FROM questions

答案

SELECT ID AS questionID,
    q.answer.value('answer[1]', 'nvarchar(max)') AS answer,
    q.answer.value('answer[1]/@number', 'int') AS number,
    q.answer.value('answer[1]/@value', 'int') AS val,
    q.answer.value('answer[1]/@chosen', 'bit') AS chosen
FROM questions_and_answers AS q

答案 1 :(得分:1)

这里唯一的关系是使用questionText。因此,可以像这样获取此列

pip install numpy-1.12.0+mkl-cp36-cp36m-win64.whl
pip install scipy-0.18.1-cp36-cp36m-win64.whl
pip install matplotlib-2.0.0-cp36-cp36m-win64.whl

或者,您可以根据questionText生成ID

DECLARE @answers TABLE (questionText nvarchar(max),answer nvarchar(max), 
number int, val int, chosen bit);

INSERT INTO @answers (questionText, answer, number, val, chosen)
SELECT
n.q.value('(../questionText)[1]', 'nvarchar(max)') as questionText,
n.q.value('.[1]', 'nvarchar(max)') AS answer,
n.q.value('@number', 'int') AS number,
n.q.value('@value', 'int') AS val,
n.q.value('@chosen', 'bit') AS chosen
FROM
@xml.nodes('/quizresult/question/answer') AS n (q);

如果相同的问题/答案可以重复,其他解决方案

DECLARE @questions TABLE (Id int, questionText nvarchar(max), result nvarchar(50));

INSERT INTO @questions (id, questionText, result)
SELECT
    Rank() over(order by n.q.value('(./questionText)[1]', 'nvarchar(max)')) as Id,
    n.q.value('(./questionText)[1]', 'nvarchar(max)') AS questionText,
    n.q.value('(./result)[1]', 'nvarchar(50)') AS result
FROM
    @xml.nodes('/quizresult/question') AS n (q);

DECLARE @answers TABLE (Id int, questionText nvarchar(max),answer 
nvarchar(max), number int, val int, chosen bit);

INSERT INTO @answers (Id, questionText, answer, number, val, chosen)
SELECT
   Dense_rank() over(order by n.q.value('(../questionText)[1]', 'nvarchar(max)')) as Id,
   n.q.value('(../questionText)[1]', 'nvarchar(max)') as questionText,
   n.q.value('.[1]', 'nvarchar(max)') AS answer,
   n.q.value('@number', 'int') AS number,
   n.q.value('@value', 'int') AS val,
   n.q.value('@chosen', 'bit') AS chosen
FROM
   @xml.nodes('/quizresult/question/answer') AS n (q);

答案 2 :(得分:1)

通过使用MERGE插入问题,您可以从源数据和目标数据中捕获字段。因此,您可以访问新插入的问题ID和相应的答案,并且无需依赖问题文本将问题链接到答案。

在插入问题后,您只需要一个映射表来存储中间结果。

DECLARE @Mapping TABLE (QuestionID INT NOT NULL, Answers XML);

MERGE @questions AS q
USING
(   SELECT  questionText = q.x.value('questionText[1]', 'NVARCHAR(MAX)'),
            result = q.x.value('result[1]', 'NVARCHAR(MAX)'),
            Answers = q.x.query('answer')
    FROM    @xml.nodes('quizresult/question') q (x)
) AS t
    ON 1 = 0 
WHEN NOT MATCHED THEN 
    INSERT (QuestionText, Result)
    VALUES (t.QuestionText, t.Result)
OUTPUT inserted.QuestionID, t.Answers INTO @Mapping (QuestionID, Answers);

然后存储了中间结果,包括问题ID,您可以查询映射表以插入答案。

-- INSERT ANSWERS USING MAPPING TABLE
INSERT @Answers (QuestionID, Answer, Number, Val, Chosen)
SELECT  m.QuestionID,
        answer = a.x.value('text()[1]', 'NVARCHAR(MAX)'),
        number = a.x.value('@number[1]', 'INT'),
        val = a.x.value('@value[1]', 'INT'),
        chosen = a.x.value('@chosen[1]', 'BIT')
FROM    @Mapping m
        CROSS APPLY Answers.nodes('answer') a (x);

全面演示

DECLARE @xml XML = N'<quizresult>
  <question>
    <questionText>Which fire extinguisher is most suitable for a waste paper basket fire?</questionText>
    <answer number="0" value="0" chosen="0" imageURL="">Powder</answer>
    <answer number="1" value="0" chosen="0" imageURL="">Carbon Dioxide (CO2)</answer>
    <answer number="2" value="1" chosen="1" imageURL="">Water (H2O)</answer>
    <answer number="3" value="0" chosen="0" imageURL="">Foam</answer>
    <result>Correct</result>
  </question>
  <question>
    <questionText>What should your immediate action be on hearing a fire alarm?</questionText>
    <answer number="0" value="0" chosen="0" imageURL="">Find all of your colleagues before making a speedy exit together</answer>
    <answer number="1" value="0" chosen="0" imageURL="">Collect all your valuables before making a speedy exit</answer>
    <answer number="2" value="0" chosen="0" imageURL="">Check the weather to see if you need your coat before leaving</answer>
    <answer number="3" value="1" chosen="1" imageURL="">Leave the building by the nearest exit, closing doors behind you if the rooms are empty</answer>
    <result>Correct</result>
  </question>
  <question>
    <questionText>Which is the most suitable extinguisher for a Computer which is on fire?</questionText>
    <answer number="0" value="0" chosen="1" imageURL="">Water (H2O)</answer>
    <answer number="1" value="0" chosen="0" imageURL="">Powder</answer>
    <answer number="2" value="1" chosen="0" imageURL="">Carbon Dioxide (CO2)</answer>
    <result>Incorrect</result>
  </question>
  <question>
    <questionText>Which is the most suitable extinguisher for a Computer which is on fire?</questionText>
    <answer number="0" value="0" chosen="1" imageURL="">Water (H2O) DUPLICATE</answer>
    <answer number="1" value="0" chosen="0" imageURL="">Powder DUPLICATE</answer>
    <answer number="2" value="1" chosen="0" imageURL="">Carbon Dioxide (CO2) DUPLICATE</answer>
    <result>Incorrect</result>
  </question>
</quizresult>';

-- DEMO TARGE TABLES
DECLARE @questions TABLE 
(
    QuestionID INT IDENTITY(1, 1) NOT NULL,
    questionText NVARCHAR(MAX), 
    result NVARCHAR(50)
);
DECLARE @answers TABLE 
(
    AnswerID INT IDENTITY(1, 1) NOT NULL,
    QuestionID INT NOT NULL,
    answer NVARCHAR(MAX), 
    number INT, 
    val INT, 
    chosen BIT
);



-- MAPPING TABLE
DECLARE @Mapping TABLE (QuestionID INT NOT NULL, Answers XML);

-- INSERT ANSWERS
MERGE @questions AS q
USING
(   SELECT  questionText = q.x.value('questionText[1]', 'NVARCHAR(MAX)'),
            result = q.x.value('result[1]', 'NVARCHAR(MAX)'),
            Answers = q.x.query('answer')
    FROM    @xml.nodes('quizresult/question') q (x)
) AS t
    ON 1 = 0 
WHEN NOT MATCHED THEN 
    INSERT (QuestionText, Result)
    VALUES (t.QuestionText, t.Result)
OUTPUT inserted.QuestionID, t.Answers INTO @Mapping (QuestionID, Answers);

-- INSERT ANSWERS USING MAPPING TABLE
INSERT @Answers (QuestionID, Answer, Number, Val, Chosen)
SELECT  m.QuestionID,
        answer = a.x.value('text()[1]', 'NVARCHAR(MAX)'),
        number = a.x.value('@number[1]', 'INT'),
        val = a.x.value('@value[1]', 'INT'),
        chosen = a.x.value('@chosen[1]', 'BIT')
FROM    @Mapping m
        CROSS APPLY Answers.nodes('answer') a (x);

-- CHECK RESULTS
SELECT  *
FROM    @Questions AS q
        INNER JOIN @Answers AS a
            ON a.QuestionID = q.QuestionID;