I have a domain
class InvoiceLine {
String itemName
BigDecimal unitCost
Integer quantity
}
}
I would like to come up with a grails closure using .withCriteria that does an aggregation of the (unitCost * quantity) so that I end up with sql
select item_name, sum(unit_cost * quantity) from invoice_line group by item_name;
For now, the best I could come up with is
def result = InvoiceLine.withCriteria {
projections {
groupProperty('itemName')
sum ('quantity * unitCost')
}
}
Unfortunately, grails chokes up when I run the code above. Anyone have any idea how I could achieve my objective? Any help is highly appreciated.
Does it need to be a criteria query? HQL works great here:
def result = InvoiceLine.executeQuery(
"select itemName, sum(unitCost * quantity) " +
"from InvoiceLine " +
"group by itemName")
The results will be a List of Object[] where the 1st element is a String (the name) and the 2nd is a number (the sum), so for example you could iterate with something like
results.each { row ->
println "Total for ${row[0]} is ${row[1]}"
}
Related
If I have three domain classes like this:
class Candidate {
static belongsto=[application:Application]
}
class Vote {
String name;
static belongsto=[application:Application]
}
class Application {
Date datedemand;
Candidate candidates;
Vote votes
static hasMany = [candidates:Candidate,votes:Vote]
}
I want to retrieve all candidates grouped by Vote's Name .
I started the following attempt, and I remain blocked :
def criteria = Application.createCriteria()
def results = criteria.list {
votes {
}
candidates{
}
}
In decent grails (2.x+) you could try something similar:
Application.withCriteria {
createAlias("votes", votes)
projections {
groupProperty("votes.name")
}
}
In this scenario I would go for a simple vanilla HQL instead of Criteria, something like this or more (use join the way you need):
String hql = "Select V.name, C.name " +
"from Candidate C, Vote V " +
"where C.application = V.application " +
"group by V.name, C.name"
Query query = session.createQuery(hql)
List results = query.list()
I have the following table/model:
class Post {
int id;
String comment;
static belongsTo = [category_id:Category];
}
I wish to create a query that can filter out the last Post (highest id) per Category. I want the results in List<Post> form.
In other words (I believe) in SQL the query would look as follows:
SELECT *
FROM
Post AS source
JOIN (
SELECT MAX(id) AS id, category_id
FROM Post
GROUP BY category_id
) AS filter
ON source.id = filter.id;
If I understand correctly, the first step is to use a HibernateCriteriaBuilder:
def c = Post.createCriteria();
def results = c.list {
projections {
groupProperty("category_id", "myid")
max("id", "version")
}
}
So my question is a two part:
Am I on the right track?
How can I use the results object to obtain a List<Post> array?
(Something like: def latest = Post.FindAllByXXX(result); )
Yes, you are on the right track. I would also add the id property for the Post to my projections:
projections {
property('id')
}
and then collect all Posts using the id to get a list of posts, something like:
def latestPosts = results?.collect{Post.read(it[0])}
I tried to fetch some data with the sql.rows() Groovy method and it took a very long time to return the values.
So I tried the "standard" way and it's much much faster (150 times faster).
What am I missing ?
Look at the code below : the first method returns results in about 2500ms and the second in 15 ms !
class MyService {
javax.sql.DataSource dataSource
def SQL_QUERY = "select M_FIRSTNAME as firstname, M_LASTNAME as lastname, M_NATIONALITY as country from CT_PLAYER order by M_ID asc";
def getPlayers1(int offset, int maxRows)
{
def t = System.currentTimeMillis()
def sql = new Sql(dataSource)
def rows = sql.rows(SQL_QUERY, offset, maxRows)
println "time1 : ${System.currentTimeMillis()-t}"
return rows
}
def getPlayers2(int offset, int maxRows)
{
def t = System.currentTimeMillis();
Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement();
statement.setMaxRows(offset + maxRows -1);
ResultSet resultSet = statement.executeQuery(SQL_QUERY);
def l_list =[];
if(resultSet.absolute(offset)) {
while (true) {
l_list << [
'firstname':resultSet.getString('firstname'),
'lastname' :resultSet.getString('lastname'),
'country' :resultSet.getString('country')
];
if(!resultSet.next()) break;
}
}
resultSet.close()
statement.close()
connection.close()
println "time2 : ${System.currentTimeMillis()-t}"
return l_list
}
When you call sql.rows, groovy eventually calls SqlGroovyMethods.toRowResult for each row returned by the resultSet.
This method interrogates the ResultSetMetaData for the resultSet each time to find the column names, and then fetches the data for each of these columns from the resultSet into a Map which it adds to the returned List.
In your second example, you directly get the columns required by name (as you know what they are), and avoid having to do this lookup every row.
I think I found the reason this method is so slow : statement.setMaxRows() is never called !
That means that a lot of useless data is sent by the database (when you want to see the first pages of a large datagrid)
I wonder how your tests would turn out if you try with setFetchSize instead of setMaxRows. A lot of this has to the underlying JDBC Driver's default behavior.
I need to get a sum of all items sold per order per store. I am running a sum() on expression using executeQuery(). It works fine as shown below but I wanted to know if there is a better, groovier way to do it.
StoreService {
static transactional = false
def getTotalOrders(def store) {
return Store.executeQuery("select sum(a.soldQuantity * a.soldPrice) as total
from OrderItem a inner join a.order b inner join b.store c
where c= :store", [store: store]).get(0)
}
}
Store {
transient storeService
def getTotalSales() {
storeService.getTotalSales()
}
static hasMany = [items: Item]
// no hasMany to Order
}
Item {
static belongsTo = [store: Store]
// no hasMany to OrderItem
}
Order {
static hasMany = [orderItems: OrderItem]
static belongsTo = [store: Store]
}
OrderItem {
BigDecimal soldPrice
Integer soldQuantity
static belongsTo = [order: Order, item: Item]
}
I think withCriteria() would be easier to read but I couldn't figure out how to do it with expressions within sum() wouldn't take for obvious reasons.
projections {
sum("soldPrice * soldQuantity")
}
Thanks
There are two options you can go with.
Option 1
You can add a formula mapping to your domain class then query it directly.
OrderItem {
BigDecimal soldPrice
Integer soldQuantity
BigDecimal totalPrice
static mapping = {
totalPrice formula: "sold_price * sold_quantity"
}
static belongsTo = [order: Order, item: Item]
}
Now your criteria query can just contain
projections {
sum("totalPrice")
}
Not only that but you can query it with dynamic finders OrderItem.findAllByTotalPriceGreaterThan(20.00) as well as simple access println "The final price is ${orderInstance.totalPrice}. We find this really nifty however there are times when you would want to get totalPrice before the OrderItem has been persisted so we usually write a simple(Not DRY) getter
BigDecimal getTotalPrice() {
totalPrice ?: (soldPrice && soldQuantity) ? soldPrice * soldQuantity : null
}
But you only need this sort of thing if you require totalPrice before it has been persisted.
Option 2
Before formula mappings we used to drop down to the Hibernate Criteria API and use a sqlProjection Projection as part of our criteria query.
projections {
addProjectionToList(Projections.sqlProjection(
"sum(sold_price * sold_quantity) as totalPrice",
["totalPrice"] as String[],
[Hibernate.LONG] as Type[],
), "sumProjection")
}
Note
I think it is important to note that in both the formula and the sql projection, use the column names in the database and your database specific sum syntax.
As of Grails 2.2, SQL projections are supported without having to drop down to the Hibernate Criteria API. Note that a formula mapping may still be more desirable, but with this you can directly implement the sum('soldPrice * soldQuantity') style projection as per your question.
http://grails.org/doc/latest/guide/single.html#criteria
I'd try to add a transient derived property total to OrderItem and use sum() on it.
Try SQL Projection
projections {
sqlProjection 'sum("soldPrice * soldQuantity") as total', 'total', StandardBasicTypes.DOUBLE
}
For farther details
http://docs.grails.org/2.5.6/guide/GORM.html#criteria
having the domain classes:
class A {
Date dateCreated
static hasMany = [b:B]
...
}
class B {
String name
String value
...
}
What createCriteria or HQL query can I use to return a list with:
A's creation dateB's value for A with the name entry set to 'X'
Note: Although there's no explicit constraint, there's only one "value" for each 'X' and a combination.
Thanks
The HQL would be
def results = A.executeQuery(
'select a.id, a.dateCreated, b from A a inner join a.b b ' +
'where b.name=:name',
[name: 'X'])
This will give you a List of 3-element Object[] arrays containing A.id, A.dateCreated, and the list of B instances. I added the id to the query so you can group by it client-side:
def grouped = results.groupBy { it[0] }
This will be a Map where the keys are the A ids and the values are the Lists from the original results.
Ideally you'd do the grouping at the database, but it would complicate the query, and assuming you don't have a large number of results it should be fast.