Accessing child nodes in SQLXML columns with XQuery - xquery

I have a database which has a table with an XML column. The XML data has a bunch of child nodes which look something like this:
<test>
<result id="1234">
<data elementname="Message">some error message</data>
<data elementname="Cat">Cat01</data>
<data elementname="Type">WARNING</data>
</result>
<result id="5678">
<data elementname="Message">some error message</data>
<data elementname="Cat">Cat01</data>
<data elementname="Type">WARNING</data>
</result>
</test>
The Cat element can have a number of different values. I'm trying to create reports on this data, so one thing I'd like to do is get a list of all the categories througout our data. This is my query:
Select Id, XmlData.query('/test/result/data[#elementname = ''Cat''] ') AS Message
From Table
WHERE XmlData.exist('/test/result/data[#elementname = ''Cat'']') = 1
ORDER BY FriendlyName
This correctly gets all the rows in my table with this type of categorization (there'll be other results in the same table without that element), but the categories are all combined into one column for each table record:
Id1, <data elementname="Cat">Cat01</data><data elementname="Cat">Cat01</data>
Id2, <data elementname="Cat">Cat01</data><data elementname="Cat">Cat01</data>
I'm including the Id column so it's easy to see where the data is coming from, the main problem is that I can only get it to concatenate the values for each row - I need each of those data elements to have its own row, then maybe do a Select Distinct on the result.
Is there a way I can do that?
Thanks

Always the Google after you post your question....
Think I found the answer here: http://blogs.msdn.com/b/simonince/archive/2009/04/24/flattening-xml-data-in-sql-server.aspx
SELECT DISTINCT cref.value('(text())[1]', 'varchar(50)') as Cat
FROM
SGIS CROSS APPLY
Data.nodes('/test/result') AS Results(rref) CROSS APPLY
rref.nodes('data[#elementname = ''Cat'']') AS Categories(cref)
Seems the key is the Cross Apply keywords

Related

MarkLogic: How to manage additional query in constraint

MarkLogic 9.0.8
On UI, apart from search text, few filters are applied including publish date year.
As we can't control how end user will write query in multiline textbox.
So at end query look like something
AU:Manish AND PY:>=2001 AND CT:Control AND UT:uncontrol AND PY:<=2010
(AU:Manish AND PY:>=2001) OR (CT:Control AND UT:uncontrol AND PY:<=2010)
AU:Manish AND CT:Control AND UT:uncontrol AND PY:>=2001 AND PY:<=2010
Till now we managed with having year range at the end in query and was working with following code
Qyery: AU:Manish AND CT:Control AND UT:uncontrol OR PY:>=2001 AND PY:<=2010
<additional-query>
{
cts:and-query((
cts:path-range-query("contg/sortdate/yr", ">=",xs:int($startYear)),
cts:path-range-query("contg/sortdate/yr", "<=",xs:int($endYear))))
}
</additional-query>
But now as user can put year range anywhere in the query text, its not working as expected
So can we write condition in constraint directly and how to put additional query inside ?
<constraint name="Year">
<range type="xs:int" facet="false">
<path-index>article/date/year</path-index>
</range>
</constraint>
Expected Behavior
If user pass year range then it should return documents within given range
if not then it will not apply year range

get distinct dates of timestamp elements in Basex with xquery

I have the following xml format
<root>
<node1>
<timestamp>2016-05-16T00:57:30.889</timsetamp>
</node1>
<node1>
<timestamp>2016-05-18T00:57:30.889</timsetamp>
</node1>
....
</root>
how can I get the distinct dates(for example 2016-05-16,2016-05-18)?
If you just need the dates, you can use XPath, casts and fn:distinct-values():
distinct-values(//timestamp/xs:date(xs:dateTime(.)))

Invoking database using xquery giving duplicate values

I am using a XQuery to query database in an OSB project. Consider the
following table:
userId Name Category
------ ------- --------
1 Dheepan Student
2 Raju Student
and the XQuery
let $userName:=fn-bea:execute-sql(
$dataSourceJndiName,
xs:string("NAME"),
xs:string("select NAME from USER where CATEGORY= 'Student'")
)/*:NAME[1]
return <root> {data($userName)} </root>
For this query I am getting the result as <root>Dheepan Raju</root>. But I
need to return only one row even the query returns more than one row like the
following <root>Dheepan</root>. I have used predicate [1] in the query but
no clue why it concatenates the values and returning. Can anybody tell me how
to return only the first row when more than one row is returned.
You need to use proper paranthesis:
let $userName:=(fn-bea:execute-sql(
$dataSourceJndiName,
xs:string("NAME"),
xs:string("select NAME from USER where CATEGORY= 'Student'")
)/*:NAME)[1]
return <root> {data($userName)} </root>

How to create XML file by SQL query?

I have a Select query which selects the top 10 values from table
Create proc [dbo].[SP_Select_Top10]
as
select DISTINCT top (10)
Score, FBid
from
FB_Player
ORDER BY
Score DESC
What I need is to have the results of this query in xml file as.
<player>
<name> </name>
<score> </score>
</player>
I use ASP.NET to create this file how can I do that ?
Create your stored procedure like this - use the FOR XML PATH(), ROOT() syntax to have SQL Server generate a proper XML for you:
CREATE PROCEDURE dbo.procGetPlayerScore
AS BEGIN
SELECT DISTINCT TOP (10)
ID AS '#ID', -- creates an attribute on the <Player> node
Name, -- gets output as element inside <Player>
Score -- gets output as element inside <Player>
FROM
dbo.FB_Players
ORDER BY
Score DESC
FOR XML PATH('Player'), ROOT('AllPlayers')
END
In your C# code, you need something like this - connect to the database, execute the stored procedure, get back the single row, single column of that stored procedure (the XML produced):
// set up SQL Server connection and command to execute the stored procedure
using(SqlConnection conn = new SqlConnection("server=.;database=test;Integrated Security=SSPI"))
using (SqlCommand cmdGetPlayers = new SqlCommand("dbo.procGetPlayerScore", conn))
{
// define that it's a stored procedure
cmdGetPlayers.CommandType = CommandType.StoredProcedure;
// open connection, execute procedure, get resulting XML, close connection
conn.Open();
string playersXml = cmdGetPlayers.ExecuteScalar().ToString();
conn.Close();
}
As result, you'll get a XML something like this:
<AllPlayers>
<Player ID="4">
<Name>Player 4</Name>
<Score>72.1500</Score>
</Player>
<Player ID="1">
<Name>Player 1</Name>
<Score>50.5000</Score>
</Player>
......
</AllPlayers>
I would suggest looking at the native XML options of SQL Server
Link here
Also please note
<player>
<playername="" score="" />
</player>
is not valid xml, it would have to something like
<player name="" score="" />
or
<player>
<name></name>
<score></score>
</player>
depending on whether you want to be element-centric or attribute-centric, but all of these can be specified in the SQL server XML output options. Then you could just get the ASP.NET side to save the resulting query as a file.

Query to output content of AllXml column of ELMAH_Error sql server table

I am having trouble writing query so that I can query the content of AllXml column inside Elmah_Error table.
How can I list out all the item nodes as an output of the query.
How could I write query to only list for certain item nodes?
I would like to get follow resultset:
item value
===== =====
ALL_HTTP HTTP_CONNECTION:xxxx
ALL_RAW Connection: xxxxx
I would also like to be able to filter the query by ErrorID
Content of AllXml column may look like this.
<error
application="/"
message="hello world"
source="TestWebElmah"
detail="xxxxx">
<serverVariables>
<item
name="ALL_HTTP">
<value
string="HTTP_CONNECTION:xxxx" />
</item>
<item
name="ALL_RAW">
<value
string="Connection: xxxxx" />
</item>
</serverVariables>
</error>
Remote Addr nodes
select T.N.value('(value/#string)[1]', 'varchar(30)') as REMOTE_ADDR
from
(select cast(AllXml as xml) as AllXml from ELMAH_Error) e
cross apply AllXml.nodes('//item[#name="REMOTE_ADDR"]') as T(N)
HTTP User Agents which contain mozilla
select T.N.value('(value/#string)[1]', 'varchar(30)') as HTTP_USER_AGENT
from
(select cast(AllXml as xml) as AllXml from ELMAH_Error) e
cross apply AllXml.nodes('//item[#name="HTTP_USER_AGENT"]') as T(N)
where T.N.value('(value/#string)[1]', 'varchar(30)') like '%mozilla%'
Elmah table stores the AllXml column as nvarchar so it needs to be casted to xml
all tags + values, by error id
select T.N.value('#name', 'varchar(30)') as Name,
T.N.value('(value/#string)[1]', 'varchar(30)') as Value
from
(select cast(AllXml as xml) as AllXml from ELMAH_Error where ErrorId = 'DC82172B-F2C0-48CE-8621-A60B702ECF93') e
cross apply AllXml.nodes('/error/serverVariables/item') as T(N)
Before voting down this answer, because uses most of the part of Mikael Eriksson's answer, I let you know I'll happily accept the downvotes only for this reason, since is mainly true
This query will give you all item nodes
select T.N.value('#name', 'varchar(30)') as Name,
T.N.value('(value/#string)[1]', 'varchar(30)') as Value
from Elmah_Error
cross apply AllXml.nodes('/error/serverVariables/item') as T(N)
If you want to filter on any of the values you can put that in a sub-query apply a regular where clause.
select Name,
Value
from
(
select T.N.value('#name', 'varchar(30)') as Name,
T.N.value('(value/#string)[1]', 'varchar(30)') as Value
from Elmah_Error
cross apply AllXml.nodes('/error/serverVariables/item') as T(N)
) T
where Name = 'ALL_HTTP'

Resources