【问题标题】:Parsing/Processing XML and Inserting Into SQL Tables解析/处理 XML 并插入 SQL 表
【发布时间】:2012-10-10 14:23:03
【问题描述】:

跟进来自:Is this XML format correct?

我有一些 SQL 代码在工作,循环通过 QABLOCK 并打印它们(在生产中 PRINT 将更改为 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,我需要打印: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。

附:其中一些代码来自: 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 到 10,而选项在 1-5 的两个块中(即 position() 重置为1 在第 6 个选项元素上)。快速修复是: (/tests/test/qablock/options/option/@number)[sql:variable("@cnt")]
  • 非常感谢您的回复。上面的快速修复代码会在 SSMS 中生成错误:XQuery [value()]: 'value()' requires a singleton (or empty sequence), found operand of type 'xdt:untypedAtomic *' 所以将等待您的完整回复. :)
  • 我什至不知道执行 WHILE 循环是否是正确的方法。这个网站似乎从不使用它们:link

标签: sql xml sql-server-2005 xquery


【解决方案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)

【讨论】:

  • 天哪,感谢您所做的一切!我将经历这个并尝试根据您的示例来解决所有问题。再次感谢您的回复!
  • 关于编辑,是的,这正是我所期待的!会仔细研究它并希望从中学到很多东西!
  • 不用担心 - 感谢您的积极反馈 :)。有任何问题都给我留言。
  • 我如何在 SO 之外与您联系?您的博客没有联系信息。
  • 当然,但是如果它与问题有关,我建议堆栈溢出,因为这样其他人可以在我被捆绑时弥补不足(当我给出不好的建议时他们可以指出也)。您可以使用我的 SO 用户名和 gmail 取得联系(这样的措辞是为了避免垃圾邮件)。
猜你喜欢
  • 2010-11-03
  • 2012-10-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-16
  • 1970-01-01
  • 2015-06-22
相关资源
最近更新 更多