Вопрос

I'm trying to use sql:variable to read data from a XML file. My problem is, I can read the first or n^th line (or node) of the XML, however I can't make iteration in the lines (or nodes). Here is where I used sql:variable:

    CAST((SELECT @xmlExpenseItems.value(N'(/ExpenseItem//ExpenseID/node())[sql:variable("@iteratorVarChar")]','int')) AS int),
    CAST((SELECT @xmlExpenseItems.value(N'(/ExpenseItem//ExpenseTypeID/node())[sql:variable("@iteratorVarChar")]','int')) AS int),
    CAST((SELECT @xmlExpenseItems.value(N'(/ExpenseItem//ExpenseAmount/node())[sql:variable("@iteratorVarChar")]','float')) AS float)

where @iteratorVarChar is a varchar casted from an int.

I get the error "XQuery [value()]: Only 'http://www.w3.org/2001/XMLSchema#decimal?', 'http://www.w3.org/2001/XMLSchema#boolean?' or 'node()*' expressions allowed as predicates, found 'xs:string ?'" at the first line of the code above.

When I switched @iteratorVarChar with @iterator which is already an int, I get "XQuery [value()]: 'value()' requires a singleton (or empty sequence), found operand of type 'xdt:untypedAtomic *'"

As I said, when I replace sql:variable("@iteratorVarChar") with an int, like 1, then the code works with the first node of the xml.

My question is, am I missing something or am I making a fundamental mistake? How to make this work?

My whole code is below (I commented out the CREATE in order to avoid the recreation errors):

DECLARE @xmlExpenseItems XML

    SET @xmlExpenseItems = ' 
    <ExpenseItem>
            <ExpenseID>5</ExpenseID>
            <ExpenseTypeID>5</ExpenseTypeID>
            <ExpenseAmount>5</ExpenseAmount>
        </ExpenseItem>
        <ExpenseItem>
            <ExpenseID>3</ExpenseID>
            <ExpenseTypeID>5</ExpenseTypeID>
            <ExpenseAmount>7</ExpenseAmount>
        </ExpenseItem>
    '

--CREATE TABLE #ExpenseItems
--(ExpenseItemID int not null identity(1,1), 
--ExpenseID int not null,
--ExpenseTypeID int not null, 
--ExpenseAmount float not null
--)

DECLARE @iterator int = 1
DECLARE @IDCount int
SELECT @IDCount = (SELECT @xmlExpenseItems.value('count(/ExpenseItem)', 'int') )
DECLARE @iteratorVarChar varchar(3)

WHILE @iterator <= @IDCount
    BEGIN
    SET @iteratorVarChar = CAST(@iterator AS varchar(3))
    INSERT INTO #ExpenseItems
    (ExpenseID, ExpenseTypeID, ExpenseAmount)
    VALUES
    (
        CAST((SELECT @xmlExpenseItems.value(N'(/ExpenseItem//ExpenseID/node())[sql:variable("@iteratorVarChar")]','int')) AS int),
        CAST((SELECT @xmlExpenseItems.value(N'(/ExpenseItem//ExpenseTypeID/node())[sql:variable("@iteratorVarChar")]','int')) AS int),
        CAST((SELECT @xmlExpenseItems.value(N'(/ExpenseItem//ExpenseAmount/node())[sql:variable("@iteratorVarChar")]','float')) AS float)
    )
    SET @iterator = @iterator + 1
    END

select * from #ExpenseItems
Это было полезно?

Решение

Try a set based approach instead of iteration. The nodes() function returns a rowset from an XML document:

insert  #ExpenseItems
        (ExpenseID, ExpenseTypeID, ExpenseAmount)
select  col.value('ExpenseID[1]', 'int')
,       col.value('ExpenseTypeID[1]', 'int')
,       col.value('ExpenseAmount[1]', 'int')
from    @xmlExpenseItems.nodes('/ExpenseItem') doc(col)
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top