With regular MySQL I can artificially group records against a common "ID" with a query like
SELECT FLOOR(ID / 4) as GroupID FROM Table;
Assuming my IDs are sequential, this gives me groups of 4 with the same GroupID.
My question is, can this be done in JPA with a CriteriaQuery? I can see there is a CriteriaBuilder.prod() method (which can be used for division), but how to I convert the result to a straight integer?
Ok, I see there are actually conversion methods: http://docs.oracle.com/javaee/6/api/javax/persistence/criteria/CriteriaBuilder.html#toLong%28javax.persistence.criteria.Expression%29
Related
I want to do something like this in OrmLite
SELECT *, COUNT(title) as titleCount from table1 group by title;
Is there any way to do this via QueryBuilder without the need for queryRaw?
The documentation states that the use of COUNT() and the like necessitates the use of selectRaw(). I hoped for a way around this - not having to write my SQL as strings is the main reason I chose to use ORMLite.
http://ormlite.com/docs/query-builder
selectRaw(String... columns):
Add raw columns or aggregate functions
(COUNT, MAX, ...) to the query. This will turn the query into
something only suitable for using as a raw query. This can be called
multiple times to add more columns to select. See section Issuing Raw
Queries.
Further information on the use of selectRaw() as I was attempting much the same thing:
Documentation states that if you use selectRaw() it will "turn the query into" one that is supposed to be called by queryRaw().
What it does not explain is that normally while multiple calls to selectColumns() or selectRaw() are valid (if you exclusively use one or the other),
use of selectRaw() after selectColumns() has a 'hidden' side-effect of wiping out any selectColumns() you called previously.
I believe that the ORMLite documentation for selectRaw() would be improved by a note that its use is not intended to be mixed with selectColumns().
QueryBuilder<EmailMessage, String> qb = emailDao.queryBuilder();
qb.selectColumns("emailAddress"); // This column is not selected due to later use of selectRaw()!
qb.selectRaw("COUNT (emailAddress)");
ORMLite examples are not as plentiful as I'd like, so here is a complete example of something that works:
QueryBuilder<EmailMessage, String> qb = emailDao.queryBuilder();
qb.selectRaw("emailAddress"); // This can also be done with a single call to selectRaw()
qb.selectRaw("COUNT (emailAddress)");
qb.groupBy("emailAddress");
GenericRawResults<String[]> rawResults = qb.queryRaw(); // Returns results with two columns
Is there any way to do this via QueryBuilder without the need for queryRaw(...)?
The short answer is no because ORMLite wouldn't know what to do with the extra count value. If you had a Table1 entity with a DAO definition, what field would the COUNT(title) go into? Raw queries give you the power to select various fields but then you need to process the results.
With the code right now (v5.1), you can define a custom RawRowMapper and then use the dao.getRawRowMapper() method to process the results for Table1 and tack on the titleCount field by hand.
I've got an idea how to accomplish this in a better way in ORMLite. I'll look into it.
I'd like to write a Predicate which is calculated based on 2 columns.
for example - I have a daysBetween (int) and lastSent (date).
I'd like to know if there's a possibility to perform the following query :
select from X where the last_sent + [some other column which is interval] lessThan now()
something similar to (and I now this is not the correct way to write it..),
builder.between(userData.<Date>get("lastSent").addDays(userIntervals.<Integer>get("interval")),DateUtils.now());
Is there anyway of writing such a query using predicates?
JPA Criteria does not define an addDays() API. You can call a database specific function through the function() API,
See,
http://en.wikibooks.org/wiki/Java_Persistence/Criteria#Special_Operations
Some JPA providers provide extensions to JPQL and Criteria.
See,
http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/Criteria#JpaCriteriaBuilder_and_EclipseLink_Extensions
and,
http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/JPQL#EclipseLink_Extensions_.28EQL.29
I know how to use "DISTINCT" in Doctrine 2, but I really need to use "DISTINCT ON (field)" and I don't know how to do this with the QueryBuilder.
My SQL query looks like:
SELECT DISTINCT ON (currency) currency, amount FROM payments ORDER BY currency
And this query works perfect, but I can't use it with the QueryBuilder. Maybe I could write this query on some other way?
I would suggest that the SELECT DISTINCT ON (..) construct that PostgreSQL supports is outside the Object Relational Model (ORM) that is central to Doctrine. Or, perhaps put another way, because SELECT DISTINCT ON (..) is rare in SQL implementations Doctrine haven't coded for it.
Regardless of the actual logic for it not working, I would suggest you try Doctrine's "Native SQL". You need to map the results of your query to the ORM.
With NativeQuery you can execute native SELECT SQL statements and map
the results to Doctrine entities or any other result format supported
by Doctrine.
In order to make this mapping possible, you need to describe to
Doctrine what columns in the result map to which entity property. This
description is represented by a ResultSetMapping object.
With this feature you can map arbitrary SQL code to objects, such as
highly vendor-optimized SQL or stored-procedures.
SELECT DISTINCT ON (..) falls into vendor-optimized SQL I think, so using NativeQuery should allow you to access it.
Doctrine QueryBuilder has some limitations. Even if I didn't check if it's was possible with query builder, I do not hesitate to use DQL when I do not know how to write the query with query builder.
Check theses examples at
http://doctrine-orm.readthedocs.org/en/latest/reference/dql-doctrine-query-language.html#dql-select-examples
Hope this help.
INDEX BY can be used in DQL, allowing first result rows indexed by the defined string/int field to be overwritten by following ones with the same index:
SELECT
p.currency,
p.amount
FROM Namespace\To\Payments p INDEX BY p.currency
ORDER BY p.currency ASC
DQL - EBNF - INDEX BY
This is my simple JPQL:
SELECT s
FROM Site s
GROUP BY s.siteType
siteResult = q.getResultList();
for (Site site : siteResult) {
// loops all sites
}
This query returns all sites, including sites of the same siteType.
I'm using JPA 2.0 Eclipselink.
Whats wrong here?
Such a query does not make sense. If you use GROUP BY, other attributes in SELECT should be aggregated. As it is said in JPA specification:
The requirements for the SELECT clause when GROUP BY is used follow
those of SQL: namely, any item that appears in the SELECT clause
(other than as an aggregate function or as an argument to an aggregate
function) must also appear in the GROUP BY clause. In forming the
groups, null values are treated as the same for grouping purposes.
If you think SQL counterpart of your query:
SELECT s.attr1, attr2, s.siteType
FROM site s
GROUP BY (s.siteType)
you notice that it is hard to imagine which possible value of attr1 and attr2 should be chosen.
In such a case EclipseLink with derby just drops GROUP BY away from the query, which is of course little bit questionable way to handle invalid JPQL. I like more how Hibernate+MySQL behaves with such a invalid JPQL, it fails with quite clear error message:
java.sql.SQLSyntaxErrorException: The SELECT list of a grouped query
contains at least one invalid expression. If a SELECT list has a GROUP
BY, the list may only contain valid grouping expressions and valid
aggregate expressions.
Answer to comment:
One Site contains probably also attributes other than siteType as well. Lets use following example:
public class Site {
int id;
String siteType;
}
and two instances: (id=1, siteType="same"), (id=2, siteType="same"). Now when type of select is Site itself (or all attributes of it) and you make group by by siteType, it is impossible to define should result have one with id value 1 or 2. Thats why you have to use some aggregate function (like AVG, which gives you average of attribute values) for remaining attributes (id in our case).
Behind this link: ObjectDB GROUP BY you can find some examples with GROUP BY and aggregates.
The following works
s = select([tsr.c.kod]).where(tsr.c.rr=='10').group_by(tsr.c.kod)
and this does not:
s = select([tsr.c.kod, tsr.c.rr, any fields]).where(tsr.c.rr=='10').group_by(tsr.c.kod)
Why?
thx.
It doesn't work because the query isn't valid like that.
Every column needs to be in the group_by or needs an aggregate (i.e. max(), min(), whatever) according to the SQL standard. Most databases have always complied to this but there are a few exceptions.
MySQL has always been the odd one in this regard, within MySQL this behaviour depends on the ONLY_FULL_GROUP_BY setting: https://dev.mysql.com/doc/refman/8.0/en/group-by-handling.html
I would personally recommend setting the sql_mode setting to ANSI. That way you're largely compliant to the SQL standard which will help you in the future if you ever need to use (or migrate) to a standards compliant database such as PostgreSQL.
What you are trying to do is somehow valid in mysql, but invalid in standard sql, postgresql and common sense. When you group rows by 'kod', each row in a group has the same 'kod' value, but different values for 'rr' for example. With aggregate functions you can get some aspect of the values in this column for each group, for example
select kod, max(rr) from table group by kod
will give you list of 'kod's and the max of 'rr's in each group (by kod).
That being sad, in the select clause you can only put columns from the group by clause and/or aggregate functions from other columns. You can put whatever you like in where - this is used for filtering. You can also put additional 'having' clause after group that contains aggregate function expression that can also be used as post-group filtering.