SELECT AVG("...") with Criteria API - sql

I'm having difficulties with translating following SQL syntax into Criteria API:
SELECT AVG(dbo.Member.Points) FROM dbo.Member WHERE dbo.Member.PaidMemberRegDate IS NOT NULL;
I have a Member class with a Points property. I just want to get the average Points of all Members that have the property PaidMemberRegDate set to null.

You should be able to use Projections to take care of this:
Criteria criteria = session.createCriteria(dbo.Member.class)
.setProjection(Projections.avg("Points"))
.add(Restrictions.isNotnUll("PaidMemberRegDate"))
Change the values around to match your class and associations and that should do it.

Related

Hasura GraphQL query : where clause with value from related entity

I recently started using GraphQL through a Hasura layer on top of a PostgreSQL DB. I am having troubles implementing a basic query.
Here are my entities :
article :
articleId
content
publishedDate
categoryId
category :
categoryId
name
createdDate
What I am trying to achieve, in English, is the following : get the articles that were published in the first 3 days following the creation of their related category.
In pseudo-SQL, I would do something similar to this :
SELECT Article.content, Category.name
FROM Article
INNER JOIN Category ON Article.categoryId = Category.categoryId
WHERE Article.publishedDate < Category.createdDate + 3 days
Now as a GraphQL query, I tried something similar to this :
query MyQuery {
articles(where: {publishedDate: {_lt: category.createdDate + 3 days}}) {
content
category {
name
}
}
}
Unfortunately, it does not recognise the “category.createdDate” in the where clause. I tried multiple variations, including aliases, with no success.
What am I missing ?
To my understanding of the Hasura docs, there is no way to reference a field within a query like you can do in SQL. But that does not mean, you can't do, what you are trying to do. There are three ways of achieving the result that you want:
1. Create a filtered view
CREATE VIEW earliest_articles AS
SELECT Article.*
FROM Article
INNER JOIN Category ON Article.categoryId = Category.categoryId
WHERE Article.publishedDate < Category.createdDate + 3 days
This view should now become available as a query. Docs for views are here.
2. Create a view with a new field
CREATE VIEW articles_with_creation_span AS
SELECT
Article.*,
(Article.publishedDate - Category.createdDate) AS since_category_creation
FROM Article
INNER JOIN Category ON Article.categoryId = Category.categoryId
Now you can again query this view and use a filter query on the extra field. This solution is useful if you want to vary the amount of time, that you want to query for. The downside is, that there are now two different article types, it might make sense to hide the regular articles query then.
3. Use a computed field
You can also define computed fields in Hasura. These fields are not only included in the output object type of the corresponding GraphQL type, but they can also be used for querying. Refer to the docs on how to create a computed field. Here you can again, calculate the difference and then use some kind of comparison operator (<) to check if this time is smaller than '3 days'.

How to create a member that filters data across dimensions?

I would like to create a calculated member, (or whatever is best) that will filter data based on a set of conditions.
Example: If [dimension1].[attribute1] = 'Y', and if [dimension2].[attribute2] between 0 and 8, then "call this member "Red", else, call this member "black".
I would like to then be able to drag whichever member i need to the where clause of a MDX statement, and have it filter the data based on the conditions i specified.
I am new to this, and if someone can give me a sample query to do this, i would sure appreciate it!
WITH
MEMBER A AS
STRTOVAL( [dimension2].[attribute2].currentmember.name)
MEMBER B AS
IIF(A >=0 AND A < 9 AND [dimension1].[attribute1].currentmember IS [dimension1].[attribute1].&[y],'Red', 'Black'
I created two members. The first handles the range. Attributes are stored as text, they need to be converted. This was it's own member due to needing to reference it twice in member B. Member B is a basic IIF that includes the conditions and outputs specified.
For these members to work, Dimension1.attribute1 and dimension2.Attribute2 will need to be present in your select statement.

Field's value of native query in JPA

How to get value of some fields in a native query (JPA)?
For example I want to get name and age of customer table:
Query q = em.createNativeQuery("SELECT name,age FROM customer WHERE id=...");
Note: I don't want to map results to entities. I just want to get the value of the field.
Thanks
A native query with multiple select expressions returns an Object[] (or List<Object[]>). From the specification:
3.6.1 Query Interface
...
The elements of the result of a Java
Persistence query whose SELECT clause
consists of more than one select
expression are of type Object[]. If
the SELECT clause consists of only one
select expression, the elements of the
query result are of type Object. When
native SQL queries are used, the SQL
result set mapping (see section
3.6.6), determines how many items (entities, scalar values, etc.) are
returned. If multiple items are
returned, the elements of the query
result are of type Object[]. If only a
single item is returned as a result of
the SQL result set mapping or if a
result class is specified, the
elements of the query result are of
type Object.
So, to get the name and age in your example, you'd have to do something like this:
Query q = em.createNativeQuery("SELECT name,age FROM customer WHERE id = ?1");
q.setParameter(1, customerId);
Object[] result = (Object[])q.getSingleResult();
String name = result[0];
int age = result[1];
References
JPA 1.0 specification
Section 3.6.1 "Query Interface"
Section 3.6.6 "SQL Queries"
Depends on the JPA implementation. Hibernate does it different than castor, for example.
Here's a example how it would work in Hibernate:
Query query = session.createSQLQuery(
"select s.stock_code from stock s where s.stock_code = :stockCode")
.setParameter("stockCode", "7277");
List result = query.list();
I don't think that it is possible to get a plane value such as an integer... but this one should come very close to it.

NHibernate How to Select distinct objects based on specific property using HQL?

How can HQL be used to select specific objects that meet a certain criteria?
We've tried the following to generate a list of top ten subscribed RSS feeds (where SubscriptionCount is a derived property):
var topTen = UoW.Session.CreateQuery( #"SELECT distinct rss
FROM RssFeedSubscription rss
group by rss.FeedUrl
order by rss.SubscriptionCount DESC
")
.SetMaxResults(10)
.List<RssFeedSubscription>();
Where the intention is only to select the two unique feed URLs in the database, rather than the ten rows int the database instantiated as objects. The result of the above is:
Column 'RssSubscriptions.Id' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
ORDER BY items must appear in the select list if SELECT DISTINCT is specified.
It's possible just to thin out the results so that we take out the two unique feed URLs after we get the data back from the database, but there must be a way to do this at the DB level using HQL?
EDIT: We realise it's possible to do a Scalar query and then manually pull out values, but is there not a way of simply specifying a match criteria for objects pulled back?
If you change your HQL a bit to look like that:
var topTen = UoW.Session.CreateQuery( #"SELECT distinct rss.FeedUrl
FROM RssFeedSubscription rss
group by rss.FeedUrl
order by rss.SubscriptionCount DESC
")
.SetMaxResults(10)
.List();
the topTen variable will be an object[] with 2 elements in there being the 2 feed URLs.
You can have this returned as strongly typed collection if you use the SetResultTransformer() method of the IQuery interfase.
You need to perform a scalar query. Here is an example from the NHibernate docs:
IEnumerable results = sess.Enumerable(
"select cat.Color, min(cat.Birthdate), count(cat) from Cat cat " +
"group by cat.Color"
);
foreach ( object[] row in results )
{
Color type = (Color) row[0];
DateTime oldest = (DateTime) row[1];
int count = (int) row[2];
.....
}
It's the group by rss.FeedUrl that's causing you the problem. It doesn't look like you need it since you're selecting the entities themselves. Remove that and I think you'll be good.
EDIT - My apologies I didn't notice the part about the "derived property". By that I assume you mean it's not a Hibernate-mapped property and, thus doesn't actually have a column in the table? That would explain the second error message you received in your query. You may need to remove the "order by" clause as well and do your sorting in Java if that's the case.

Query for restricting associated entities

I would like to form a query where an associated collection has been
restricted, ideally with Hibernate Criteria or HQL, but I'd be
interested in how to do this in SQL. For example, say I have a Boy
class with a bidirectional one-to-many association to the Kites class.
I want to get a List of the Boys whose kites' lengths are in a range.
The problem is that the HQL/Criteria I know only gets me Boy objects
with a complete (unrestricted) Set of Kites, as given in these two
examples (the HQL is a guess). I.e., I get the Boys who have Kites
in the right range, but for each such Boy I get all of the Kites, not
just the ones in the range.
select new Boy(name) from Boy b
inner join Kite on Boy.id=Kite.boyId
where b.name = "Huck" and length >= 1;
Criteria crit = session.createCriteria(Boy.class);
crit.add(Restrictions.eq("name", "Huck"))
.createCriteria("kites")
.add(Restrictions.ge("length", new BigDecimal(1.0)));
List list = crit.list();
Right now the only way I have to get the correct Kite length Sets is
to iterate through the list of Boys and for each one re-query Kites
for the ones in the range. I'm hoping some SQL/HQL/Criteria wizard
knows a better way. I'd prefer to get a Criteria solution because my
real "Boy" constructor has quite a few arguments and it would be handy
to have the initialized Boys.
My underlying database is MySQL. Do not assume that I know much about
SQL or Hibernate. Thanks!
I'm no hibernate expert, but as you say you're interested in the SQL solution as well...:
In SQL, I assume you mean something like (with the addition of indices, keys, etc):
CREATE TABLE Boys (Id INT, Name VARCHAR(16))
CREATE TABLE Kites(Length FLOAT, BoyID INT, Description TEXT)
plus of course other columns &c that don't matter here.
All boys owning 1+ kites with lenghts between 1.0 and 1.5:
SELECT DISTINCT Boys.*
FROM Boys
JOIN Kites ON(Kites.BoyID=Boys.ID AND Kites.Length BETWEEN 1.0 AND 1.5)
If you also want to see the relevant kites' description, with N rows per boy owning N such kites:
SELECT Boys.*, Kites.Length, Kites.Description
FROM Boys
JOIN Kites ON(Kites.BoyID=Boys.ID AND Kites.Length BETWEEN 1.0 AND 1.5)
Hope somebody can help you integrate these with hybernate...!
It turns out that this is best done by reversing the join:
Criteria crit = session.createCriteria(Kite.class);
crit.add(Restrictions.ge("length", new BigDecimal(1.0))
.createCriteria("boy")
.add(Restrictions.eq("name", "Huck")));
List<Kite> list = crit.list();
Note that the list's Kites need to be aggregated into Boys, this
can be done easily with a HashMap.