XQuery retrieval of a value - sql

I have the following T-SQL that determines if a row exists using two criteria:
Declare #x xml = '
<row ParentID="45" ObjectID="0" Node="root.local.navigation[7]" itemKey="page" itemValue="Confirmation" itemType="string" />
<row ParentID="45" ObjectID="0" Node="root.local.navigation[7]" itemKey="visited" itemValue="false" itemType="bool" />'
SELECT #x.exist('/row[#Node eq "root.local.navigation[7]"] and /row[#itemValue eq "Confirmation"]')
Question: Given the above SELECT, how can I SELECT the second row's itemValue?
i.e. Since there's a row with Node="root.local.navigation[7]" and itemValue="Confirmation", return the itemType value in the row where node is the same and itemKey="visited"?

How about this:
declare #x xml = '
<row ParentID="45" ObjectID="0" Node="root.local.navigation[7]" itemKey="page" itemValue="Confirmation" itemType="string" />
<row ParentID="45" ObjectID="0" Node="root.local.navigation[7]" itemKey="visited" itemValue="false" itemType="bool" />'
select case when #x.exist('/row[#Node eq "root.local.navigation[7]"] and /row[#itemValue eq "Confirmation"]') = 1
then #x.value('/row[#Node eq "root.local.navigation[7]" and #itemKey eq "visited"][1]/#itemType', 'varchar(50)')
end as item_type

Related

Looping thru xml then updating xml variable in SQL

My xml looks like the ff:
<root>
<TemplateQuestion>
<Row rfqID="1" rftID="1" questionDesc="Question 1" responseType="1" rfqDisplayOrder="1" deletedBit="0" />
<Row rfqID="2" rftID="1" questionDesc="Question 2" responseType="2" rfqDisplayOrder="2" deletedBit="0" />
<Row rfqID="3" rftID="1" questionDesc="Question 3" responseType="3" rfqDisplayOrder="3" deletedBit="0" />
</TemplateQuestion>
</root>
Now my goal is to make the rfqID to have the letter "q" before it. So the results should be like the ff:
<root>
<TemplateQuestion>
<Row rfqID="q1" rftID="1" questionDesc="Question 1" responseType="1" rfqDisplayOrder="1" deletedBit="0" />
<Row rfqID="q2" rftID="1" questionDesc="Question 2" responseType="2" rfqDisplayOrder="2" deletedBit="0" />
<Row rfqID="q3" rftID="1" questionDesc="Question 3" responseType="3" rfqDisplayOrder="3" deletedBit="0" />
</TemplateQuestion>
</root>
I am achieving that by doing this:
declare #xml XML
set #xml = (select dbo.udfGetXMLVal(1))
declare #nodeCount int
declare #i int
declare #qid nvarchar(20)
set #i = 1
select #nodeCount = #xml.value('count(/root/TemplateQuestion/Row/#rfqID)','int')
while(#i <= #nodeCount)
begin
select #qid = x.value('#rfqID[1]', 'VARCHAR(20)')
from #xml.nodes('/root/TemplateQuestion/Row[position()=sql:variable("#i")]') e(x)
set #qid = 'q' + #qid
select #qid
Set #xml.modify('replace value of (/root/TemplateQuestion/Row/#rfqID)[1] with sql:variable("#qid")')
set #i = #i + 1
end
Im having problems with this line:
Set #xml.modify('replace value of (/root/TemplateQuestion/Row/#rfqID)[1] with sql:variable("#qid")')
How can I replace the [1] to the variable #i? I get some error with string literals when I try to use sql:variable.
Any help you could provide would be greatly appreciated. Thank you
"How can I replace the [1] to the variable #i? I get some error with string literals when I try to use sql:variable"
You can do it like this (tested and works in SQL Server 2008R2) :
Set #xml.modify('
replace value of ((/root/TemplateQuestion/Row/#rfqID)[sql:variable("#i")] )[1]
with sql:variable("#qid")
')
Quick and dirty :-)
SELECT CAST(REPLACE(CAST(#x AS VARCHAR(MAX)),' rftID="',' rftID="q') AS XML);
And here's a clean approach:
DECLARE #x XML='<root>
<TemplateQuestion>
<Row rfqID="1" rftID="1" questionDesc="Question 1" responseType="1" rfqDisplayOrder="1" deletedBit="0" />
<Row rfqID="2" rftID="1" questionDesc="Question 2" responseType="2" rfqDisplayOrder="2" deletedBit="0" />
<Row rfqID="3" rftID="1" questionDesc="Question 3" responseType="3" rfqDisplayOrder="3" deletedBit="0" />
</TemplateQuestion>
</root>';
SELECT
(
SELECT 'q' + R.value('#rfqID','varchar(max)') AS [#rfqID]
,R.value('#rftID','int') AS [#rftID]
,R.value('#questionDesc','varchar(max)') AS [#questionDesc]
--other attributes similar
FROM #x.nodes('/root/TemplateQuestion/Row') AS A(R)
FOR XML PATH('Row'),ROOT('TemplateQuestion'),TYPE
)
FOR XML PATH('root');

Converting XML node values to comma separated values in SQL

I am trying to convert XML node values to comma separated values but, getting a
Incorrect syntax near the keyword 'SELECT'.
error message
declare #dataCodes XML = '<Root>
<List Value="120" />
<List Value="110" />
</Root>';
DECLARE #ConcatString VARCHAR(MAX)
SELECT #ConcatString = COALESCE(#ConcatString + ', ', '') + Code FROM (SELECT T.Item.value('#Value[1]','VARCHAR(MAX)') as Code FROM #dataCodes.nodes('/Root/List') AS T(Item))
SELECT #ConcatString AS Result
GO
I tried to follow an article but not sure how to proceed further. Any suggestion is appreciated.
Expectation:
Comma separated values ('120,110') stored in a variable.
Try this;
DECLARE #dataCodes XML = '<Root>
<List Value="120" />
<List Value="110" />
</Root>';
DECLARE #ConcatString VARCHAR(MAX)
SELECT #ConcatString = COALESCE(#ConcatString + ', ', '') + Code
FROM (
SELECT T.Item.value('#Value[1]', 'VARCHAR(MAX)') AS Code
FROM #dataCodes.nodes('/Root/List') AS T(Item)
) as TBL
SELECT #ConcatString AS Result
GO
You just need to add an alias to your sub SQL query.
For future readers, XML data can be extracted into arrays, lists, vectors, and variables for output in comma separated values more fluidly using general purpose languages. Below are open-source solutions using OP's needs taking advantage of XPath.
Python
import lxml.etree as ET
xml = '<Root>\
<List Value="120" />\
<List Value="110" />\
</Root>'
dom = ET.fromstring(xml)
nodes = dom.xpath('//List/#Value')
data = [] # LIST
for elem in nodes:
data.append(elem)
print((", ").join(data))
120, 110
PHP
$xml = '<Root>
<List Value="120" />
<List Value="110" />
</Root>';
$dom = simplexml_load_string($xml);
$node = $dom->xpath('//List/#Value');
$data = []; # Array
foreach ($node as $n){
$data[] = $n;
}
echo implode(", ", $data);
120, 110
R
library(XML)
xml = '<Root>
<List Value="120" />
<List Value="110" />
</Root>'
doc<-xmlInternalTreeParse(xml)
data <- xpathSApply(doc, "//List", xmlGetAttr, 'Value') # LIST
print(paste(data, collapse = ', '))
120, 110
To do this without a variable, you can use the nodes method to convert the xml nodes into a table format with leading commas, then use FOR XML PATH('') to collapse it into a single line of XML, then wrap that in STUFF to convert it to varchar and strip off the initial leading comma:
DECLARE #dataCodes XML = '<Root>
<List Value="120" />
<List Value="110" />
</Root>';
SELECT STUFF(
(
SELECT ', ' + T.Item.value('#Value[1]', 'VARCHAR(MAX)')
FROM #dataCodes.nodes('/Root/List') AS T(Item)
FOR XML PATH('')
), 1, 2, '')

how to delete empty namespace from child element in sql server

I am trying to delete empty namespace from the child element. I tried with following code but its not deleting
SET #xDocTemp.modify('declare default element namespace "mynamepsace";
delete /worksh/Data/row[#xmlns=""]')
xml data:
<worksh xmlns="mynamespace">
<Data>
<row r="1" ht="18">
<row xmlns="" rl="39" spans="2">
<row xmlns="" rl="39" spans="2">
</Data>
<worksh>
Expected output
<worksh xmlns="mynamespace">
<Data>
<row rl="1" ht="18">
<row rl="39" spans="2">
<row rl="39" spans="2">
</Data>
<worksh>
not sure if it's possible with modify(), but you can just replace it like
set #xDocTemp = select cast(replace(cast(#xDocTemp as nvarchar(max)), ' xmlns=""', '') as xml)

Searching for "|" pipe symbol in XML column in SQL server

I am using a script like this to look for a "|" symbol in a XML column no matter where it appears. I know there are pipes in there but the below query gives me empty results
SELECT TOP 100 *
FROM
[DB].[dbo].[InputData]
WHERE
Content.exist('//.[text() = "|"]') = 1
AND DataFileId = '75d48aed6327'
What am I doing wrong? This is the xml content of the column Content:
<CLAIM version="native">
<INPUT>
<HEADER BIRTH_DT="1/1/1941">
<DIAG_CODES>
<DX CODE="7234" />
</DIAG_CODES>
<CON_CODES>
<CON_CODE VALUE="M0" />
</CON_CODES>
<VAL_CODES>
<VAL_CODE CODE="A2" AMT="604.03" />
</VAL_CODES>
</HEADER>
</CLAIM>
Hi was looking the answer and found it here https://stackoverflow.com/a/11738172/1692632
DECLARE #xmlTable TABLE (xmlData XML)
INSERT INTO #xmlTable SELECT N'
<CLAIM version="native">
<HEADER BIRTH_DT="1/1/1941">
<DIAG_CODES>
<DX CODE="7234" />
</DIAG_CODES>
<CON_CODES>
<CON_CODE VALUE="M0" />
</CON_CODES>
<VAL_CODES>
<VAL_CODE CODE="A2" AMT="604.03" />
</VAL_CODES>
</HEADER>
</CLAIM>
'
INSERT INTO #xmlTable SELECT N'
<CLAIM version="native">
<HEADER BIRTH_DT="1/1/1941">
<DIAG_CODES>
<DX CODE="72|34" />
</DIAG_CODES>
<CON_CODES>
<CON_CODE VALUE="M0" />
</CON_CODES>
<VAL_CODES>
<VAL_CODE CODE="A2" AMT="604.03" />
</VAL_CODES>
</HEADER>
</CLAIM>
'
SELECT T.*
FROM #xmlTable AS T
CROSS APPLY T.xmlData.nodes('//#*') as tx(r)
WHERE tx.r.value('contains((.),"|")','bit')=1
Also you can try this one as you tried (this gives without duplicates):
SELECT TOP 100 *
FROM
#xmlTable
WHERE
xmlData.exist('//#*[contains(., "|")]') = 1
SELECT TOP 100 *
FROM [DB].[dbo].[InputData]
WHERE DataFileId = '75d48aed6327'
and charindex('|',Content) > 1

XML query error in SQL Server 2005

For updating a mass records, I used xml query. From the front end (C#.net) I populate xml and pass it to a stored procedure as a parameter (like #Master_rows_xml_Update).
I face a situation.. when I go for this command in the stored procedure
declare #i INTEGER,#Master_rows_xml_Update XML;
SET #Master_rows_xml_Update= '<root>
<row Id="1" FinYearId="5" EmployeeId="55" EnteredOn="7/1/2011 12:00:00 AM" EnteredBy="1" HouseRentPaid="False" HouseRentAmount="3500.00" officeId="9"/>
<row Id="2" FinYearId="5" EmployeeId="55" EnteredOn="7/1/2011 12:00:00 AM" EnteredBy="1" HouseRentPaid="False" HouseRentAmount="3500.00" officeId="9"/>
</root>';
exec sp_xml_preparedocument #i output, #Master_rows_xml_Update;
update EmployeeFinYearInvestment
set EmployeeFinYearInvestment.FinYearId = ox.FinYearId,
EmployeeFinYearInvestment.EmployeeId = ox.EmployeeId,
EmployeeFinYearInvestment.EnteredOn = ox.EnteredOn,
EmployeeFinYearInvestment.EnteredBy = ox.EnteredBy,
EmployeeFinYearInvestment.HouseRentPaid = ox.HouseRentPaid,
EmployeeFinYearInvestment.HouseRentAmount = ox.HouseRentAmount,
EmployeeFinYearInvestment.officeId=ox.officeId
from OpenXml(#i, 'root/row')
with (Id INT,FinYearId INT, EmployeeId INT,EnteredOn DATETIME,EnteredBy INT , HouseRentPaid BIT,HouseRentAmount DECIMAL ,officeId INT ) ox
where EmployeeFinYearInvestment.Id = ox.Id;
exec sp_xml_removedocument #i;
..IT CAN NOT UPDATE ACTION TO FOLLOWING TABLE...
Procedure also not returning any error...
ANY IDEA...
Thanks..
Anirban
I would try to get rid of OpenXml and those function - use the new XQuery/XPath stuff in SQL Server 2005 !
Try this - this should give you the values from your XML:
DECLARE #XmlUpdate XML
SET #XmlUpdate =
'<root>
<row Id="1" FinYearId="5" EmployeeId="55" EnteredOn="7/1/2011 12:00:00 AM" EnteredBy="1" HouseRentPaid="False" HouseRentAmount="3500.00" officeId="9"/>
<row Id="2" FinYearId="5" EmployeeId="55" EnteredOn="7/1/2011 12:00:00 AM" EnteredBy="1" HouseRentPaid="False" HouseRentAmount="3500.00" officeId="9"/>
</root>';
SELECT
UpdRow.value('(#Id)[1]', 'int') AS 'ID',
UpdRow.value('(#FinYearId)[1]', 'int') AS 'FinYearID',
UpdRow.value('(#EmployeeId)[1]', 'int') AS 'EmployeeID',
UpdRow.value('(#EnteredOn)[1]', 'datetime') AS 'EnteredOn',
UpdRow.value('(#EnteredBy)[1]', 'int') AS 'EnteredBy',
UpdRow.value('(#HouseRentPaid)[1]', 'bit') AS 'RentPaid',
UpdRow.value('(#HouseRentAmount)[1]', 'decimal(20,4)') AS 'RentAmount',
UpdRow.value('(#OfficeId)[1]', 'int') AS 'OfficeID'
FROM
#XmlUpdate.nodes('/root/row') AS R(UpdRow)
And of course, if you can select it, you can also use it to update!
UPDATE
dbo.EmployeeFinYearInvestment
SET
EmployeeFinYearInvestment.FinYearId = UpdRow.value('(#FinYearId)[1]', 'int'),
EmployeeFinYearInvestment.EmployeeId = UpdRow.value('(#EmployeeId)[1]', 'int'),
..... (and so on). ......
EmployeeFinYearInvestment.EnteredOn = ox.EnteredOn,
FROM
#XmlUpdate.nodes('/root/row') AS R(UpdRow)
WHERE
EmployeeFinYearInvestment.Id = UpdRow.value('(#Id)[1]', 'int');