Parsing XML with namespace in SQL Server 2008 - sql-server-2008

I have following xml in my table. I want to get ID of a fv node:
<fv ID="Description"> or not
<DataFormItem xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="bb_appfx_dataforms">
<Values>
<fv ID="JobInterval">
<Value xsi:type="xsd:int">10</Value>
</fv>
<fv ID="JobEnabled">
<Value xsi:type="xsd:boolean">false</Value>
</fv>
<fv ID="JobName">
<Value xsi:type="xsd:string">Default transactional email process</Value>
</fv>
<fv ID="ScheduleFrequencySubDayType">
<Value xsi:type="xsd:int">2</Value>
</fv>
</Values>
</DataFormItem>

Assuming your table is called YourTable and the XML column is called XmlData (adapt and change this to meet your actual situation!), you can try this XQuery code:
-- declare the XML namespace as the DEFAULT
;WITH XMLNAMESPACES(DEFAULT 'bb_appfx_dataforms')
SELECT
-- extract the value from the <Value> subnode of the <fv> node, if found
FvValue = XC.value('(Value)[1]', 'varchar(200)')
FROM
dbo.YourTable
CROSS APPLY
-- get a list of XML fragments - one for each <fv> node
XmlData.nodes('/DataFormItem/Values/fv') AS XT(XC)
WHERE
-- find the XML fragment with the ID="Description"
XC.value('#ID', 'varchar(50)') = 'Description'

Related

Convert local-name() from MSSQL to MySQL

I'm working on migrating some codes from MSSQL to MySQL, but I don't know the equivalent of local-name()in MySQL.
The original MSSQL code is as followed:
with cte as (select convert(xml,'<Attributes>
<Map>
<entry key="trigger" value = "action">
<value>
<Map>
<entry key=" Processing">
<value>
<Boolean>true</Boolean>
</value>
</entry>
</Map>
</value>
</entry>
</Map>
</Attributes>') as attributes)
select entr.value('#key','nvarchar(100)') AS AttrKey
,entr.value('#value','nvarchar(500)') AS AttrValue
,HasValueElement.value('local-name(.)','nvarchar(100)') AS ValueType
FROM cte a1
cross apply a1.attributes.nodes(N'/Attributes/Map/entry') A(entr)
OUTER APPLY A.entr.nodes(N'value/*') B(HasValueElement)
The output is:
AttrKey ||AttrValue ||ValueType
Trigger ||Action ||Map
How we can apply similar to local-name() in MySQL to return the ValueType = "Map"? When I tried something like "//*[local-name()]" in MySQL, it returned error.
And what is the "." in "local-name(.)" meaning?
Really appreciate your support.
TIA

Iterate over XML node then check if has a matching json value then select the value

Apparently I have a field call Data which store some xml node + json inside the those node.
I can retrieve the deliveryID by using:
JSON_VALUE(ml.Data.value('(/row/value)[2]', 'NVARCHAR(MAX)'),'$.transactions[0].deliveryId') deliveryID
However, the transactions might not always be at the second node of it,
it can be on any row.
Is there any other way I can do to iterate the node and find if it has a json transactions inside then get the deliveryID from the row?
Thanks
<row>
<value id="1ae95d67-599e-4ab6-9ffd-08d4d90ab608" display-name="Cardholder_id" data-type="Int32">17</value>
<value id="1ae95d67-599e-4ab6-9ffd-08d4d90ab608" display-name="Cardholder_id" data-type="Int32">17</value>
<value id="eb71fd46-f0b2-401d-9775-08d4d90ab608" display-name="Card_Number">3083 2614 5022 21321</value>
<value id="4fc261b2-f776-4fd4-8e1d-08d4d90ab608" display-name="Email_Address">jello#anc.com</value>
<value id="c867d4e5-cc0b-4ee6-b911-08d6134132e0" display-name="BP_TRIGGERS_2.0">{"transactions":[{"BP_CommsRef":"V0001","BP_Offer_Expiry":"2018-10-01T00:00:00","deliveryId":"20320925","Job_Number":"A34F443","Send_Date":"2018-09-26T00:00:00"}]}</value>
</row>
If I get you correctly, you want to find a JSON within your <value> elements and read the deliverId from there:
Declare #XML xml = '
<row>
<value id="1ae95d67-599e-4ab6-9ffd-08d4d90ab608" display-name="Cardholder_id" data-type="Int32">17</value>
<value id="1ae95d67-599e-4ab6-9ffd-08d4d90ab608" display-name="Cardholder_id" data-type="Int32">17</value>
<value id="eb71fd46-f0b2-401d-9775-08d4d90ab608" display-name="Card_Number">3083 2614 5022 21321</value>
<value id="4fc261b2-f776-4fd4-8e1d-08d4d90ab608" display-name="Email_Address">jello#anc.com</value>
<value id="c867d4e5-cc0b-4ee6-b911-08d6134132e0" display-name="BP_TRIGGERS_2.0">{"transactions":[{"BP_CommsRef":"V0001","BP_Offer_Expiry":"2018-10-01T00:00:00","deliveryId":"20320925","Job_Number":"A34F443","Send_Date":"2018-09-26T00:00:00"}]}</value>
</row>';
--This will check for an opening { (you could search for an opening {"transactions" as well)
SELECT JSON_VALUE(
#XML.query(N'/row/value[substring(text()[1],1,1)="{"]/node()')
.value(N'text()[1]','nvarchar(max)')
,N'$.transactions[0].deliveryId');
--The same approach but looking at the attribute display-name
SELECT JSON_VALUE(
#XML.query(N'/row/value[#display-name="BP_TRIGGERS_2.0"]/node()')
.value(N'text()[1]','nvarchar(max)')
,N'$.transactions[0].deliveryId');
The idea in short: .query() the XML for a <value> below <row>, which fullfills the filter. Take this node() and read the .value() from there.
This is handed into JSON_VALUE in order to retrieve the needed deliveryId.
You can do the same with the attribute id and you can hand in the values with a declard variable (sql:variable()) or from a column in your result-set (sql:column()).

XML to SQL Server 2008 extract

I need further assistance with extracting records from XML and loading it into a SQL Server table.
I have this as my #xml:
<admin submitter_id="login0" effective_date="mm/dd/yyyy">
<rec effected_id="login1" adjustment="100.00" type="foo">
<reason reason_id="1" />
<reason reason_id="2" />
</rec>
<rec effected_id="login2" adjustment="50.00" type="bar">
<reason reason_id="3" />
</rec>
</admin>
I need this from a result set:
login0, login1, mm/dd/yyyy, 100.00, foo, 1
login0, login1, mm/dd/yyyy, 100.00, foo, 2
login0, login2, mm/dd/yyyy, 50.00, bar, 3
Does that make sense? The adjustment to the reason_id is one to many. I have figured out how to extract all the values except for the second line. I can only obtain the first reason_id and then it proceeds to the next record. I think this can be beaten with a CROSS APPLY but I cannot get it to work. Please help!
oh, I may also have received bogus XML. So if that's wrong, please tell me!
How about something like
DECLARE #Xml XML
SELECT #Xml = '<admin submitter_id="login0" effective_date="mm/dd/yyyy">
<rec effected_id="login1" adjustment="100.00" type="foo">
<reason reason_id="1" />
<reason reason_id="2" />
</rec>
<rec effected_id="login2" adjustment="50.00" type="bar">
<reason reason_id="3" />
</rec>
</admin>'
SELECT #Xml,
A2.B.value('(../../#submitter_id)[1]','VARCHAR(50)'),
A2.B.value('(../#effected_id)[1]','VARCHAR(50)'),
A2.B.value('(../../#effective_date)[1]','VARCHAR(50)'),
A2.B.value('(../#adjustment)[1]','FLOAT'),
A2.B.value('(../#type)[1]','VARCHAR(50)'),
A2.B.value('(#reason_id)[1]','INT')
FROM #XML.nodes('//admin/rec/reason') A2(B)
SQL Fiddle DEMO
Try to use this T-SQL code snippet:
SELECT
Submitter = #xml.value('(/admin/#submitter_id)[1]', 'varchar(50)'),
EffectedID = Rec.value('(#effected_id)[1]', 'varchar(50)'),
DateStamp = #xml.value('(/admin/#effective_date)[1]', 'varchar(50)'),
TypeID = Rec.value('(#type)[1]', 'varchar(50)'),
ReasonID = Reason.value('(#reason_id)[1]', 'int')
FROM
#xml.nodes('/admin/rec') AS Tbl(Rec)
CROSS APPLY
Rec.nodes('reason') AS T2(Reason)
It gives me an output of:
You need to have two nested lists of nodes - the first one grabs all the <rec> nodes from inside <admin>, while the second one iterates over all the <reason> nodes inside each of the <rec> nodes. That way, you can reliably extract all information from those two nested levels of subnodes, no matter how many subnodes there are on each level.

Delete xml node from SQL Server 2008 table column based on element value

I have an xml structure as follows
<ControlVisibilityRule xmlns="urn:gjensidige:processguide:201201">
<Id>e68c53a3-79ba-41e4-81cb-341050016783</Id>
<Code>b53b687c-2617-4d02-8aa0-4d0d0898bd15_Code</Code>
<Author />
<Updated>9/5/2012</Updated>
<Sequence>0</Sequence>
<FromControls>
<Control>
<Code>ada</Code>
<Id>ba2abc55-3b25-4280-8bd0-a1d23a4575d0</Id>
<FilterValues>
<FilterValue xmlns:p5="urn:gjensidige:processguide:201201" p5:Id="e2b830f8-9a58-4edf-b56c-4c5a9580f362" p5:Code="1" p5:LookupId="ebb6066f-a976-4dcb-aabe-07c4f7d2686b" />
<FilterValue xmlns:p5="urn:gjensidige:processguide:201201" p5:Id="44e268ef-2869-4df1-b61e-c59c2d3f1a5a" p5:Code="56" p5:LookupId="ebb6066f-a976-4dcb-aabe-07c4f7d2686b" />
</FilterValues>
</Control>
</FromControls>
<ToControls>
<Control>
<Code>adeUnittest01</Code>
<Id>0a1cd240-20ee-4405-9613-d3006693c390</Id>
</Control>
</ToControls>
<IsVisible>True</IsVisible>
</ControlVisibilityRule>
I want to delete the element Id in the xpath
/qn:ControlVisibilityRule/qn:ToControls/qn:Control/qn:Id
whose value is 0a1cd240-20ee-4405-9613-d3006693c390. I've done till below but I'm sure I'm missing comparison of element value with a variable.
declare #Id nvarchar(50)
declare #ruleId nvarchar(50)
;WITH XMLNAMESPACES ('urn:gjensidige:processguide:201201' as qn)
update
pdr_processdefinitionrule
set
PDR_RuleXml.modify('delete (/qn:ControlVisibilityRule/qn:ToControls/qn:Control/qn:Id=sql:variable("#Id"))')
where pdr_guid = #ruleId
Any guidance will be appreciated
You need to do this instead:
;WITH XMLNAMESPACES ('urn:gjensidige:processguide:201201' as qn)
update
pdr_processdefinitionrule
set
PDR_RuleXml.modify('delete (/qn:ControlVisibilityRule/qn:ToControls/qn:Control/qn:Id[text()=sql:variable("#Id")])')
where
pdr_guid = #ruleId
Basically, you want to delete the qn:Id element where the text() (the text that XML element contains) matches your variable #Id.

Parsing xml stored in table records in the select statement - SQL Server

I've run into a problem while trying to parse the xml which is stored in the table records
The xml structure is following :
<?xml version="1.0" encoding="utf-16"?>
<WidgetsLayout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<WidgetsList>
<WidgetConfiguration>
<WidgetId>4</WidgetId>
<DockOrder>0</DockOrder>
<DockZoneId>0</DockZoneId>
<Width />
<Height />
<IsRemovable>true</IsRemovable>
</WidgetConfiguration>
<WidgetConfiguration>
<WidgetId>3</WidgetId>
<DockOrder>0</DockOrder>
<DockZoneId>1</DockZoneId>
<Width />
<Height />
<IsRemovable>true</IsRemovable>
</WidgetConfiguration>
</WidgetsList>
</WidgetsLayout>
And the xml is stored as varchar in the table records.
I need to get the temporary table which will contain distinct set of WidgetId from the xml structure.
UPDATED :
I did write the following batch statement to retrieve the set on WidgetConfiguration xml strings, so I would be able to retrieve WidgetId set, but I've run into a problem with the insert statement:
GO
declare #dashboard_layout table (
id int,
config_xml xml
)
INSERT INTO #dashboard_layout(id)
SELECT
widget_config.value('(WidgetId)[1]', 'int')
FROM
dbo.dashboard_configuration c
CROSS APPLY
CAST(RIGHT(c.configuration_xml_string, LEN(c.configuration_xml_string) - 41), XML).nodes('/WidgetsLayout/WidgetsList/WidgetConfiguration') AS list(widget_config)
select * from #dashboard_layout
I've got an syntax error in last insert statement line when calling 'nodes' on 'cast' result
Thanks in advance.
Try this - this would work:
DECLARE #input XML = '<WidgetsLayout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<WidgetsList>
<WidgetConfiguration>
<WidgetId>4</WidgetId>
<DockOrder>0</DockOrder>
<DockZoneId>0</DockZoneId>
<Width />
<Height />
<IsRemovable>true</IsRemovable>
</WidgetConfiguration>
<WidgetConfiguration>
<WidgetId>3</WidgetId>
<DockOrder>0</DockOrder>
<DockZoneId>1</DockZoneId>
<Width />
<Height />
<IsRemovable>true</IsRemovable>
</WidgetConfiguration>
</WidgetsList>
</WidgetsLayout>'
SELECT
WList.value('(WidgetId)[1]', 'int')
FROM
#input.nodes('/WidgetsLayout/WidgetsList/WidgetConfiguration') AS Widget(WList)
So basically:
store your XML as datatype XML - or if you can't, you will have to convert your VARCHAR column to XML do to the processing
grab the list of <WidgetsLayout>/<WidgetsList>/<WidgetConfiguration> nodes as a "pseudo-table"
extract the <WidgetId> element from each of the members of that pseudo table as an INT
Update: OK, to do this from a table, use this:
INSERT INTO #dashboard_layout(ID)
SELECT
WList.value('(WidgetId)[1]', 'int')
FROM
dbo.dashboard_configuration c
CROSS APPLY
CAST(c.YourColumn AS XML).nodes('/WidgetsLayout/WidgetsList/WidgetConfiguration') AS Widget(WList)