I have the below XML when I run the select query it considers only the first value, what I am missing here?
<?xml version="1.0" encoding="utf-16"?>
<ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<string>1</string>
<string>2</string>
<string>3</string>
.............
</ArrayOfString>
Below query only returns 1, I am expecting it to return all values like 1,2,3...
SELECT xmlu.v1.value('string[1]', 'BIGINT')
FROM
#xml.nodes('/ArrayOfString')
AS xmlU(v1)
If you want one row per string node, you need to include the string node in your nodes clause:
SELECT AOS.s.value('(./text())[1]','bigint') AS string --If it's string, why are you asking for a bigint?
FROM (VALUES(#XML))V(X)
CROSS APPLY V.X.nodes('/ArrayOfString/string')AOS(s);
It seems that you are looking for something like the following.
It is better not to use the IN clause because it usually gets converted to inefficient OR expressiions.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, city VARCHAR(20));
INSERT INTO #tbl (city) VALUES
('Miami'),
('Orlando'),
('Dallas'),
('New York'),
('Hollywood');
DECLARE #parameter XML =
N'<ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<string>1</string>
<string>2</string>
<string>3</string>
</ArrayOfString>'
-- DDL and sample data population, end
;WITH rs AS
(
SELECT c.value('.', 'INT') AS ID
FROM #parameter.nodes('/ArrayOfString/string/text()') AS t(c)
)
SELECT *
FROM #tbl AS t INNER JOIN
rs ON t.ID = rs.ID;
Related
I'm struggling with querying a SQL table and returning each row and also splitting out all the "Expressions" xml records into separate rows. I'm tried a few approaches.
This is some test data. I'm hoping to get 2 rows back both id an ID column of 1 and then some columns showing all the attributes of each expression.
CREATE TABLE #MyData (ID INT, MyXML xml)
INSERT INTO #MyData SELECT
1 AS ID
,'<ArrayOfExpressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Expressions>
<ExpressionID>1</ExpressionID>
<FieldName>AlternateDescription</FieldName>
<Operator>is not filled out</Operator>
<Condition>777</Condition>
<AndOr>OR</AndOr>
</Expressions>
<Expressions>
<ExpressionID>2</ExpressionID>
<FieldName>InputMask</FieldName>
<Operator>does not contain</Operator>
<Condition>hello</Condition>
<AndOr>AND</AndOr>
</Expressions>
</ArrayOfExpressions>' AS MyXML
And based on some examples this is as far as I got but I'm a bit lost now....
SELECT * FROM
(select
ID
,MyXMLRows.ExpressionID.value('(text())[1]', 'varchar(32)') as ExpressionID
from
#MyData CROSS APPLY
MyXML.nodes('/ArrayOfExpressions/Expressions') AS Roles(MyXMLRows)
) as MyResults
Try something like this:
SELECT
ID,
ExpressionId = XC.value('(ExpressionID)[1]', 'int'),
FieldName = XC.value('(FieldName)[1]', 'varchar(50)'),
Operator = XC.value('(Operator)[1]', 'varchar(50)'),
Condition = XC.value('(Condition)[1]', 'varchar(50)'),
AndOr = XC.value('(AndOr)[1]', 'varchar(25)')
FROM
#MyData
CROSS APPLY
MyXML.nodes('/ArrayOfExpressions/Expressions') AS XT(XC)
That should return something like this a a result set:
ID
ExpressionId
FieldName
Operator
Condition
AndOr
1
1
AlternateDescription
is not filled out
777
OR
1
2
InputMask
does not contain
hello
AND
I'm trying to shred a table of XML documents into SQL Server columns and am going around in circles with one bit.
Basically I have a table (ID int, XMLData XML) and each row will contain a document in the XML column.
I need to turn it into the following format
(
ID int,
ReferenceCurrency varchar,
TargetCurrency varchar,
ReferenceAmount decimal,
TargetAmount decimal,
DueDate date
)
I've cut down this table.
The code I have inherited was a mix of XML.value and string searches with charindex which haven't been too reliable.
This is a snippet of the message we have.
<ReferenceCurrency>
<Ccy>GBP</Ccy>
</ReferenceCurrency>
<TargetCurrency>
<Ccy>USD</Ccy>
</TargetCurrency>
<BalanceAmtItem Type="technical_account_settlement_balance_due_to_sender">
<Amt Ccy="USD" CcyIndic="reference_currency" Share="receiver_share">65.62</Amt>
<Amt Ccy="USD" CcyIndic="target_currency" Share="receiver_share">96.62</Amt>
<DueDate>2019-09-04</DueDate>
</BalanceAmtItem>
I can get most of the data just using XML.value
SELECT
ID,
XMLDATA.value('(Jv-Ins-Reinsurance/TechAccount/ReferenceCurrency)[1]', 'varchar(4)') AS ReferenceCurrency ,
XMLDATA.value('(Jv-Ins-Reinsurance/TechAccount/TargetCurrency)[1]', 'varchar(4)') AS TargetCurrency,
XMLDATA.value('(Jv-Ins-Reinsurance/TechAccount/BalanceAmtItem/DueDate)[1]', 'date') AS DueDate
FROM
dta
The bit I'm struggling with is how to get the two amount columns where CcyIndic = reference_currency or target_currency.
In this example I would expect to get a single line of 1, GBP, USD, 65.62, 96.62, 2019-09-04
Any help appreciated.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE(ID INT IDENTITY(1,1) PRIMARY KEY, XMLData XML);
INSERT INTO #tbl
VALUES
(N'<Jv-Ins-Reinsurance>
<TechAccount>
<ReferenceCurrency>
<Ccy>GBP</Ccy>
</ReferenceCurrency>
<TargetCurrency>
<Ccy>USD</Ccy>
</TargetCurrency>
<BalanceAmtItem Type="technical_account_settlement_balance_due_to_sender">
<Amt Ccy="USD" CcyIndic="reference_currency" Share="receiver_share">65.62</Amt>
<Amt Ccy="USD" CcyIndic="target_currency" Share="receiver_share">96.62</Amt>
<DueDate>2019-09-04</DueDate>
</BalanceAmtItem>
</TechAccount>
</Jv-Ins-Reinsurance>');
-- DDL and sample data population, end
SELECT ID,
c.value('(ReferenceCurrency/Ccy)[1]', 'varchar(4)') AS ReferenceCurrency ,
c.value('(TargetCurrency/Ccy)[1]', 'varchar(4)') AS TargetCurrency,
c.value('(BalanceAmtItem/Amt[#CcyIndic="reference_currency"])[1]', 'MONEY') AS ref_money,
c.value('(BalanceAmtItem/Amt[#CcyIndic="target_currency"])[1]', 'MONEY') AS target_money,
c.value('(BalanceAmtItem/DueDate)[1]', 'date') AS DueDate
FROM #tbl AS tbl
CROSS APPLY tbl.xmldata.nodes('/Jv-Ins-Reinsurance/TechAccount') AS t(c);
Something like this:
declare #doc xml =
'<ReferenceCurrency>
<Ccy>GBP</Ccy>
</ReferenceCurrency>
<TargetCurrency>
<Ccy>USD</Ccy>
</TargetCurrency>
<BalanceAmtItem Type="technical_account_settlement_balance_due_to_sender">
<Amt Ccy="USD" CcyIndic="reference_currency" Share="receiver_share">65.62</Amt>
<Amt Ccy="USD" CcyIndic="target_currency" Share="receiver_share">96.62</Amt>
<DueDate>2019-09-04</DueDate>
</BalanceAmtItem>';
SELECT
-- ID,
XMLDATA.value('(/ReferenceCurrency)[1]', 'varchar(4)') AS ReferenceCurrency,
XMLDATA.value('(/TargetCurrency)[1]', 'varchar(4)') AS TargetCurrency,
XMLDATA.value('(/BalanceAmtItem/Amt[#CcyIndic="reference_currency"])[1]', 'varchar(4)') AS ReferenceAmount ,
XMLDATA.value('(/BalanceAmtItem/Amt[#CcyIndic="target_currency"])[1]', 'varchar(4)') AS TargetAmount ,
XMLDATA.value('(/BalanceAmtItem/DueDate)[1]', 'date') AS DueDate
FROM
(select #doc XMLDATA) d
outputs
ReferenceCurrency TargetCurrency ReferenceAmount TargetAmount DueDate
----------------- -------------- --------------- ------------ ----------
GBP USD 65.6 96.6 2019-09-04
I have ugly XML that looks like this:
<?xml version="1.0"?>
<MainTag xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" RecordID="201801026543210">
<Field Name="TheFieldName">
<FieldValue>Field Contents</FieldValue>
<ListTag>
<ListItem>
<Value>This List Value</Value>
<Source>source.txt</Source>
<ListType>text</ListType>
<ItemNumber>6912</ItemNumber>
<MoreData>some text here</MoreData>
<Address>address data</Address>
<Ranking>102</Ranking>
</ListItem>
<ListItem>
<Value>Another List Value</Value>
<Source>other.txt</Source>
<ListType>text</ListType>
<ItemNumber>7919</ItemNumber>
<MoreData>more text here</MoreData>
<Address>address data</Address>
<Ranking>41</Ranking>
</ListItem>
</ListTag>
</Field>
</MainTag>
What I want is query results that gives me a spreadsheet, essentially:
RecordID FieldName FieldValue ListValue ListSource ListType ListItemNumber …
201801026543210 TheFieldName Field Contents This List Value source.txt text 6912
201801026543210 TheFieldName Field Contents Another List Value other.txt text 7919
To aid in the fun, the XML is stored in a varchar field, not an XML field.
As example data and queries I've tried:
DECLARE #Tbl TABLE (
TblID varchar(15)
, Fld varchar(max)
)
INSERT INTO #Tbl SELECT '201801026543210', '<?xml version="1.0"?><MainTag xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" RecordID="201801026543210"><Field Name="TheFieldName"><FieldValue>Field Contents</FieldValue><ListTag><ListItem><Value>This List Value</Value><Source>source.txt</Source><ListType>text</ListType><ItemNumber>6912</ItemNumber><MoreData>some text here</MoreData><Address>address data</Address><Ranking>102</Ranking></ListItem><ListItem><Value>Another List Value</Value><Source>other.txt</Source><ListType>text</ListType><ItemNumber>7919</ItemNumber><MoreData>more text here</MoreData><Address>address data</Address><Ranking>41</Ranking></ListItem></ListTag></Field></MainTag>'
-- this shows that i am reading the main tag
SELECT t.r.value('#RecordID','varchar(15)') AS RecordID
, t.r.query('.') as fullvalue
FROM (
SELECT top 10 CONVERT(xml, Fld) AS Fld, TblID
FROM #Tbl
) AS s
CROSS APPLY Fld.nodes('/MainTag') AS t(r)
-- and this works to read the attribute from the first field
SELECT t.r.value('#RecordID', 'varchar(18)') AS RecordID
, f.r.value('#Name', 'varchar(100)') AS Field
FROM (
SELECT top 10 CONVERT(xml, Fld) AS Fld, TblID
FROM #Tbl
) AS s
CROSS APPLY Fld.nodes('/MainTag') AS t(r)
CROSS APPLY Fld.nodes('/MainTag/Field') AS f(r)
-- this does NOT work to read the second field
SELECT t.r.value('#RecordID','varchar(18)') AS RecordID
, f.r.value('.', 'varchar(100)') AS ValueF
, p.r.query('.') AS QueryP
, p.r.value('.', 'varchar(100)') AS ValueP
FROM (
SELECT top 10 CONVERT(xml, Fld) AS Fld, TblID
FROM #Tbl
) AS s
CROSS APPLY Fld.nodes('/MainTag') AS t(r)
CROSS APPLY Fld.nodes('/MainTag/Field') AS f(r)
CROSS APPLY Fld.nodes('/MainTag/FieldValue') AS p(r)
-- i honestly feel like this should be using nodes off of the parent nodes method
-- , but this is NOT working either
SELECT t.r.value('#RecordID','varchar(18)') AS RecordID
, p.r.query('.') AS FieldValue
FROM (
SELECT top 10 CONVERT(xml, Fld) AS Fld, TblID
FROM #Tbl
) AS s
CROSS APPLY s.Fld.nodes('/MainTag') AS t(r)
CROSS APPLY t.r.nodes('/MainTag/FieldValue') AS p(r)
I've seen some other questions/samples that grab just the first entry. I do not want that. I want a big blob of data. I want the unique fields to repeat for each "ListItem". To say that another way: I know this is akin to a one-to-many join where the fields in the "one" table will repeat for each row in the "many" table.
Technically, in my data there may be more than one "Field" as well.
There is only one FieldValue for that field.
There is one or more ListItems.
Try it with this query:
SELECT s.Fld.value('(/MainTag/#RecordID)[1]','bigint') AS RecordID
,A.f.value('#Name','nvarchar(max)') as FieldName
,A.f.value('(FieldValue/text())[1]','nvarchar(max)') as FieldValue
,B.li.value('(Value/text())[1]','nvarchar(max)') as ListValue
,B.li.value('(Source/text())[1]','nvarchar(max)') as ListSource
--and so on
FROM (
SELECT top 10 CONVERT(xml, Fld) AS Fld, TblID
FROM #Tbl
) AS s
CROSS APPLY Fld.nodes('/MainTag/Field') AS A(f)
CROSS APPLY A.f.nodes('ListTag/ListItem') AS B(li)
Hint
If there is any chance to change storage from VARCHAR to XML it's worth it...
I hate dealing with XML queries. I don't do it often enough to remember how to format everything, but I'm at my wits' end.
Let's say I have a table called Messages and a column inside it called Payload. Payload contains XML stored as varchar(max). The XML is formatted as such:
<NCOAPACP xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="uri://www.gen.da.hob/VC/Contract/QualityManagement/EMSM">
<VID>3656183</VID>
</NCOAPACP>
How do I query the table to retrieve a list of the values in the VID node?
Try this:
declare #w as xml = cast('
<NCOAPACP xmlns:i=''http://www.w3.org/2001/XMLSchema-instance''
xmlns="uri://www.gen.da.hob/VC/Contract/QualityManagement/EMSM">
<VID>3656183</VID>
</NCOAPACP>' as xml)
SELECT #w.value('declare namespace ns="uri://www.gen.da.hob/VC/Contract/QualityManagement/EMSM";
ns:NCOAPACP[1]/ns:VID[1]','varchar(10)') AS VID
If you have multiple VID node you can try this:
declare #w as xml = cast('
<NCOAPACP xmlns:i=''http://www.w3.org/2001/XMLSchema-instance''
xmlns="uri://www.gen.da.hob/VC/Contract/QualityManagement/EMSM">
<VID>3656183</VID>
<VID>454545</VID>
</NCOAPACP>' as xml)
SELECT VID.value('.','varchar(10)')
from #w.nodes(
'declare namespace ns="uri://www.gen.da.hob/VC/Contract/QualityManagement/EMSM";
ns:NCOAPACP[1]/ns:VID'
)
AS C(VID)
If XML data are in a table column then the solution is a little more tricky. Especially if data type is varchar(max).
declare #tbl table(id int,payload varchar(max))--sample table
--sample data
insert #tbl values
(1,'<NCOAPACP xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="uri://www.gen.da.hob/VC/Contract/QualityManagement/EMSM">
<VID>3656183</VID>
<VID>3656184</VID>
</NCOAPACP>'),
(2,'<NCOAPACP xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="uri://www.gen.da.hob/VC/Contract/QualityManagement/EMSM">
<VID>123456</VID>
<VID>987654</VID>
</NCOAPACP>')
--it is convenient to define namespaces before the query
;with xmlnamespaces('uri://www.gen.da.hob/VC/Contract/QualityManagement/EMSM' as x)--we will use this x:
select id, t.vid.value('.[1]','varchar(20)') vid
-- . means self. [1] ensure single value
from
--convert varchar(max) to xml first
(select id,cast(payload as xml) payload from #tbl) tt
cross apply
--convert xml to a tabular form
tt.payload.nodes('//x:VID') t(vid)
Results:
id vid
1 3656183
1 3656184
2 123456
2 987654
U can try this
DECLARE #Messages TABLE(ID INT, Payload NVARCHAR(MAX))
INSERT INTO #Messages(ID, Payload) VALUES(1, '<NCOAPACP xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="uri://www.gen.da.hob/VC/Contract/QualityManagement/EMSM"> <VID>3656183</VID> </NCOAPACP>')
INSERT INTO #Messages(ID, Payload) VALUES(2, '<NCOAPACP xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="uri://www.gen.da.hob/VC/Contract/QualityManagement/EMSM"> <VID>3656183</VID> <VID>3656184</VID> </NCOAPACP>')
;WITH CTE(ID, Payload) AS
(
SELECT ID, CAST(Payload AS XML)
FROM #Messages
)
SELECT
ID,
x.items.value('.', 'NVARCHAR(10)')
FROM CTE
CROSS APPLY Payload.nodes('declare namespace xx="uri://www.gen.da.hob/VC/Contract/QualityManagement/EMSM"; xx:NCOAPACP/xx:VID') as x(items)
The most simple way (CTE test created just to get output, your xml value converted to varchar(max) as in your table, then SELECT *.value):
;WITH test AS (
SELECT CAST(
'<NCOAPACP xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="uri://www.gen.da.hob/VC/Contract/QualityManagement/EMSM">
<VID>3656183</VID>
</NCOAPACP>' as varchar(max)) as Payload
)
SELECT CAST(Payload as xml).value('(/*/*)[1]', 'int')
FROM test
Output:
3656183
Given the following from an XML field in a table:
<View>
<Criminal xmlns="http://tempuri.org/crimes.xsd">
<Person>
<PersonID>1234</PersonID>
<LastName>SMITH</LastName>
<FirstName>KEVIN</FirstName>
<Cases>
<PersonID>1234</PersonID>
<CaseNumber>12CASE34</CaseNumber>
</Cases>
</Person>
</Criminal>
</View>
How would I pull the Person/PersonID, LastName, Firstname info? Same goes for the CaseNumber.
My next issue is similar to above but lets add a second namespace:
<MessageContent xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Content>Content in here!!</Content>
<Type>Empty</Type>
</MessageContent>
Notice I have 2 namespaces in there AND they also have ":xsi" and ":xsd" in there too. I think those are referred to as schemas.
Try this:
DECLARE #table TABLE (ID INT NOT NULL, XmlContent XML)
INSERT INTO #table VALUES(1, '<View>
<Criminal xmlns="http://tempuri.org/crimes.xsd">
<Person>
<PersonID>1234</PersonID>
<LastName>SMITH</LastName>
<FirstName>KEVIN</FirstName>
<Cases>
<PersonID>1234</PersonID>
<CaseNumber>12CASE34</CaseNumber>
</Cases>
</Person>
</Criminal>
</View>')
;WITH XMLNAMESPACES('http://tempuri.org/crimes.xsd' AS ns)
SELECT
PersonID = XmlContent.value('(/View/ns:Criminal/ns:Person/ns:PersonID)[1]', 'int'),
FirstName = XmlContent.value('(/View/ns:Criminal/ns:Person/ns:FirstName)[1]', 'varchar(50)'),
LastName = XmlContent.value('(/View/ns:Criminal/ns:Person/ns:LastName)[1]', 'varchar(50)')
FROM #table
WHERE ID = 1
Returns an output of:
And for your second part of the question: yes, you have two namespaces defined - but they're not being used at all - so you can basically just ignore them:
INSERT INTO #table VALUES(2, '<MessageContent xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Content>Content in here!!</Content>
<Type>Empty</Type>
</MessageContent>')
SELECT
Content = XmlContent.value('(/MessageContent/Content)[1]', 'varchar(50)'),
Type = XmlContent.value('(/MessageContent/Type)[1]', 'varchar(50)')
FROM #table
WHERE ID = 2
Returns: