I'm using XQuery to perform addition. Following is the structure of XML saved in database:
<Events>
<Event>
<id>1</id>
<code>1001</code>
<Amount>50,1</Amount>
</Event>
<Event>
<id>1</id>
<code>1002</code>
<Amount>5,5</Amount>
</Event>
<Event>
<id>1</id>
<code>1001</code>
<Amount>50,1</Amount>
</Event>
<Event>
<id>1</id>
<code>1002</code>
<Amount>5,5</Amount>
</Event>
</Events>
I want to get below output by using XQuery: the sum of amount having same code. Please note , is .. I need to replace , by . and the perform arithmetic operation.
<Total>
<1001> 100,2 </1001>
<1002> 11,0 </1002>
</Total>
If your XQuery processor supports XQuery 3.0, use the group by statement.
<Total>
{
for $i in //Event
let $code := $i/code
group by $code
return element {"code"} { attribute {"id"} {$code}, sum($i/Amount)}
}
</Total>
There are two differences to the XML snippets in your question: I changed the floating point seperator to points (which is required, of course you could do this using some XQuery string operations, too) and element names may not consist of numbers only, have a look at the element naming rules. I decided to return the code as id-attribute instead in my example.
This will get you the data as a result set.
declare #X xml
set #X =
'<Events>
<Event>
<id>1</id>
<code>1001</code>
<Amount>50,1</Amount>
</Event>
<Event>
<id>1</id>
<code>1002</code>
<Amount>5,5</Amount>
</Event>
<Event>
<id>1</id>
<code>1001</code>
<Amount>50,1</Amount>
</Event>
<Event>
<id>1</id>
<code>1002</code>
<Amount>5,5</Amount>
</Event>
</Events>'
select T.code,
sum(Amount) as Amount
from
(
select T.X.value('code[1]', 'int') as code,
cast(replace(T.X.value('Amount[1]', 'varchar(13)'), ',', '.') as float) as Amount
from #X.nodes('Events/Event') as T(X)
) as T
group by T.code
The following code will calculate the totals and output the result as XML, but not in your output (which is invalid):
SELECT Code AS 'Code', SUM(Value) AS 'Total'
FROM (
SELECT
CONVERT(DECIMAL(9,2), REPLACE(c.value('Amount[1]', 'VARCHAR(10)'), ',', '.')) AS Value
, c.value('code[1]', 'INT') AS Code
FROM #x.nodes('//Event') AS t(c)
) t
GROUP BY Code
FOR XML PATH('Total'), ROOT('Totals')
where #x is a XML variable containing your data.
Related
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(.)))
I would like to ask about the way to speed up the select queries in a Basex database.
I have for example the following xml in a database with many events(650000 approximately)
<EventList>
<Event>
<ID>317849</ID>
<Type>Measurement</Type>
<TimeStamp>2016-03-15T18:00:09.409</TimeStamp>
<Space>BIOCAT</Space>
<SourceID>BIOCAT.TE310A</SourceID>
<Content>
<Measurement>
<value>920</value>
</Measurement>
</Content>
</Event>
<Event>
<ID>317850</ID>
<Type>Measurement</Type>
<TimeStamp>2016-03-15T18:05:09.409</TimeStamp>
<Space>BIOCAT</Space>
<SourceID>BIOCAT.TE310A</SourceID>
<Content>
<Measurement>
<value>920</value>
</Measurement>
</Content>
</Event>
</EventList>
I am retrieving the events with the following code that selects with respect to the datetime of the Timestamp node
for $b in doc('mydb/my.xml')//EventList/Event
let $date_string as xs:string := xs:string($b/TimeStamp/data())
let $date as xs:dateTime := xs:dateTime($date_string)
where $date ge xs:dateTime('"+startdate+"')
and $date le xs:dateTime('"+enddate+"')
and $b/Type='"+EventType+"'
return $b
But it it is very slow it makes one minute to return 60 events.
There are many data in the BaseX database.
How can I speed up the request or my database?
BaseX currently does not have a range index for xs:dateTime, but you can use the text index for getting all events with a given event Type by moving the comparison into the XPath:
for $b in //EventList/Event[Type = 'Measurement']
let $date as xs:dateTime := xs:dateTime($b/TimeStamp)
where $date ge xs:dateTime('2016-03-15T18:00:00.000')
and $date le xs:dateTime('2016-03-15T19:00:00.000')
return $b
In the Info View of the GUI you can see that the text index is applied:
Compiling:
rewriting descendant-or-self step(s)
applying text index for "Measurement"
pre-evaluating "2016-03-15T18:00:00.000" cast as xs:dateTime
pre-evaluating "2016-03-15T19:00:00.000" cast as xs:dateTime
Optimized Query:
for $b_0 in db:text("mydb/my.xml", "Measurement")
/parent::*:Type/parent::*:Event[parent::*:EventList]
let $date_1 as xs:dateTime := $b_0/TimeStamp cast as xs:dateTime?
where (($date_1 ge "2016-03-15T18:00:00")
and ($date_1 le "2016-03-15T19:00:00"))
return $b_0
Say, I have 5 documents that belong to either of different collections such as:
Biography, Fiction, Humour, Adventure
For instance, document 1 is in Adventure Collection:
<Book>
<title>Harry Potter and the Deathly Hallows</title>
<Author>J.K.Rowling</Author>
<year>2007</year>
</Book>
document 2, is in Biography Collection:
<Book>
<title>Steve Jobs</title>
<Author>Walter Issacson</Author>
<year>2011</year>
</Book>
Now I want to apply suggest on year element. I want to apply this on collections i.e. for example-suggest on Biography collections
I have defined element range index on year and ingested sample documents using application loader and configured collections for documents in document settings.
Follwing is my XQUERY code:
xquery version "1.0-ml";
import module namespace search = "http://marklogic.com/appservices/search"
at "/MarkLogic/appservices/search/search.xqy";
let $options :=
<options xmlns="http://marklogic.com/appservices/search">
<constraint name="Group">
<collection prefix="Biography/"/>
</constraint>
<suggestion-source ref="Group">
<range collation="http://marklogic.com/collation/"
type="xs:string" >
<element name="year"/>
</range>
</suggestion-source>
</options>
return
search:suggest("Group:20", $options)
On running this query I am getting suggestions for both 2011 and 2007 which is not what I am expecting.
Expected suggestion is 2011 (as only Biography collection should be searched).
I have refered this doc on search:suggest but I am not able to find out what exactly is the mistake.
Where am I doing wrong?
Shrey:
The values from the suggestion source are substituted for the values from the referenced constraint. In other words, the options say that input qualified by the group constraint should come from the year range index, which is what you're seeing. For more detail, please see:
http://docs.marklogic.com/guide/search-dev/search-api#id_40748
To filter a set of suggestions, you can call search:suggest() with two string queries. The first provides the input for the suggestions. The second provides the filtering constraint. In the case above, the filtering constraint would be the Biography collection. For more detail, please see:
http://docs.marklogic.com/guide/search-dev/search-api#id_91911
Hoping that helps
However, I am able to achieve the required output using additional-query in options as follows:
xquery version "1.0-ml";
import module namespace search = "http://marklogic.com/appservices/search"
at "/MarkLogic/appservices/search/search.xqy";
let $options :=
<options xmlns="http://marklogic.com/appservices/search">
<additional-query>{cts:collection-query("Biography")}
</additional-query>
<constraint name="Search_Element">
<range collation="http://marklogic.com/collation/"
type="xs:string" >
<element name="year"/>
</range>
</constraint>
<suggestion-source ref="Search_Element">
<range collation="http://marklogic.com/collation/"
type="xs:string" >
<element name="year"/>
</range>
</suggestion-source>
</options>
return
search:suggest("Search_Element:20", $options)
Still, I was wondering how could it be done without using additional-query paramater because it would be more optimized to use collection constraint for above.
I have an xml with the following type of content
<DATA>
<CUSTOMER>
<BASIC_INFO>
<F_NAME>TOM<F_NAME>
<M_NAME>AND<M_NAME>
<L_NAME>HANKS<L_NAME>
</BASIC_INFO>
<ADDITIONAL_INFO>
<EMAIL>TOM.HANKS#GMAIL.COM</EMAIL>
<PHONE_NO>22211132</PHONE_NO>
</ADDITIONAL_INFO>
</CUSTOMER>
<CUSTOMER>
<BASIC_INFO>
<F_NAME>TOM<F_NAME>
<L_NAME>HANKS<L_NAME>
</BASIC_INFO>
<ADDITIONAL_INFO>
<EMAIL>TOM.HANKS#GMAIL.COM</EMAIL>
</ADDITIONAL_INFO>
</CUSTOMER>
<CUSTOMER>
<BASIC_INFO>
<F_NAME>TOM<F_NAME>
</BASIC_INFO>
<ADDITIONAL_INFO>
<PHONE_NO>22211132</PHONE_NO>
</ADDITIONAL_INFO>
</CUSTOMER>
I want to store the information of a customer in the table with columns as F_NAME,M_NAME,L_NAME,PHONE_NO,EMAIL and each value should go in the respective column
Now the point to be noted is that for the customers for which some information is not there for them the value in the table should not be inserted.
You can do it in two steps.
First create a relational representation of you data:
Example from sql-plsql-de.blogspot.co.at:
create table xmltest (
dokument xmltype
)
/
insert into xmltest values (xmltype(
'<blog>
<name>SQL und PL/SQL</name>
<autor>Carsten Czarski</autor>
<themen>
<thema>XML</thema>
<thema>PL/SQL</thema>
</themen>
</blog>'
))
/
This Statement:
select
extractvalue(dokument, '/blog/name') as blog_name,
extractvalue(dokument, '/blog/autor') as blog_autor,
extractvalue(value(thema), '/thema/text()') as thema
from xmltest,
table(xmlsequence(extract(dokument, '/blog/themen/thema'))) thema
/
Re sluts in:
BLOG_NAME BLOG_AUTOR THEMA
-------------------- --------------- --------------------
SQL und PL/SQL Carsten Czarski XML
SQL und PL/SQL Carsten Czarski PL/SQL
Now that you have it in a relational way you can loop over it:
for cur in (select * from ...)
loop
if cur.column_a is not null then
--insert
end if;
end loop;
This is slow but stays readable even if you have complex rules.
Alternatively you can use 'normal' SQL or maybe merge statement.
<data>
<food>
<id>1</id>
<name>asparagus</name>
<catlog>7190</catlog>
</food>
<food>
<id>2</id>
<name>almonds</name>
<catlog>7190</catlog>
</food>
<food>
<id>3</id>
<name>asparagus</name>
<catlog>7192</catlog>
</food>
<food>
<id>4</id>
<name>asparagus</name>
<catlog>7193</catlog>
</food>
</data>
i would like to get the unique catlogs, so from this list i want to extract only 7190, 7192, and 7193. i have a script that puts it into a dropdownlist by using:
DropDownList1.DataSource = dv
DropDownList1.DataBind()
but i need it to get only the unique values.
Take a look at LINQ to XML! With this you have the power to directly query a blob of xml but with less headache than using XPATH (which you could also use to do the same task).
Then you could point your datasource at the result from the LINQ query over your XML blob.
Try the following
Public Function Unique(ByVal doc As XDocument) As IEnumerable(Of String)
Return doc...<catalog>.Select(Function(x) CType(x,Integer)).Distinct()
End Function
Quick Note: The CType may seem strange at first but it does work because the XElement class defines an explicit conversion operator for many value types including Integer.
LINQ is the prefered way I think, but an another option is :
Dim newTable As DataTable = dataView.ToTable( True, "Category")
DropDownList1.DataSource = newTable
DropDownList1.DataBind()