解析/处理XML并插入SQL表

时间:2012-10-19 22:22:20

标签: sql xml sql-server-2005 xquery

跟进:Is this XML format correct?

我有一些SQL代码在QABLOCK中循环并打印它们(PRINTs将在生产中更改为INSERT)。

-- XML instance
DECLARE    @x1 XML
SELECT @x1 = 
'
<tests>
    <test id="1">
        <qablock number="1">
            <question>What is 1 + 1?</question>
            <explanation>It"s 2.</explanation>
            <options>
                <option number="1" value="1" correct="0" />
                <option number="2" value="2" correct="1" />
                <option number="3" value="3" correct="0" />
                <option number="4" value="4" correct="0" />
                <option number="5" value="5" correct="0" />
            </options>
        </qablock>
        <qablock number="2">
            <question>What is 2 + 2?</question>
            <explanation>It"s 4.</explanation>
            <options>
                <option number="1" value="1" correct="0" />
                <option number="2" value="2" correct="0" />
                <option number="3" value="3" correct="0" />
                <option number="4" value="4" correct="1" />
                <option number="5" value="5" correct="0" />
            </options>
        </qablock>
    </test>
</tests>
'

DECLARE 
    @cnt INT, 
    @totCnt INT,
    @child XML

-- counter variables
SELECT 
    @cnt = 1,
    @totCnt = @x1.value('count(/tests/test/qablock)','INT')

-- loop
WHILE @cnt <= @totCnt BEGIN
    SELECT
        1 AS tests_id, --this is hard-coded
        @cnt AS qablock_number, 
        @x1.value('(/tests/test/qablock[position()=sql:variable("@cnt")]/question/text())[1]','varchar(500)') AS question, 
        @x1.value('(/tests/test/qablock[position()=sql:variable("@cnt")]/explanation/text())[1]','varchar(1000)') AS explanation

    PRINT 'Processing Child Element: ' + CAST(@cnt AS VARCHAR)
    PRINT 'Child element:  ' + CAST(@child AS VARCHAR(max))
    PRINT ''

    -- increment the counter variable
    SELECT @cnt = @cnt + 1
END

我不知道这是否是正确的程序,但至少它似乎有效。我接下来要做的是运行另一个类似的脚本,它循环遍历所有的OPTION和PRINT。对于每个OPTION,我需要打印:QABLOCK编号,OPTION编号,OPTION值和OPTION is_correct。我甚至无法接近。 :(这是我到目前为止所得到的:

-- XML instance
DECLARE    @x1 XML
SELECT @x1 = 
'
<tests>
    <test id="1">
        <qablock number="1">
            <question>What is 1 + 1?</question>
            <explanation>It"s 2.</explanation>
            <options>
                <option number="1" value="1" correct="0" />
                <option number="2" value="2" correct="1" />
                <option number="3" value="3" correct="0" />
                <option number="4" value="4" correct="0" />
                <option number="5" value="5" correct="0" />
            </options>
        </qablock>
        <qablock number="2">
            <question>What is 2 + 2?</question>
            <explanation>It"s 4.</explanation>
            <options>
                <option number="1" value="1" correct="0" />
                <option number="2" value="2" correct="0" />
                <option number="3" value="3" correct="0" />
                <option number="4" value="4" correct="1" />
                <option number="5" value="5" correct="0" />
            </options>
        </qablock>
    </test>
</tests>
'

DECLARE 
    @cnt INT, 
    @totCnt INT,
    @child XML

-- counter variables
SELECT 
    @cnt = 1,
    @totCnt = @x1.value('count(/tests/test/qablock/options/option)','INT')

-- loop
WHILE @cnt <= @totCnt BEGIN
    SELECT
        1 AS tests_id, --hard-coded value
        @x1.value('(/tests/test/qablock/@number)[1]','varchar(500)') AS qablock_number, 
        @x1.value('(/tests/test/qablock/options/option[position()=sql:variable("@cnt")]/@number)[1]','varchar(500)') AS option_number

    PRINT 'Processing Child Element: ' + CAST(@cnt AS VARCHAR)
    PRINT 'Child element:  ' + CAST(@child AS VARCHAR(max))
    PRINT ''

    -- increment the counter variable
    SELECT @cnt = @cnt + 1
END

所以,显然它已经搞砸了,但我似乎无法弄清楚要改变什么。

首先,我不知道如何获取当前OPTION的父QABLOCK编号。

其次,只有我的前5个option_number正确显示其数字,第二个5显示为NULL。

P.S。部分代码来自: http://beyondrelational.com/modules/2/blogs/28/posts/10316/xquery-lab-35-how-to-write-a-loop-to-process-all-the-child-elements-of-an-xml-value.aspx

1 个答案:

答案 0 :(得分:1)

这应该可以解决问题:

-- XML instance
DECLARE    @x1 XML
SELECT @x1 = 
'
<tests>
    <test id="1">
        <qablock number="1">
            <question>What is 1 + 1?</question>
            <explanation>It"s 2.</explanation>
            <options>
                <option number="1" value="1" correct="0" />
                <option number="2" value="2" correct="1" />
                <option number="3" value="3" correct="0" />
                <option number="4" value="4" correct="0" />
                <option number="5" value="5" correct="0" />
            </options>
        </qablock>
        <qablock number="2">
            <question>What is 2 + 2?</question>
            <explanation>It"s 4.</explanation>
            <options>
                <option number="1" value="1" correct="0" />
                <option number="2" value="2" correct="0" />
                <option number="3" value="3" correct="0" />
                <option number="4" value="4" correct="1" />
                <option number="5" value="5" correct="0" />
            </options>
        </qablock>
    </test>
</tests>
'

declare @testCount int
, @testIndex int 
, @qaCount int
, @qaIndex int 
, @optCount int
, @optIndex int 

, @testId int
, @qaNo int
, @question nvarchar(500)
, @explanation nvarchar(500)
, @optNo int
, @optVal int
, @optCorrect bit


--for each test
set @testCount = @x1.value('count(/tests/test)','int')
set @testIndex = 1 
while @testIndex <= @testCount
begin

    --insert a record for this test
    set @testId = @x1.value('(/tests/test[sql:variable(''@testIndex'')]/@id)[1]','int')
    print 'insert into tests (id, ...) values (' + + cast(@testId as nvarchar(9)) + ')'
    print 'set @testIdDB = scope_identity()' --if we're using db generated ids rather than relying on the ids in the imported xml

    --for each qablock
    set @qaCount = @x1.value('count(/tests/test[sql:variable(''@testIndex'')]/qablock)','int')
    set @qaIndex = 1    
    while @qaIndex <= @qaCount
    begin   
        --insert a qa record
        select @qaNo = @x1.value('(/tests/test[sql:variable(''@testIndex'')]/qablock[sql:variable(''@qaIndex'')]/@number)[1]','int')
        , @question = @x1.value('(/tests/test[sql:variable(''@testIndex'')]/qablock[sql:variable(''@qaIndex'')]/question/text())[1]','nvarchar(500)')
        , @explanation = @x1.value('(/tests/test[sql:variable(''@testIndex'')]/qablock[sql:variable(''@qaIndex'')]/explanation/text())[1]','nvarchar(500)')
        print 'insert into qa (testId, qaNo, Question, Explanation) values (@testIdDB, ' + cast(@qaNo as nvarchar(9)) + ', ' + quotename(@question,'''') + ', ' + quotename(@explanation,'''') + ')'
        print 'set @qaIdDB = scope_identity()' 

        --for each option
        set @optCount = @x1.value('count(/tests/test[sql:variable(''@testIndex'')]/qablock[sql:variable(''@qaIndex'')]/options/option)','int')
        set @optIndex = 1   
        while @optIndex <= @optCount
        begin   
            --insert an option record
            select @optNo = @x1.value('(/tests/test[sql:variable(''@testIndex'')]/qablock[sql:variable(''@qaIndex'')]/options/option[sql:variable(''@optIndex'')]/@number)[1]','int') 
            , @optVal  = @x1.value('(/tests/test[sql:variable(''@testIndex'')]/qablock[sql:variable(''@qaIndex'')]/options/option[sql:variable(''@optIndex'')]/@value)[1]','int')
            , @optCorrect  = @x1.value('(/tests/test[sql:variable(''@testIndex'')]/qablock[sql:variable(''@qaIndex'')]/options/option[sql:variable(''@optIndex'')]/@correct)[1]','bit')
            print 'insert into tests (qaId, number, value, isCorrect) values (@qaIdDB, ' + cast(@optNo as nvarchar(9)) + ', ' + cast(@optVal as nvarchar(9)) + ', ' + cast(@optCorrect as nchar(1)) +  ')'

            set @optIndex = @optIndex + 1
        end
        set @qaIndex = @qaIndex + 1
    end
    set @testIndex = @testIndex + 1
end

或者更好(感谢您提供给我的链接):

select x.value('@id','int') testId
FROM @x1.nodes('/tests/test') d(x)

select x.value('../@id','int') parentTestId
, x.value('@number','int') qaNumber
,  x.value('(question/text())[1]','nvarchar(500)') question
,  x.value('(explanation/text())[1]','nvarchar(500)') explanation
FROM @x1.nodes('/tests/test/qablock') d(x)

select x.value('../../@number','int') parentQaNumber
, x.value('@number','int') number
, x.value('@value','int') value
, x.value('@correct','bit') correct
FROM @x1.nodes('/tests/test/qablock/options/option') d(x)