SQL server 2012 parsing XML with namespaces - sql

I have a table containing rows of xml in the following format:
<msit:message xmlns:wsa="http://URL1" xmlns:msit="http://URL2" xmlns:env="http://URL3">
<env:Body>
<ns0:parent xmlns:ns0="http://URL4">
<ns0:child>123456789</ns0:child>
...
</ns0:parent>
</env:Body>
</msit:message>`
in a table name mytable, column name data.
I have written the following query:
;with xmlnamespaces('http://URL2' as msit,
'http://URL3' as env,
'http://URL1' as wsa,
'http://URL4' as ns0)
select
t2.field.value('child[1]','varchar(20)') as ban
from mytable
cross apply data.nodes('/message/Body/parent') t2(field)
it returns empty set, when I need to return 123456789
What am I doing wrong ?
Thank you

you may need to include the prefixes in the xpath expressions:
declare #mytable table (data xml)
insert into #mytable values
('<msit:message xmlns:wsa="http://URL1" xmlns:msit="http://URL2" xmlns:env="http://URL3">
<env:Body>
<ns0:parent xmlns:ns0="http://URL4">
<ns0:child>123456789</ns0:child>
</ns0:parent>
</env:Body>
</msit:message>')
;with xmlnamespaces('http://URL2' as msit,
'http://URL3' as env,
'http://URL1' as wsa,
'http://URL4' as ns0)
select
t2.field.value('ns0:child[1]','varchar(20)') as ban
from #mytable
cross apply data.nodes('/msit:message/env:Body/ns0:parent') t2(field)

The whole point of namespaces is to differentiate between elements that were brought together from multiple documents.
It is similar to the way we qualify columns with tables' names or aliases, e.g. t1.x Vs. t2.x.
So when you refer to an element you should qualify it with the right namespace.
You might also want to use outer apply instead of cross apply in case there's a missing element.
create table mytable (x xml);
insert into mytable (x) values
(
'
<msit:message xmlns:wsa="http://URL1" xmlns:msit="http://URL2" xmlns:env="http://URL3">
<env:Body>
<ns0:parent xmlns:ns0="http://URL4">
<ns0:child>123456789</ns0:child>
</ns0:parent>
</env:Body>
</msit:message>
'
)
;
;
with xmlnamespaces
(
'http://URL2' as msit
,'http://URL3' as env
,'http://URL1' as wsa
,'http://URL4' as ns0
)
select t2.field.value('ns0:child[1]','varchar(20)') as ban
from mytable
outer apply x.nodes('/msit:message/env:Body/ns0:parent') t2(field)
;

Related

SQL Parse xml column data

I have very little experience with SQL Server. Here is what I am trying to accomplish:
I have a table with many rows. Every row contains a column named "Config". The data stored in the Config column is of the xml type. The xml structure looks like this:
<root>
<path1>
<path2>
<path3>true</path3>
</path2>
</path1>
</root>
I am trying to go through every row in the table and find the percentage of true to false values of <path3>.
I looked at some SQL documentation and tried to use the value() function I am having difficulties extracting the XML data from the column:
select Config.value('(/root/path1/path2/path3)[1]','nvarchar(max)') from [MyDB].[dbo].[MyTable]
Here is the result of my query:
I would like to query and extract data from the XML "Config" column of my table and aggregate that data into columns.
You need to specify namespaces in the query when xml is built with namespaces. For example
CREATE TABLE tbl (Config xml);
INSERT INTO tbl (Config)
VALUES ('<root xmlns="abc">
<path1>
<path2>
<path3>true</path3>
</path2>
</path1>
</root>') ;
Then
with xmlnamespaces (DEFAULT 'abc')
select Config.value('(/root/path1/path2/path3)[1]','nvarchar(max)') path3Txt
from tbl;
or explicit specification
with xmlnamespaces ('abc' as x)
select Config.value('(/x:root/x:path1/x:path2/x:path3)[1]','nvarchar(max)') path3Txt
from tbl;
You would need to use CROSS APPLY. Check it out.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (ID INT IDENTITY(1,1) PRIMARY KEY, [Config] XML);
INSERT INTO #tbl
VALUES (N'<root>
<path1>
<path2>
<path3>true</path3>
</path2>
</path1>
</root>')
, (N'<root>
<path1>
<path2>
<path3>false</path3>
</path2>
</path1>
</root>');
-- DDL and sample data population, end
;WITH rs AS
(
SELECT ID
, col.value('(./text())[1]','VARCHAR(20)') AS Result
FROM #tbl tbl
CROSS APPLY tbl.[Config].nodes('/root/path1/path2/path3') AS tab(col)
)
SELECT * FROM rs;

How to Select SQL for XML with fixed head line

I have a simple SQL table with Values "Alpha", "Bravo" and "Charlie".
I Need to get result as displayed. The data within transaction: type, date, etc. are static and should be part of the select Statement.
I think this can be done using
SELECT ... for XML
But don't know how?
CREATE TABLE [dbo].[SampleTable]([ID_extern] [varchar](50) NULL)
INSERT INTO SampleTable VALUES ('Alpha')
INSERT INTO SampleTable VALUES ('Bravo')
INSERT INTO SampleTable VALUES ('Charlie')
INSERT INTO SampleTable VALUES ('Delta')
Here is a little kick-start for you
Example
Select [transaction/#type]='import_serial_number'
,[transaction/#date]='123459'
,[transaction/#vaultname]='Type in the name of the vault here'
,[transaction/serial_number/#name] = 'SampleAppendSerialNo'
,[transaction/serial_number/#mode] = 'append'
,[transaction/serial_number] = (
Select [serno_item/#item_counter] = row_number() over (order by ID_extern)
,[serno_item/#serno_item] = ID_extern
From SampleTable
For XML Path (''),TYPE
)
For XML Path('transactions'),Root('xml')
Returns
<xml>
<transactions>
<transaction type="import_serial_number" date="123459" vaultname="Type in the name of the vault here">
<serial_number name="SampleAppendSerialNo" mode="append">
<serno_item item_counter="1" serno_item="Alpha" />
<serno_item item_counter="2" serno_item="Bravo" />
<serno_item item_counter="3" serno_item="Charlie" />
<serno_item item_counter="4" serno_item="Delta" />
</serial_number>
</transaction>
</transactions>
</xml>
Where a column value goes in the XML is determined by a path in an alias. Use # for attributes. To get the ordinals you can use row_number().
Something like
SELECT row_number() OVER (ORDER BY id_extern) "serno_item/#item_counter",
id_extern "serno_item/#item_value"
FROM simple
FOR XML PATH ('');
gives you the inner part of the XML. You can either try nested queries or use string concatenation (concat()) to prepend/append the outer parts like in the following.
SELECT convert(xml, concat('<serial_number type="">',
(SELECT row_number() OVER (ORDER BY id_extern) "serno_item/#item_counter",
id_extern "serno_item/#item_value"
FROM simple
FOR XML PATH ('')),
'</serial_number>'));
(I'm not going to type all that stuff off of your screen shot, so this is just exemplary.)

How to manipulate comma-separated list in SQL Server

I have a list of values such as
1,2,3,4...
that will be passed into my SQL query.
I need to have these values stored in a table variable. So essentially I need something like this:
declare #t (num int)
insert into #t values (1),(2),(3),(4)...
Is it possible to do that formatting in SQL Server? (turning 1,2,3,4... into (1),(2),(3),(4)...
Note: I can not change what those values look like before they get to my SQL script; I'm stuck with that list. also it may not always be 4 values; it could 1 or more.
Edit to show what values look like: under normal circumstances, this is how it would work:
select t.pk
from a_table t
where t.pk in (#place_holder#)
#placeholder# is just a literal place holder. when some one would run the report, #placeholder# is replaced with the literal values from the filter of that report:
select t.pk
from a_table t
where t.pk in (1,2,3,4) -- or whatever the user selects
t.pk is an int
note: doing
declare #t as table (
num int
)
insert into #t values (#Placeholder#)
does not work.
Your description is a bit ridicuolus, but you might give this a try:
Whatever you mean with this
I see what your trying to say; but if I type out '#placeholder#' in the script, I'll end up with '1','2','3','4' and not '1,2,3,4'
I assume this is a string with numbers, each number between single qoutes, separated with a comma:
DECLARE #passedIn VARCHAR(100)='''1'',''2'',''3'',''4'',''5'',''6'',''7''';
SELECT #passedIn; -->: '1','2','3','4','5','6','7'
Now the variable #passedIn holds exactly what you are talking about
I'll use a dynamic SQL-Statement to insert this in a temp-table (declared table variable would not work here...)
CREATE TABLE #tmpTable(ID INT);
DECLARE #cmd VARCHAR(MAX)=
'INSERT INTO #tmpTable(ID) VALUES (' + REPLACE(SUBSTRING(#passedIn,2,LEN(#passedIn)-2),''',''','),(') + ');';
EXEC (#cmd);
SELECT * FROM #tmpTable;
GO
DROP TABLE #tmpTable;
UPDATE 1: no dynamic SQL necessary, all ad-hoc...
You can get the list of numbers as derived table in a CTE easily.
This can be used in a following statement like WHERE SomeID IN(SELECT ID FROM MyIDs) (similar to this: dynamic IN section )
WITH MyIDs(ID) AS
(
SELECT A.B.value('.','int') AS ID
FROM
(
SELECT CAST('<x>' + REPLACE(SUBSTRING(#passedIn,2,LEN(#passedIn)-2),''',''','</x><x>') + '</x>' AS XML) AS AsXml
) as tbl
CROSS APPLY tbl.AsXml.nodes('/x') AS A(B)
)
SELECT * FROM MyIDs
UPDATE 2:
And to answer your question exactly:
With this following the CTE
insert into #t(num)
SELECT ID FROM MyIDs
... you would actually get your declared table variable filled - if you need it later...

How to read column values from xmlstring using forxml clause

In our database table, columns are stored as an xml string, which is shown below.
<DocumentElement>
<PartInfo>
<ID>0</ID>
<PartNo>0</PartNo>
<SerialNo>1</SerialNo>
<Parameter>0</Parameter>
<InstalledDate>2013-01-15T00:00:00+05:30</InstalledDate>
<InstalledTill>2013-01-25T00:00:00+05:30</InstalledTill>
</PartInfo>
</DocumentElement>
I want to get column values of this string. For example I have to get the value 2013-01-15T00:00:00+05 of Installed Date column. How can I obtain this using forxml clause?
Assuming SQL-Server you can use something like this:
DECLARE #T TABLE (X XML);
INSERT #T VALUES ('<DocumentElement>
<PartInfo>
<ID>0</ID>
<PartNo>0</PartNo>
<SerialNo>1</SerialNo>
<Parameter>0</Parameter>
<InstalledDate>2013-01-15T00:00:00+05:30</InstalledDate>
<InstalledTill>2013-01-25T00:00:00+05:30</InstalledTill>
</PartInfo>
</DocumentElement>');
SELECT InstalledDate = X.value('/DocumentElement[1]/PartInfo[1]/InstalledDate[1]', 'DATETIME')
FROM #T;
If you could have multiple PartInfo nodes within DocumentElement then you'll need to use CROSS APPLY .. nodes to get all the InstalledDates
SELECT InstalledDate = PartInfo.value('InstalledDate[1]', 'DATETIME')
FROM #T
CROSS APPLY X.nodes('/DocumentElement/PartInfo') p (PartInfo);

Select SQL Query to get xml node values from ntext column?

I want to get one xml node value from NTEXT column which contains xml based on where clause quering on another xml node value.
RDBMS Type: Microsoft SQL Server T-SQL
Here: I want to get Code node value based on StoreId where clause value. How do I get it?
Input: 100
Output:ABCDE
For example:
<root>
<StoreProfile>
<General>
<StoreId>100</StoreId>
<Code>ABCDE</Code>
</General>
</StoreProfile>
</root>
If you are using SQL Server 2005 or 2008 you can use XQuery like so:
For more on XQuery see XQuery Language Reference
DECLARE #storeId INT
SET #storeId = 100
CREATE TABLE #TestTable
(
xmlColumn NTEXT
)
INSERT INTO #TestTable (xmlColumn) Values('<root><StoreProfile><General><StoreId>100</StoreId><Code>ABCDE</Code></General></StoreProfile></root>')
INSERT INTO #TestTable (xmlColumn) Values('<root><StoreProfile><General><StoreId>200</StoreId><Code>FGHIJ</Code></General></StoreProfile></root>')
SELECT
StoreProfile.value('Code[1]', 'nvarchar(10)') as Code
FROM #TestTable
CROSS APPLY (SELECT CAST(xmlColumn AS XML)) AS A(B)
CROSS APPLY A.B.nodes('//root/StoreProfile/General[StoreId = sql:variable("#storeId")]') AS StoreProfiles(StoreProfile)
DROP TABLE #TestTable