Reputation: 23
I'm trying to modify an XML value but keep getting the message
The target of the replace must be at most one node, found attribute(prefType, xdt:untypedAtomic)
First here is my XML
<preferences>
<categories>
<category id="1" prefType="2">
<subcat id="1" prefType="2" />
<subcat id="2" prefType="2" />
<subcat id="3" prefType="2" />
<subcat id="77" prefType="2" />
</category>
<category id="2" prefType="2">
<subcat id="9" prefType="2" />
<subcat id="10" prefType="2" />
<subcat id="11" prefType="2" />
<subcat id="12" prefType="2" />
<subcat id="13" prefType="2" />
<subcat id="14" prefType="2" />
<subcat id="17" prefType="2" />
<subcat id="78" prefType="2" />
<subcat id="101" prefType="2" />
</category>
<category id="3" prefType="2">
<subcat id="18" prefType="2" />
<subcat id="19" prefType="2" />
<subcat id="20" prefType="2" />
</category>
</categories>
</preferences>
And my code
declare @XMLinput as XML;
declare @custXML as XML;
declare @subcatid as nvarchar(3);
declare @interest as nvarchar(8);
declare @newValue as varchar(1);
declare @cnt as int;
set @XMLinput = '<preferences><categoryId>73</categoryId><interestLevel>POSITIVE</interestLevel></preferences>';
-- get the subcatid and interest level
SET @subcatid = @XMLinput.value('(//preferences/categoryId)[1]','nvarchar(3)');
SET @interest = @XMLinput.value('(//preferences/interestLevel)[1]','nvarchar(20)');
SET @newValue =
CASE @interest
WHEN 'POSITIVE' THEN '1'
WHEN 'NEGATIVE' THEN '3'
ELSE '2'
END;
set @custXML = (select Preferences from Customer_Preferences where custID=11584);
select @custXML.exist('//preferences/categories/category/subcat[@id=sql:variable("@subcatid")]');
if (@@ROWCOUNT > 0)
BEGIN TRY
BEGIN TRAN;
set @cnt = CAST(CAST(@custXML.query('count(//preferences/categories/category/subcat[@id=(sql:variable("@subcatid"))])') AS VARCHAR) AS INT);
-- replace the value
UPDATE Customer_Preferences
SET preferences.modify('
replace value of
(//*/subcat[@id=sql:variable("@subcatid")]/@prefType[1])
with sql:variable("@newValue")
')
where CustID = 11584;
COMMIT TRAN;
END TRY
BEGIN CATCH
select XACT_STATE() as 'XACT_STATE', @@TRANCOUNT as '@@TRANCOUNT';
if @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
END CATCH
select preferences from Customer_Preferences where custid=11584
SELECT XACT_STATE() as 'XACT_STATE', @@TRANCOUNT AS '@@TRANCOUNT'
I've tried removing the sql variables and replacing them with fixed values but still get the same issue. I've also tried removing all the XML subcats except for one and the same error occurs.
After 3 hours of working through this and getting no where, I'd really appreciate your help.
Upvotes: 2
Views: 3957
Reputation: 6218
Just as further reference (although you solved your problem already on your own) for others with similar problems: Here is what actually went wrong: The error message already indicates that the replace target can be at most one value (meaning you can replace only one value at a time).
However, (//*/subcat[@id=sql:variable("@subcatid")]/@prefType[1])
yields a sequence of results What is means literally is to take each subcat
element and select the first attribute with the name prefType
. This actually does not make much sense as an XML element can not have multiple attributes with the same name, so the query would be the same without the [1]
predicate.
What you probably wanted to write is: Give me each prefType
attribute of each subcat
element and return only the first one of the whole result set. That is exactly what your working query is doing: (//*/subcat[@id=sql:variable("@subcatid")]/@prefType)[1]
Upvotes: 2