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.)
Related
SQL Server 2016Field1 (XML(.), null)
Example data:
<ProcessPositionOpening xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.hr-xml.org/3">
<ApplicationArea xmlns="http://www.openapplications.org/oagis/9">
...
</ApplicationArea>
<DataArea>
<Process xmlns="http://www.openapplications.org/oagis/9">
...
</Process>
<PositionOpening>
<DocumentID />
<PositionRequester>
...
</PositionRequester>
<PositionProfile>
...
<PositionFormattedDescription>
<ID>...</ID>
<Content />
</PositionFormattedDescription>
...
</PositionProfile>
</PositionOpening>
</DataArea>
</ProcessPositionOpening>
In this table, Field1 is setup as XML and I have been trying to find all records where the node Content may not be NULL. However, I've tried different examples of query and exist, but can't seem to get the query right. Here is the current one I'm trying, but it's not returning what I expected:
SELECT *
FROM Table1
WHERE Field1.value('(/ProcessPositionOpening/DataArea/PositionOpening/PositionProfile/PositionFormattedDescription/Content)[1]','varchar(50)') <> ''
AND Message = 'Request Received';
I've also tried:
SELECT *
FROM Table1
WHERE Message = 'Request Received'
AND Field1.exist('/ProcessPositionOpening') = 1;
I tried the query above just to see if the basic setup of the query would work. It returns no records. If I remove the AND part of the query, I have over 19,000 rows in this table that are all in this format. If I change the 1 to a 0 in the AND part of the query, it returns records, but I feel that's wrong as I thought (based on pages explaining the exist function, 0 means NOT exist). So, I'm confused.
I want to find all records where the Content node has a value, and likewise find all where Content does NOT have a value. Any help is truly appreciated. Thanks!
I hope the following provides some templates how to solve this:
DECLARE #mockup TABLE(Descr VARCHAR(100),theXML XML);
INSERT INTO #mockup VALUES
('<content> exists with value','<root><content>blah</content></root>' )
,('<content> exists without value','<root><content></content></root>' ) --semantically the same as self-closed...
,('<content> exists self-closed','<root><content/></root>' )
,('<content> does not exist','<root></root>' );
--Find a Content with value
SELECT * FROM #mockup
WHERE theXML.exist('/root/content/text()')=1;
--Find a Content with or without value
SELECT * FROM #mockup
WHERE theXML.exist('/root/content')=1;
--Find a Content without value
SELECT * FROM #mockup
WHERE theXML.exist('/root/content[empty(text())]')=1;
--Find rows with no content
SELECT * FROM #mockup
WHERE theXML.exist('/root/content')=0;
The xml.exist function is not intuitive to me and I've
failed quite often using it. Even though the documentation clearly states how to use it: https://learn.microsoft.com/en-us/sql/t-sql/xml/exist-method-xml-data-type?view=sql-server-ver15
But to read the documentation beforehand is not intuitive to me either :)
DECLARE #mockup TABLE(Descr VARCHAR(100),theXML XML);
INSERT INTO #mockup VALUES
('<content> exists with value','<root><content>blah</content></root>' )
,('<content> exists without value','<root><content></content></root>' ) --semantically the same as self-closed...
,('<content> exists self-closed','<root><content/></root>' )
,('<content> does not exist','<root></root>' )
,('XML is null', NULL );
SELECT *, theXML.exist('/root/content') FROM #mockup
Gives
<content> exists with value <root><content>blah</content></root> 1
<content> exists without value <root><content /></root> 1
<content> exists self-closed <root><content /></root> 1
<content> does not exist <root /> 0
XML is null NULL NULL
So I always wrap the condition in ISNULL(..,..)
SELECT *, ISNULL(theXML.exist('/root/content'),0) FROM #mockup
I have a table in SQL Server and I am putting the values of the result set from the table into XML like this:
select
lAccountID,
sName,
sAge
from
AccountDVDetails
where
laccountID = 10
for xml raw ('Values'), ROOT ('Accounts'), ELEMENTS
This query gives me XML like
<Accounts>
<Values>
<lAccountID>10</lAccountID>
<sName>A</sName>
<sAge>21</sAge>
</Values>
<Values>
<lAccountID>10</lAccountID>
<sName>B</sName>
<sAge>22</sAge>
</Values>
<Values>
<lAccountID>10</lAccountID>
<sName>C</sName>
<sAge>23</sAge>
</Values>
</Accounts>
Now I want to store this XML with lAccountId in a temporary table #tmpAccount like
lAccountId XMLValue
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
10 <Accounts><Values><lAccountID>10</lAccountID><sName>A</sName><sAge>21</sAge></Values><Values><lAccountID>10</lAccountID><sName>B</sName><sAge>22</sAge></Values><Values><lAccountID>10</lAccountID><sName>C</sName><sAge>23</sAge></Values></Accounts>
How can I achieve this? I have tried putting the XML into string variable but the XML gets truncated to a certain limit.
Any help will be deeply appreciated. Thanks.
Try this for a nested, hierarchical XML for many Accounts
First I declare some table variables to mock-up a test scenario. I assume, that there is a parent table with accounts:
DECLARE #dummyAccount TABLE(lAccountID INT IDENTITY,SomeData VARCHAR(100));
DECLARE #dummyAccountDetail TABLE(lAccountDetail INT IDENTITY,lAccountID INT,sName VARCHAR(100),sAge INT);--do not store the age as int but the DoB!!!
INSERT INTO #dummyAccount VALUES('Account 1'),('Account 2');
INSERT INTO #dummyAccountDetail VALUES(1,'Jane',20),(1,'John',30)
,(2,'Tim',40),(2,'Tom',50);
The query will list the accounts as first level (I added some descibing attributes just to show the principles, but you can easily let them away. The sub-select will create an XML for each account separately.
SELECT A.lAccountID
,A.SomeData
,(
SELECT D.lAccountID
,D.sName
,D.sAge
FROM #dummyAccountDetail AS D
WHERE D.lAccountID=A.lAccountID
FOR XML PATH('Values'),ROOT('Accounts'),TYPE
)
FROM #dummyAccount AS A
You can easily create the table #tmpAccount just with
SELECT ...
INTO #tmpAccount
FROM ...
...or, if it exists already, just use
INSERT INTO #tmpAccount
SELECT ...
Check This.
select distinct
(
select
lAccountID,
sName,
sAge
from
AccountDVDetails
where
laccountID = 10
for xml raw ('Values'), ROOT ('Accounts'), ELEMENTS
)as XMLValue
,lAccountID into #tmpAccount from AccountDVDetails
where
laccountID = 10
select * from #tmpAccount
table tbl_event_log
columnName DataType
id Int
event XML
userinfo XML
and data should be in event is
<Event><Player readName="9.Make You Talk!" demoName="Video Game" **portal="FB"** totalDuration="0:07/0:07(100%)" /></Event>
and i want to write query to get data from portal="FB"
Use nodes() method to split your rows and then get values:
Check this solution and hope it helps you:
Declare #mytable table (id int,event xml,userinfo varchar(200))
Insert into #mytable
select 1, '<Event><Player readName="9.Make You Talk!" demoName="Video Game" portal="FB" totalDuration="0:07/0:07(100%)" /></Event>','Test'
Union
select 2, '<Event><Player readName="9.Make You Talk!" demoName="Video Game" portal="TW" totalDuration="0:07/0:07(100%)" /></Event>','Test'
select
s.id,
e.p.value('#portal', 'varchar(max)') as portal
from #mytable as s
outer apply s.event.nodes('Event/Player') as e(p)
select * from (
select
s.id,
e.p.value('#portal', 'varchar(max)') as portal
from #mytable as s
outer apply s.event.nodes('Event/Player') as e(p)
) Test
where Test.portal = 'FB'
Attn: Replace #mytable with your table name.
Note: Event column in your table must be XML datatype.
To get the data into specific fields you could extract them directly from the xml.
For example:
select id, userinfo,
event.value('/Event[1]/Player[#portal="FB"][1]/#readName','varchar(max)') as readNameFB,
event.value('/Event[1]/Player[#portal="FB"][1]/#demoName','varchar(max)') as demoNameFB,
event.value('/Event[1]/Player[#portal="FB"][1]/#totalDuration','varchar(30)') as totalDurationFB
from tbl_event_log;
[#portal="FB"] : only get the Player tags where the portal="FB"
[1] : use the first one
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)
;
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);