Exporting a xml document from sql 2008 - sql

All
I have very little experience of xml, so please be gentle.
The situation is we use an application called dataswitch to take xml and process it into our ERP system via business objects.
The output I am trying to recreate is as follows from a sample xml document.
**<?xml version="1.0" encoding="Windows-1252" ?>
- <IssueDetails Language="05" CssStyle="" DecFormat="1" DateFormat="01" Role="01" Version="6.0.000">**
- <Item>
<Job>00000121</Job>
<NonStocked>N</NonStocked>
<Warehouse>RM</Warehouse>
<StockCode>BBMB100</StockCode>
<Line>00</Line>
<QuantityToIssue>1.000</QuantityToIssue>
</Item>
</IssueDetails>
So far I have worked on the For xml Path method to build my query and used the sp from Phil Factor to write the output
So far I have been successful creating an xml document which opens in the browser (many tries required!)
This is the output I have managed to create, please note the top lines are different
**<IssueDetails xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">**
- <Item>
<Job>00044816</Job>
<NonStocked>N</NonStocked>
<Warehouse>C2</Warehouse>
<StockCode>FG00707</StockCode>
<Line>00</Line>
<QuantityToIssue>400.000000000</QuantityToIssue>
</Item>
- <Item>
<Job>00044816</Job>
<NonStocked>N</NonStocked>
<Warehouse>C2</Warehouse>
<StockCode>FG00707</StockCode>
<Line>00</Line>
<QuantityToIssue>10.000000000</QuantityToIssue>
</Item>
</IssueDetails>
So my question is how do I get the top line in my output to be the same as the sample output?
My procedure is listed below
Declare
#XML_1 varchar (max),
#myXML varchar(max),
#myFile varchar (30),
#Path varchar (100)
set #XML_1=''
set #myFile ='MaterialIssuesTest.xml'
set #Path='D:\Temp\'
--Query database for materials
Set #XML_1 =#XML_1 + ( SELECT WJM.[Job]
,Case when WJM.[Warehouse]='**' then 'Y' else 'N' end as NonStocked
,WJM.[Warehouse]
,rtrim(LP.StockCode) as StockCode
,WJM.[Line]
,[UnitQtyReqd]*LP.LQtyComplete as QuantityToIssue
FROM [SysproCompanyR].[dbo].[WipJobAllMat] WJM
inner join CHC_Production.LabPostsAtMileOps LP On WJM.Job=LP.Job
Where WJM.Job in ('00044816')and (WJM.OperationOffset > LP.PrevOp
and WJM.OperationOffset<LP.LOperation)
FOR XML Path ('Item'),ROOT ('IssueDetails'), ELEMENTS XSINIL
)
set #myXML = LTRIM(rtrim(#XML_1))
--Debug print xml
Print #myXML
--Output xml to document
execute dbo.spWriteStringToFile #myXML, #Path, #myFile
GO

To get you first line, which is a child element of the main body you need to do a sub query like what I have done below. To get the attributes you need to name them with the # symbol, again in my example (formatting changes to aid me setting it out!):
SET #XML_1 =#XML_1 +
(
SELECT WJM.[Job],
(
SELECT '' AS '#Language',
'' AS '#CssStyle',
'' AS '#DecFormat',
'' AS '#DateFormat',
'' AS '#Role',
'' AS '#Version'
FROM YOURTABLENAME YTN
WHERE YTN.[SOMEFIELD] = WJM.[SOMEFIELD]
FOR XML PATH(''), TYPE
) AS 'IssueDetails'
,Case when WJM.[Warehouse]='**' then 'Y' else 'N' end as NonStocked
,WJM.[Warehouse]
,rtrim(LP.StockCode) as StockCode
,WJM.[Line]
,[UnitQtyReqd]*LP.LQtyComplete as QuantityToIssue
FROM [SysproCompanyR].[dbo].[WipJobAllMat] WJM
INNER JOIN CHC_Production.LabPostsAtMileOps LP ON WJM.Job = LP.Job
WHERE WJM.Job IN ('00044816') AND (WJM.OperationOffset > LP.PrevOp
AND WJM.OperationOffset<LP.LOperation)
FOR XML Path ('Item'), ROOT ('IssueDetails'), ELEMENTS XSINIL
)
I don't know where the data is coming from, so you will have to fill in the table name and the WHERE clause to join it to the data that you already have. That is assuming the data is a child of the data that you have already provided.
Bare in mind the syntax might not be quite correct as I am doing this from memory and there are several different variations to get this result.

Related

Creating xml for SQL Server stored procedure

I have a function to insert an item into the database. It takes a lot of values as input and the values are passed as XML.
Consider a sample item XML:
<ROOT>
<Item
ItemName="CarlsApplication"
ItemTypeID="2">
<TSDefaultDescription DefaultitemDescription="C:\t.text"/>
<ItemSellers>
<ComputerObject Alias="" DisplayName="" ServiceName="" UserAccount="" />
<ComputerObject Alias="" DisplayName="" ServiceName="" UserAccount="" />
</ItemSellers>
<ItemOwners>
<ItemOwner Alias="rafchen" FirstName="Rafael" LastName="Chenkov"/>
</ItemOwners>
</Item>
</ROOT>
This has to be passed to stored procedure.
Now, each of these individual values in this XML, I have to extract from somewhere else. I can get the individual values like Item name etc, but how do I organize them into an XML that can be passed?
How do I construct this XML from the values I have?
I guess I will have to make some sort of template with this format and then put variables in that template and fill the variables to prepare the template.
Any help is greatly appreciated.
If I understand what you really want, You can use a query like this to generate that XML:
select
ItemName 'Item/#ItemName', --> Node:`Item` Attribute: `ItemName`
ItemTypeId 'Item/#ItemTypeId',
cast((
select
Alias 'ComputerObject/#Alias',
DisplayName 'ComputerObject/#DisplayName',
ServiceName 'ComputerObject/#ServiceName',
UserAccount 'ComputerObject/#UserAccount'
from
ItemSellers
where
ItemSellers.ItemId = Item.ItemId
for xml path('')) as xml) 'Item/ItemSellers', --> Node:`Item` Sub-Node:`ItemSellers`
cast((
select
Alias 'ItemOwner/#Alias',
FirstName 'ItemOwner/#FirstName',
LastName 'ItemOwner/#LastName'
from
ItemOwners
where
ItemOwners.ItemId = Item.ItemId
for xml path('')) as xml) 'Item/ItemOwners'
from
Item
for xml path('ROOT');
SQL Fiddle Demo

Passing irregular xml file to the stored procedure

I have a sample xml as follows. I am trying to get all or specific data and then insert into the my sql table which has the same columns representing the values coming from xml. I looked through some solutions but the xml files are not formatted like I have in here. Can you help me?
<?xml version="1.0" encoding="UTF-8" standalone="true"?>
<VehicleStatusResponse xmlns:ns2=
"http://fms-standard.com/rfms/v1.0.0/xsd/common/position" xmlns="http://fms-standard.com/rfms/v1.0.0/xsd/status">
<VehicleStatus>
<VIN>VF254ANA735752628</VIN>
<TriggerType>TIMER</TriggerType>
<CreatedDateTime>2014-09-08T09:30:20</CreatedDateTime>
<ReceivedDateTime>2014-09-08T09:30:57</ReceivedDateTime>
<GNSSPosition>
<ns2:Latitude>49.18557</ns2:Latitude>
<ns2:Longitude>11.18557</ns2:Longitude>
<ns2:Heading>33</ns2:Heading>
<ns2:Altitude>500</ns2:Altitude>
<ns2:Speed>16.4</ns2:Speed>
<ns2:PositionDateTime>2014-09-08T09:30:20</ns2:PositionDateTime>
</GNSSPosition>
<WheelBasedSpeed>16.07</WheelBasedSpeed>
<TachographSpeed>15.83</TachographSpeed>
<HRTotalVehicleDistance>817.5</HRTotalVehicleDistance>
<EngineTotalFuelUsed>575</EngineTotalFuelUsed>
<FuelLevel1>83</FuelLevel1>
<CatalystFuelLevel>88.48</CatalystFuelLevel>
<GrossCombinationVehicleWeight>10000</GrossCombinationVehicleWeight>
</VehicleStatus>
</VehicleStatusResponse>
You can use an XML type and XML methods if you remove or modify the declaration. The SQL Server XML type only supports UCS-2 encoding and doesn't recognize "standalone". The example below uses string manipulation to tweak the declaration. You'll need to change the data types according to your actual column types and should specify an explicit column list on the INSERT statement. I omitted that in this example only because I didn't want to assume your actual table columns matched the element names in the XML.
DECLARE #xml xml;
DECLARE #xmlString nvarchar(MAX) = N'<?xml version="1.0" encoding="UTF-8" standalone="true"?>
<VehicleStatusResponse xmlns:ns2=
"http://fms-standard.com/rfms/v1.0.0/xsd/common/position" xmlns="http://fms-standard.com/rfms/v1.0.0/xsd/status">
<VehicleStatus>
<VIN>VF254ANA735752628</VIN>
<TriggerType>TIMER</TriggerType>
<CreatedDateTime>2014-09-08T09:30:20</CreatedDateTime>
<ReceivedDateTime>2014-09-08T09:30:57</ReceivedDateTime>
<GNSSPosition>
<ns2:Latitude>49.18557</ns2:Latitude>
<ns2:Longitude>11.18557</ns2:Longitude>
<ns2:Heading>33</ns2:Heading>
<ns2:Altitude>500</ns2:Altitude>
<ns2:Speed>16.4</ns2:Speed>
<ns2:PositionDateTime>2014-09-08T09:30:20</ns2:PositionDateTime>
</GNSSPosition>
<WheelBasedSpeed>16.07</WheelBasedSpeed>
<TachographSpeed>15.83</TachographSpeed>
<HRTotalVehicleDistance>817.5</HRTotalVehicleDistance>
<EngineTotalFuelUsed>575</EngineTotalFuelUsed>
<FuelLevel1>83</FuelLevel1>
<CatalystFuelLevel>88.48</CatalystFuelLevel>
<GrossCombinationVehicleWeight>10000</GrossCombinationVehicleWeight>
</VehicleStatus>
</VehicleStatusResponse>';
SET #xmlString = REPLACE(#xmlString, 'encoding="UTF-8"', 'encoding="UCS-2"');
SET #xmlString = REPLACE(#xmlString, 'standalone="true"', '');
SET #xml = #xmlString;
WITH XMLNAMESPACES (
DEFAULT 'http://fms-standard.com/rfms/v1.0.0/xsd/status'
,'http://fms-standard.com/rfms/v1.0.0/xsd/common/position' AS ns2
)
INSERT INTO dbo.YourTable
SELECT
#xml.value('(/VehicleStatusResponse/VehicleStatus/VIN)[1]', 'varchar(50)')
, #xml.value('(/VehicleStatusResponse/VehicleStatus/TriggerType)[1]', 'varchar(50)')
, #xml.value('(/VehicleStatusResponse/VehicleStatus/CreatedDateTime)[1]', 'datetime2(3)')
, #xml.value('(/VehicleStatusResponse/VehicleStatus/ReceivedDateTime)[1]', 'datetime2(3)')
, #xml.value('(/VehicleStatusResponse/VehicleStatus/GNSSPosition/ns2:Latitude)[1]', 'decimal(8,5)')
, #xml.value('(/VehicleStatusResponse/VehicleStatus/GNSSPosition/ns2:Longitude)[1]', 'decimal(8,5)')
, #xml.value('(/VehicleStatusResponse/VehicleStatus/GNSSPosition/ns2:Heading)[1]', 'int')
, #xml.value('(/VehicleStatusResponse/VehicleStatus/GNSSPosition/ns2:Altitude)[1]', 'int')
, #xml.value('(/VehicleStatusResponse/VehicleStatus/GNSSPosition/ns2:Speed)[1]', 'decimal(8,3)')
, #xml.value('(/VehicleStatusResponse/VehicleStatus/GNSSPosition/ns2:PositionDateTime)[1]', 'datetime2(3)')
, #xml.value('(/VehicleStatusResponse/VehicleStatus/WheelBasedSpeed)[1]', 'decimal(8,3)')
, #xml.value('(/VehicleStatusResponse/VehicleStatus/TachographSpeed)[1]', 'decimal(8,3)')
, #xml.value('(/VehicleStatusResponse/VehicleStatus/HRTotalVehicleDistance)[1]', 'decimal(8,3)')
, #xml.value('(/VehicleStatusResponse/VehicleStatus/EngineTotalFuelUsed)[1]', 'int')
, #xml.value('(/VehicleStatusResponse/VehicleStatus/CatalystFuelLevel)[1]', 'decimal(8,3)')
, #xml.value('(/VehicleStatusResponse/VehicleStatus/GrossCombinationVehicleWeight)[1]', 'int');
First of all you need to get your value into a declare variable of type XML or into an XML-typed data table column. As your XML contains namespaces you have to declare them in a WITH XMLNAMESPACES first. You might use wildcard syntax (*:), but its better to be as specific as possible.
The .nodes() call navigates to the Level of <VehicleStatus>. All elements below are simply 1:1 and easy to read...
You can try it like this:
DECLARE #xml XML=
N'<VehicleStatusResponse xmlns:ns2=
"http://fms-standard.com/rfms/v1.0.0/xsd/common/position" xmlns="http://fms-standard.com/rfms/v1.0.0/xsd/status">
<VehicleStatus>
<VIN>VF254ANA735752628</VIN>
<TriggerType>TIMER</TriggerType>
<CreatedDateTime>2014-09-08T09:30:20</CreatedDateTime>
<ReceivedDateTime>2014-09-08T09:30:57</ReceivedDateTime>
<GNSSPosition>
<ns2:Latitude>49.18557</ns2:Latitude>
<ns2:Longitude>11.18557</ns2:Longitude>
<ns2:Heading>33</ns2:Heading>
<ns2:Altitude>500</ns2:Altitude>
<ns2:Speed>16.4</ns2:Speed>
<ns2:PositionDateTime>2014-09-08T09:30:20</ns2:PositionDateTime>
</GNSSPosition>
<WheelBasedSpeed>16.07</WheelBasedSpeed>
<TachographSpeed>15.83</TachographSpeed>
<HRTotalVehicleDistance>817.5</HRTotalVehicleDistance>
<EngineTotalFuelUsed>575</EngineTotalFuelUsed>
<FuelLevel1>83</FuelLevel1>
<CatalystFuelLevel>88.48</CatalystFuelLevel>
<GrossCombinationVehicleWeight>10000</GrossCombinationVehicleWeight>
</VehicleStatus>
</VehicleStatusResponse>';
--This is the query
WITH XMLNAMESPACES(DEFAULT 'http://fms-standard.com/rfms/v1.0.0/xsd/status'
,'http://fms-standard.com/rfms/v1.0.0/xsd/common/position' AS ns2)
SELECT vs.value('VIN[1]','nvarchar(max)') AS VehicleStatus_VIN
,vs.value('TriggerType[1]','nvarchar(max)') AS VehicleStatus_TriggerType
,vs.value('CreatedDateTime[1]','datetime') AS VehicleStatus_CreatedDateTime
,vs.value('ReceivedDateTime[1]','datetime') AS VehicleStatus_ReceivedDateTime
,vs.value('(GNSSPosition/ns2:Latitude)[1]','decimal(14,6)') AS VehicleStatus_GNSSPosition_Latitude
,vs.value('(GNSSPosition/ns2:Longitude)[1]','decimal(14,6)') AS VehicleStatus_GNSSPosition_Longitude
/*other columns follow the same pattern*/
FROM #xml.nodes('/VehicleStatusResponse/VehicleStatus') AS A(vs)
update: insert into a table
Easiest was to wrap this call as CTE like
WITH XMLNAMESPACES(...)
,DerivedTableCTE AS
(
The query here
)
INSERT INTO YourTable (col1, col2, col3, ...)
SELECT col1, col2, col3, ...
FROM DerivedTableCTE

Create XML using TSQL

I am trying to create the following XML document and am having trouble understanding how to do this using TSQL. I have done a lot of research and believe I can do this using XML Path instead of XML Explicit. Any help trying to create the below output would be greatly appreciated.
Sample Dataset:
CREATE TABLE #DataTable
(
PortfolioShortName varchar(20)
,PortfolioLongName varchar(255)
,BenchmarkName varchar(255)
,BenchmarkCode varchar(255)
,PositionGroupName varchar(255)
)
INSERT INTO #DataTable (PortfolioShortName,PortfolioLongName,BenchmarkName,BenchmarkCode,PositionGroupName)
VALUES ('TESTIG','Long Division Short Duration','SP 500','SP.MC.SP500#rmgBenchmarks','Test Strategy'),
('TESTEM','Short Duration Emerging Market','Dow','Dow.MC.Dow#rmgBenchmarks','Test Strategy')
SELECT
*
FROM #DataTable dt
XML I am trying to create:
<?xml version="1.0" encoding="UTF-8"?>
<positionGroupMetadata xmlns="urn:TestingData-types" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:TestingData:1.0:positiongroupmetadata-types file:///C:/Users/BillBill/Desktop/Schemas/positiongroupmetadata-types.xsd">
<setIdentification>
<client>Georgia Capital</client>
<name>Testing Service</name>
<date>20150924</date>
</setIdentification>
<positionGroupItemList>
<positionGroupItem>
<positionGroupID type="client">
<positionGroupValue>TESTIG</positionGroupValue>
</positionGroupID>
<tagGroupList>
<tagGroup>
<name>Benchmarks</name>
<tagList>
<tag>
<type>defaultBenchmarkName</type>
<value>Long Division Short Duration</value>
</tag>
<tag>
<type>defaultBenchmarkPrimaryIdentifier</type>
<value>SP.MC.SP500#rmgBenchmarks</value>
</tag>
</tagList>
</tagGroup>
</tagGroupList>
</positionGroupItem>
</positionGroupItemList>
<positionGroupByWeightList>
<positionGroupByWeight>
<positionGroupName>Test Strategy</positionGroupName>
<positionList>
<position>
<positionGroupID type="client">
<positionGroupValue>TESTIG</positionGroupValue>
</positionGroupID>
</position>
<position>
<positionGroupID type="client">
<positionGroupValue>TESTEM</positionGroupValue>
</positionGroupID>
</position>
</positionList>
</positionGroupByWeight>
</positionGroupByWeightList>
<reportSetList>
<reportSet>
<reportSetName>DailyReports</reportSetName>
<reportDataSetList>
<reportDataSet>
<reportDataSetName>TESTIG</reportDataSetName>
<positionGroup>
<positionGroupIDList>
<positionGroupID type="client">
<positionGroupValue>TESTIG</positionGroupValue>
<holdingGroupAlias>Portfolio</holdingGroupAlias>
</positionGroupID>
</positionGroupIDList>
</positionGroup>
</reportDataSet>
<reportDataSet>
<reportDataSetName>TESTIG</reportDataSetName>
<positionGroup>
<positionGroupIDList>
<positionGroupID type="client">
<positionGroupValue>TESTIG</positionGroupValue>
<holdingGroupAlias>Portfolio</holdingGroupAlias>
</positionGroupID>
</positionGroupIDList>
</positionGroup>
</reportDataSet>
</reportDataSetList>
</reportSet>
</reportSetList>
</positionGroupMetadata>
This is as far as I could get. I am not sure how to build the upper list and work my way up the structure.
SELECT DISTINCT
type = 'defaultBenchmarkPrimaryIdentifier'
,value = dt.BenchmarkName
FROM #DataTable dt
UNION ALL
SELECT
type = 'defaultBenchmarkPrimaryIdentifier'
,dt.BenchmarkCode AS defaultBenchmarkPrimaryIdentifier
FROM #DataTable dt
FOR XML PATH('tag'),ROOT('type');
You did not give enough information... And please do the hard work yourself. The following is a beginning which will help you to understand the abilities of FOR XML PATH.
The output is not yet complete (how could it be...) and it is not yet exactly what you posted as needed. But you will be able to adapt it and come back with clear questions upon details you cannot solve on your own.
One point to state immediately: The repetition of the namespaces in nested XML cannot be surpressed with SQL Server 2012 (AFAIK). You could go on without namespaces and fill them in after the create process with string functions...
Good luck!
CREATE TABLE #DataTable
(
PortfolioShortName varchar(20)
,PortfolioLongName varchar(255)
,BenchmarkName varchar(255)
,BenchmarkCode varchar(255)
,PositionGroupName varchar(255)
)
INSERT INTO #DataTable (PortfolioShortName,PortfolioLongName,BenchmarkName,BenchmarkCode,PositionGroupName)
VALUES ('TESTIG','Long Division Short Duration','SP 500','SP.MC.SP500#rmgBenchmarks','Test Strategy'),
('TESTEM','Short Duration Emerging Market','Dow','Dow.MC.Dow#rmgBenchmarks','Test Strategy')
SELECT
*
FROM #DataTable dt;
WITH XMLNAMESPACES('http://www.w3.org/2001/XMLSchema-instance' AS [xsi]
,'urn:TestingData:1.0:positiongroupmetadata-types file:///C:/Users/BillBill/Desktop/Schemas/positiongroupmetadata-types.xsd' AS [schemaLocation]
,DEFAULT 'urn:TestingData-types')
SELECT 'Georgia Capital' AS [setIdentification/client]
,'Testing Service' AS [setIdentification/name]
,CONVERT(VARCHAR(8),GETDATE(),112) AS [setIdentification/date]
,node1.positionGroupItemList
FROM (SELECT NULL AS dummy) AS dummy
CROSS APPLY
(
SELECT 'client' AS [positionGroupID/#type]
,PortfolioShortName AS [positionGroupID/positionGroupValue]
,'Benchmarks' AS [tagGroupList/tagGroup/name]
,'defaultBenchmarkName' AS [tagGroupList/tagGroup/tagList/tag/type]
,PortfolioLongName AS [tagGroupList/tagGroup/tagList/tag/value]
,'defaultBenchmarPrimaryIdentifier' AS [tagGroupList/tagGroup/tagList/tag/type]
,BenchmarkCode AS [tagGroupList/tagGroup/tagList/tag/value]
FROM #DataTable
FOR XML PATH('positionGroupItem'),TYPE
) AS node1(positionGroupItemList)
FOR XML PATH(''),ROOT('positionGroupMetadata')
GO
DROP TABLE #DataTable
I went with a very unique approach using strings / cursors to manipulate the data into the format I needed. I worked my way down the structure adding more and more strings together for each element until I had the entire XML document.
Thanks for the hep stack overflow.
DECLARE #EndText varchar(MAX)
SELECT #StartText = '<positionGroupByWeight>
<positionGroupName>' + #Portfolio + '</positionGroupName>
<positionList>'
SELECT #EndText = '</positionList>
</positionGroupByWeight>'
SET #XML = CONVERT(varchar(max),ISNULL(#StartText,'')) + '' + CONVERT(varchar(max),#EndText)
SELECT #XML

Naming xml node based on value from DB column

Currently I have an xml structure that is built like this.
<Project>
<ProjectAttribute>
<AttributeId></AttributeId>
<FieldName></FieldName>
<Title></Title>
<DisplayRow></DisplayRow>
<DisplayColumn></DisplayColumn>
<IsRequired></IsRequired>
<RequiredErrorMessage></RequiredErrorMessage>
<DevExpressControl>
<DevExpressControlId></DevExpressControlId>
<Class></Class>
<ClientNamePrefix></ClientNamePrefix>
</DevExpressControl>
<DataType>
<DataTypeId></DataTypeId>
<Name></Name>
</DataType>
</ProjectAttribute>
</Project>
I want to rename ProjectAttribute with the value of FieldName. I am unsure how to do this at the moment, seeing how when I try to put the value of that field into the XML Path, I get an error.
Here is what my sql looks like.
Set #output = (
SELECT poa.AttributeId, pa.FieldName, pa.Title, poa.DisplayRow,
poa.DisplayColumn, poa.IsRequired, pa.RequiredErrorMessage,
(SELECT de.DevExpressControlId, de.Class, de.ClientNamePrefix
FROM SCC_DevExpressControl de
WHERE de.DevExpressControlId = pa.DevExpressControlId
FOR XML Path(''), type) AS DevExpressControl,
(SELECT dt.DataTypeId, dt.Name
FROM SCC_DataType dt
WHERE dt.DataTypeId = pa.DataTypeId
FOR XML Path(''), type) AS DataType
FROM SCC_ProjectOfficeAttribute poa
LEFT JOIN SCC_ProjectAttribute pa ON poa.AttributeId = pa.AttributeId
WHERE poa.OfficeId = #OfficeId
AND poa.Status = 1
ORDER BY poa.DisplayRow, poa.DisplayColumn
FOR XML Path('ProjectAttribute'), type, Root('Project'))
select #output
#OfficeId is just an int, and #output is simply the xml. Where I am writing in the XML Path for ProjectAttribute is what I want to replace. My question is how do I get that value in there?
UPDATE: I am still somewhat at a loss. I have tried moving things like AttributeId and FieldName into the main node itself for ProjectAttribute, but this does not really fulfill my requirements.

Modify XML values identified through cross apply

I've got a data issue with some values stored in an XML column in a database. I've reproduced the problem as the following example:
Setup Script:
create table XMLTest
(
[XML] xml
)
--A row with two duff entries
insert XMLTest values ('
<root>
<item>
<flag>false</flag>
<frac>0.5</frac>
</item>
<item>
<flag>false</flag>
<frac>0</frac>
</item>
<item>
<flag>false</flag>
<frac>0.5</frac>
</item>
<item>
<flag>true</flag>
<frac>0.5</frac>
</item>
</root>
')
In the XML portion the incorrect entries are those with <flag>false</flag> and <frac>0.5</frac> as the value of flag should be true for non-zero frac values.
The following SQL identifies the XML item nodes that require update:
select
i.query('.')
from
XMLTest
cross apply xml.nodes('root/item[flag="false" and frac > 0]') x(i)
I want to do an update to correct these nodes, but I don't see how to modify the item elements identified by a cross apply. I saw the update as looking something like this:
update t
set
x.i.modify('replace value of (flag/text())[1] with "true"')
from
XMLTest t
cross apply xml.nodes('root/item[flag="false" and frac > 0]') x(i)
However this isn't working: I get the error "Incorrect syntax near 'modify'".
Can this be done through this method?
I know an alternative would be to do a string replace on the xml column, but I don't like that as being a bit unsubtle (and I'm not confident it wouldn't break things in my real-word problem)
It is not possible to update the one XML instance in more than one place at a time so you have to do the updates in a loop until you are done.
From http://msdn.microsoft.com/en-us/library/ms190675.aspx "Expression1: Identifies a node whose value is to be updated. It must identify only a single node."
-- While there are rows that needs to be updated
while exists(select *
from XMLTest
where [XML].exist('root/item[flag="false" and frac > 0]') = 1)
begin
-- Update the first occurence in each XML instance
update XMLTest set
[XML].modify('replace value of (root/item[flag="false" and frac > 0]/flag/text())[1] with "true"')
where xml.exist('root/item[flag="false" and frac > 0]') = 1
end