2017-08-29 8 views
3

XML変数を他のテーブルに結合する方法はわかっていますが、この場合はテーブルから各行を選択し、XMLの構造に加えてがその行に沿って表示されます。ほとんどの例が単一のXML値を扱うので、私はこれを手助けするための例をオンラインで見つけることができません(もしあれば、私は無数の他のXML例の中でそれらを見つけることができませんでした)。構造化XMLデータに加えて行データを選択する

テーブル構造はこれです:

CREATE TABLE tbl_QuizHistory (
    HistoryId int PRIMARY KEY, 
    QuizData xml NOT NULL 
); 

QuizData行の値はこれに類似している:earlier question I was shown how to display the XML data hierarchically@xml ==> questions ==> answer(s))において

<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>Should you use lifts during a fire?</questionText> 
    <answer number="0" value="0" chosen="0" imageURL="">Yes</answer> 
    <answer number="1" value="1" chosen="1" imageURL="">No</answer> 
    <result>Correct</result> 
    </question> 
</quizresult> 

、だけI単一のXML値について質問/回答階層をテーブルに移行するようになっている。

-- Works for a single XML value/variable... 
;WITH q AS (
    SELECT 
     ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS qID, 
     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) 
), 
qa AS (
    SELECT 
     qID, 
     questionText, 
     result, 
     answer.query('.') AS answer 
    FROM 
     q CROSS APPLY 
     answers.nodes('answer') AS a(answer) 
) 
SELECT 
    qa.qID, 
    q.questionText, 
    q.result, 
    qa.answer.value('answer[1]', 'nvarchar(max)') AS answer, 
    qa.answer.value('answer[1]/@number', 'int') AS number, 
    qa.answer.value('answer[1]/@value', 'int') AS val, 
    qa.answer.value('answer[1]/@chosen', 'bit') AS chosen 
FROM 
    qa INNER JOIN 
    q ON qa.qID = q.qID; 

どのようにしてこのロジックを各XML行のすべてのテーブル行に適用できますか?私は(これは、SQL文によって生成されたが、明確にするための任意のIDで、そしてXMLには存在しない)クイズはそのクイズから

  • 各質問をHistoryId

    1. 表示する必要が
    2. すべてを

      HistoryId qID questionText                   result  answer                     number val chosen 
      --------- ---- --------------------------------------------------------------------------------------- ---------- ---------------------------------------------------------------------------------------- ------- ---- ------ 
      100  1 Which fire extinguisher is most suitable for a waste paper basket fire?     Correct Powder                     0  0 0 
      100  1 Which fire extinguisher is most suitable for a waste paper basket fire?     Correct Carbon Dioxide (CO2)                  1  0 0 
      100  1 Which fire extinguisher is most suitable for a waste paper basket fire?     Correct Water (H2O)                    2  1 1 
      100  1 Which fire extinguisher is most suitable for a waste paper basket fire?     Correct Foam                      3  0 0 
      100  2 What should your immediate action be on hearing a fire alarm?       Correct Find all of your colleagues before making a speedy exit together       0  0 0 
      100  2 What should your immediate action be on hearing a fire alarm?       Correct Collect all your valuables before making a speedy exit         1  0 0 
      100  2 What should your immediate action be on hearing a fire alarm?       Correct Check the weather to see if you need your coat before leaving       2  0 0 
      100  2 What should your immediate action be on hearing a fire alarm?       Correct Leave the building by the nearest exit, closing doors behind you if the rooms are empty 3  1 1 
      101  1 Which fire extinguisher is most suitable for a waste paper basket fire?     Correct Powder                     0  0 0 
      101  1 Which fire extinguisher is most suitable for a waste paper basket fire?     Correct Carbon Dioxide (CO2)                  1  0 0 
      101  1 Which fire extinguisher is most suitable for a waste paper basket fire?     Correct Water (H2O)                    2  1 1 
      101  1 Which fire extinguisher is most suitable for a waste paper basket fire?     Correct Foam                      3  0 0 
      101  2 Should you use lifts during a fire?              Correct Yes                      0  0 0 
      101  2 Should you use lifts during a fire?              Correct No                      1  1 1 
      101  3 Which part of a Carbon Dioxide (CO2) extinguisher should you not touch when operating? Incorrect The body of the extinguisher                0  0 1 
      101  3 Which part of a Carbon Dioxide (CO2) extinguisher should you not touch when operating? Incorrect The release trigger and the bottom of the extinguisher         1  0 0 
      101  3 Which part of a Carbon Dioxide (CO2) extinguisher should you not touch when operating? Incorrect The horn of the extinguisher                2  1 0 
      
      :各質問

    のための答えは私が達成しようとしている最終的な結果は次のようなものを生成します

    これは多数の重複を作成することに感謝しています(質問は各回答ごとに繰り返されます)が、大丈夫です。

    私はを持っており、サンプルデータが設定されています。

  • 答えて

    1

    何のレベル特定の計算(例えばrow_number())が必要でない場合、それは、3 CROSS APPLY一連のレベル

    SELECT HistoryId, 
         t.qID, 
         t.questionText, 
         t.result, 
         a.aId, 
         a.answerNbr, 
         a.answerChosen, 
         a.answerTxt 
        FROM 
         tbl_QuizHistory 
        CROSS APPLY QuizData.nodes('quizresult') AS n(q)  
        CROSS APPLY (
         SELECT 
          ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS qID, 
          t.q.value('(./questionText)[1]', 'nvarchar(max)') AS questionText, 
          t.q.value('(./result)[1]', 'nvarchar(50)') AS result, 
          t.q.query('.') queryXml 
         FROM 
          n.q.nodes('./question') t(q) 
        ) t 
        CROSS APPLY (
         SELECT 
          ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS aID, 
          q.a.value('(./@number)[1]', 'int') as answerNbr, 
          q.a.value('(./@chosen)[1]', 'bit') as answerChosen, 
          q.a.value('.','nvarchar(max)') as answerTxt 
         FROM 
          t.queryXml.nodes('question/answer') q(a) 
        ) a; 
    

    によってレベルビット短くすることができる。

    SELECT HistoryId, 
         t.qID, 
         t.questionText, 
         t.result, 
         q.a.value('(./@number)[1]', 'int') as answerNbr, 
         q.a.value('(./@chosen)[1]', 'bit') as answerChosen, 
         q.a.value('.','nvarchar(max)') as answerTxt 
        FROM 
         tbl_QuizHistory 
        CROSS APPLY QuizData.nodes('quizresult') AS n(q)  
        CROSS APPLY (
         SELECT 
          ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS qID, 
          t.q.value('(./questionText)[1]', 'nvarchar(max)') AS questionText, 
          t.q.value('(./result)[1]', 'nvarchar(50)') AS result, 
          t.q.query('.') queryXml 
         FROM n.q.nodes('./question') t(q) 
        ) t 
        CROSS APPLY t.queryXml.nodes('question/answer') q(a) 
    

    Demo

    +0

    それは面白いです。他の回答に応じて詳細の回答レベルを表示するように拡張してください。そのレベルでの質問がどのように行われるのかがわかりますか? – EvilDr

    +1

    はい、回答を編集しました – Serg

    1

    私が正しくあなたを理解していれば、あなたがしたい:

    ;WITH q AS (
        SELECT 
         ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS qID, 
         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 tbl_QuizHistory t 
        CROSS APPLY t.QuizData.nodes('/quizresult/question') AS n (q) 
    ), 
    qa AS (
        SELECT 
         qID, 
         questionText, 
         result, 
         answer.query('.') AS answer 
        FROM q 
        CROSS APPLY answers.nodes('answer') AS a(answer) 
    ) 
    SELECT 
        qa.qID, 
        q.questionText, 
        q.result, 
        qa.answer.value('answer[1]', 'nvarchar(max)') AS answer, 
        qa.answer.value('answer[1]/@number', 'int') AS number, 
        qa.answer.value('answer[1]/@value', 'int') AS val, 
        qa.answer.value('answer[1]/@chosen', 'bit') AS chosen 
    FROM qa 
    JOIN q ON qa.qID = q.qID; 
    

    Rextester Demo


    あるいは短い:

    ;WITH q AS (
        SELECT 
         ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS qID, 
         n.q.value('(./questionText)[1]', 'nvarchar(max)') AS questionText, 
         n.q.value('(./result)[1]', 'nvarchar(50)') AS result, 
         n.q.query('answer') AS answers, 
         answer.query('.') AS answer 
        FROM tbl_QuizHistory t 
        CROSS APPLY t.QuizData.nodes('/quizresult/question') AS n (q) 
        CROSS APPLY n.q.nodes('answer') AS a(answer) 
    ) 
    SELECT 
        q.qID, 
        q.questionText, 
        q.result, 
        answer.value('answer[1]', 'nvarchar(max)') AS answer, 
        answer.value('answer[1]/@number', 'int') AS number, 
        answer.value('answer[1]/@value', 'int') AS val, 
        answer.value('answer[1]/@chosen', 'bit') AS chosen 
    FROM q; 
    

    Rextester Demo 2

    EDIT:

    ;WITH q AS (
        SELECT 
         t.HistoryId, 
         ROW_NUMBER() OVER(PARTITION BY t.HistoryId ORDER BY(SELECT NULL)) AS qID, 
         n.q.value('(./questionText)[1]', 'nvarchar(max)') AS questionText, 
         n.q.value('(./result)[1]', 'nvarchar(50)') AS result, 
         n.q.query('answer') AS answers, 
         answer.query('.') AS answer 
        FROM tbl_QuizHistory t 
        CROSS APPLY t.QuizData.nodes('/quizresult/question') AS n (q) 
        CROSS APPLY n.q.nodes('answer') AS a(answer) 
    ) 
    SELECT 
        q.HistoryId, 
        q.qID, 
        q.questionText, 
        q.result, 
        answer.value('answer[1]', 'nvarchar(max)') AS answer, 
        answer.value('answer[1]/@number', 'int') AS number, 
        answer.value('answer[1]/@value', 'int') AS val, 
        answer.value('answer[1]/@chosen', 'bit') AS chosen 
    FROM q; 
    
    +0

    おおよそ...どちらの場合でもqIDが正しく生成されていません。これは、次のHistoryIdに対して1にリセットされます。私は結果の中にHistoryIdも必要です。 – EvilDr

    +0

    @EvilDr 'ROW_NUMBER()OVER(区切りt.History_ID ORDER BY(SELECT NULL))AS qID、'更新された回答を確認してください – lad2025

    +0

    こんにちは。 Rextesterにリビジョンを貼り付けましたが、問題は残ります。結果の行1-4では、これは同じ質問ですが、4つの回答があるため、qIDは同じ( '1')にする必要があります(OPの私の例を参照)。結果行5では、質問テキストが変更されるので、これは新しい質問です。qIdは、次の3行については「2」にする必要があります。 9行目ではそれが別のクイズなので、qIDは1にリセットする必要があります。それは意味がありますか? – EvilDr

    1

    私はそれを達成するための最も直接的な方法は、テーブル値関数に与えられた変数のために働くあなたのコードをラップすることだと思います。同じ結果をクエリのインライン展開で実現できますが、あなたのような複雑なコードでは、関数を使用すると読みやすくなります。パフォーマンスは、複数のステートメント関数ではなく、 "インライン"のテーブル値関数なので、同じままです。

    When would you use a table-valued function?

    機能

    CREATE FUNCTION [dbo].[GetQuizData] 
    (
        @ParamQuizData xml 
    ) 
    RETURNS TABLE 
    AS 
    RETURN 
    (
        WITH q AS 
        (
        SELECT 
         ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS qID, 
         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 
         @ParamQuizData.nodes('/quizresult/question') AS n (q) 
        ), 
        qa AS 
        (
         SELECT 
          qID, 
          questionText, 
          result, 
          answer.query('.') AS answer 
         FROM 
          q CROSS APPLY 
          answers.nodes('answer') AS a(answer) 
        ) 
        SELECT 
         qa.qID, 
         q.questionText, 
         q.result, 
         qa.answer.value('answer[1]', 'nvarchar(max)') AS answer, 
         qa.answer.value('answer[1]/@number', 'int') AS number, 
         qa.answer.value('answer[1]/@value', 'int') AS val, 
         qa.answer.value('answer[1]/@chosen', 'bit') AS chosen 
        FROM 
         qa INNER JOIN 
         q ON qa.qID = q.qID 
    ) 
    

    メインクエリ

    SELECT 
        tbl_QuizHistory.HistoryID 
        ,Q.* 
    FROM 
        tbl_QuizHistory 
        CROSS APPLY [dbo].[GetQuizData](tbl_QuizHistory.QuizData) AS Q 
    ; 
    

    SQL Fiddle

    を参照してください、例えば参照してください。

    免責事項:正確性に関する質問からコードを分析しませんでした。私はそれをあなたが働くために必要なときに機能すると仮定して、関数に単純にラップしました。


    TVFから長いクエリを手動でCROSS APPLYにインライン化できます。 CTEもインライン化する必要があり、見た目が醜いでしょう。このバリアントとバリアントの実行計画をTVFと比較することができます。彼らは同じでなければなりません。

    ここにはSQL Fiddleです。

    インラインクエリ

    SELECT 
        tbl_QuizHistory.HistoryID 
        ,CA.* 
    FROM 
        tbl_QuizHistory 
        CROSS APPLY 
        (
         SELECT 
          qa.qID, 
          q.questionText, 
          q.result, 
          qa.answer.value('answer[1]', 'nvarchar(max)') AS answer, 
          qa.answer.value('answer[1]/@number', 'int') AS number, 
          qa.answer.value('answer[1]/@value', 'int') AS val, 
          qa.answer.value('answer[1]/@chosen', 'bit') AS chosen 
         FROM 
          (
           SELECT 
            qID, 
            questionText, 
            result, 
            answer.query('.') AS answer 
           FROM 
            (
             SELECT 
              ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS qID, 
              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 
              tbl_QuizHistory.QuizData.nodes('/quizresult/question') AS n (q) 
            ) AS q0 
            CROSS APPLY 
            answers.nodes('answer') AS a(answer) 
          ) AS qa 
          INNER JOIN 
          (
           SELECT 
            ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS qID, 
            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 
            tbl_QuizHistory.QuizData.nodes('/quizresult/question') AS n (q) 
          ) AS q 
          ON qa.qID = q.qID 
        ) AS CA 
    ; 
    

    この長いクエリを簡素化することができるが、私はそれが何をするかを分析していないと、それはそれをしませんか。私は単に与えられた作業照会をインライン化しました。

    +0

    ありがとうございます。これはうまくいきますが、関数なしの例を表示することもできますか(たとえば、あなたが見たいと思うSQL文よりもずっと長いSQL文)?ステートメントの各部分がどのように 'APPLY'の周りで一緒に固定されているのか理解しようとしています。理解できません。ありがとうございました。 – EvilDr

    +1

    @ EvilDr、関数をインライン化するときには何も特別なことはありません。詳細には注意が必要です。まず、関数内にCTEをインライン化しました。文字通り、それらの参照の代わりに 'SELECT'サブクエリを置くだけです。次に、 'CROSS APPLY'括弧内の関数からの問い合わせを入れます。私は答えを更新しました。 –

    +1

    絶対に素晴らしいです。ピースがどのようにぴったり合っているかを見れば、はるかに分かりやすくなります。期限が切れると賞金を授与させていただきます。ありがとうございました。 – EvilDr

    関連する問題