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
Related
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.)
This is my first time here. I need to get my XML formatted in the following format:
<Summary Year="2018" QtrNum="1" WeekNum="4" Date="1/22/2018" Code="101">
Once I get it to this format, then I need this XML generated for each row of my table. Here is a screenshot of the expected result set.
Expected Result Set
Thank you in advance.
Although your question is rather unclear, my magig crystal ball tells me, that you might be looking for something like this:
DECLARE #someTable TABLE(SomeDate DATE, SomeQrt INT, SomeWeek INT, SomeCode INT);
INSERT INTO #someTable VALUES('20180122',1,4,101);
DECLARE #SomeRelatedTable TABLE(SomeText VARCHAR(100),SomeCode INT);
INSERT INTO #SomeRelatedTable VALUES('Row 1',101)
,('Row 2',101)
,('Other code',999);
SELECT YEAR(t.SomeDate) AS [#Year]
,t.SomeQrt AS [#QrtNum]
,t.SomeWeek AS [#WeekNum]
,t.SomeDate AS [#Date]
,t.SomeCode AS [#Code]
,(
SELECT SomeText AS [#InnerText]
FROM #SomeRelatedTable rt
WHERE rt.SomeCode=t.SomeCode
FOR XML PATH('InnerNode'),TYPE
)
FROM #someTable t
FOR XML PATH('Summary');
The result
<Summary Year="2018" QrtNum="1" WeekNum="4" Date="2018-01-22" Code="101">
<InnerNode InnerText="Row 1" />
<InnerNode InnerText="Row 2" />
</Summary>
Some Explanation
FOR XML PATH allows full control over the XML generation through the column's alias and PATH / ROOT extension.
The related (inner) nodes are created with a correlated sub-query.
I was able to solve my problem. Look at the scenario below.
drop table #test
create table #test(id int identity(1,1),locationcode varchar(6),businessdate date, quarternum int, weeknum int)
insert into #test(locationcode,businessdate, quarternum, weeknum)
values('000001',GETDATE(),2,2),
('000001',GETDATE(),2,2),
('000002',GETDATE(),2,2),
('000003',GETDATE(),2,2),
('000004',GETDATE(),2,2),
('000005',GETDATE(),2,2),
('000006',GETDATE(),2,2),
('000007',GETDATE(),2,2)
SELECT
(SELECT YEAR(b.businessdate) AS '#Year',b.QuarterNum AS '#QtrNum',b.WeekNum AS '#WeekNum',FORMAT(b.BusinessDate,'MM/DD/YYYY') AS '#Date'
,b.LocationCode AS '#Code'
FROM #test c where b.id = c.id FOR XML PATH('DailySummary'),TYPE) AS Row
FROM #test b
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)
;
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...
I have a table where a column contains XML data. Now i want to retrieve those xml data with restriction of nodes. Kindly see the following example for more explanation on my scenario,
declare #table table (id int, xmlfield xml) insert into #table select 1,'<Root xmlns="">
<Sample>
<Issue>
<Level>one</Level>
<Descp>First Example</Descp>
</Issue>
<Issue>
<Level>two</Level>
<Descp>Second Example</Descp>
</Issue>
</Sample> </Root>'
select * from #table
Now i need the following result set
Id XMLfield
1 first example
ie, for the selected level,i need the decription for it. More clearly, the node should be restricted for <level>one</level>
(need: What is the description for level one ?)
thanks in advance
Have a look at the xml Data Type Methods
select id,
xmlfield.value('(//Issue[Level = "one"]/Descp/text())[1]', 'varchar(100)') as XMLField
from #table
The XQuery you're looking for is
//Issue[Level = "one"]/Descp/data()