diff --git a/base/src/org/compiere/model/Query.java b/base/src/org/compiere/model/Query.java index 6ac2d34099..563ab2da43 100644 --- a/base/src/org/compiere/model/Query.java +++ b/base/src/org/compiere/model/Query.java @@ -19,6 +19,7 @@ ******************************************************************************/ package org.compiere.model; +import java.math.BigDecimal; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @@ -39,19 +40,26 @@ import org.compiere.util.Util; /** * * @author Low Heng Sin - * @author Teo Sarca, SC ARHIPAC SERVICE SRL + * @author Teo Sarca, www.arhipac.ro *
  • FR [ 1981760 ] Improve Query class *
  • BF [ 2030280 ] org.compiere.model.Query apply access filter issue *
  • FR [ 2041894 ] Add Query.match() method *
  • FR [ 2107068 ] Query.setOrderBy should be more error tolerant *
  • FR [ 2107109 ] Add method Query.setOnlyActiveRecords *
  • FR [ 2421313 ] Introduce Query.firstOnly convenient method + *
  • FR [ 2546052 ] Introduce Query aggregate methods * @author Redhuan D. Oon *
  • FR: [ 2214883 ] Remove SQL code and Replace for Query // introducing SQL String prompt in log.info - * >li>FR: [ 2214883 ] - to introduce .setClient_ID + *
  • FR: [ 2214883 ] - to introduce .setClient_ID */ public class Query { + public static final String AGGREGATE_COUNT = "COUNT"; + public static final String AGGREGATE_SUM = "SUM"; + public static final String AGGREGATE_AVG = "AVG"; + public static final String AGGREGATE_MIN = "MIN"; + public static final String AGGREGATE_MAX = "MAX"; + private static CLogger log = CLogger.getCLogger (Query.class); private Properties ctx = null; @@ -294,6 +302,68 @@ public class Query { return buildSQL(null, true); } + + /** + * Aggregate given expression on this criteria + * @param sqlExpression + * @param sqlFunction + * @return aggregated value + */ + public BigDecimal aggregate(String sqlExpression, String sqlFunction) throws DBException + { + if (Util.isEmpty(sqlFunction, true)) + { + throw new DBException("No Aggregate Function defined"); + } + if (Util.isEmpty(sqlExpression, true)) + { + if (AGGREGATE_COUNT == sqlFunction) + { + sqlExpression = "*"; + } + else + { + throw new DBException("No Expression defined"); + } + } + + StringBuffer sqlSelect = new StringBuffer("SELECT ").append(sqlFunction).append("(") + .append(sqlExpression).append(")") + .append(" FROM ").append(table.getTableName()); + + BigDecimal value = null; + String sql = buildSQL(sqlSelect, false); + PreparedStatement pstmt = null; + ResultSet rs = null; + try + { + pstmt = DB.prepareStatement(sql, this.trxName); + rs = createResultSet(pstmt); + if (rs.next()) + { + value = rs.getBigDecimal(1); + } + if (rs.next()) + { + throw new DBException("QueryMoreThanOneRecordsFound"); // TODO : translate + } + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + DB.close(rs, pstmt); + rs = null; pstmt = null; + } + // + if (value == null) + { + value = Env.ZERO; + } + return value; + } /** * Count items that match query criteria @@ -302,25 +372,18 @@ public class Query */ public int count() throws DBException { - int count = -1; - String sql = buildSQL(new StringBuffer("SELECT COUNT(*) FROM ").append(table.getTableName()), false); - PreparedStatement pstmt = null; - ResultSet rs = null; - try { - pstmt = DB.prepareStatement(sql, this.trxName); - rs = createResultSet(pstmt); - rs.next(); - count = rs.getInt(1); - } - catch (SQLException e) { - throw new DBException(e); - } - finally { - DB.close(rs, pstmt); - } - return count; + return aggregate("*", AGGREGATE_COUNT).intValue(); } + /** + * SUM sqlExpression for items that match query criteria + * @param sqlExpression + * @return sum + */ + public BigDecimal sum(String sqlExpression) + { + return aggregate(sqlExpression, AGGREGATE_SUM); + } /** * Check if there items for query criteria * @return true if exists, false otherwise @@ -548,5 +611,4 @@ public class Query } return retValue; } // get_IDs - } diff --git a/extend/src/test/functional/QueryTest.java b/extend/src/test/functional/QueryTest.java index c204ad7b76..485658c0cc 100644 --- a/extend/src/test/functional/QueryTest.java +++ b/extend/src/test/functional/QueryTest.java @@ -183,5 +183,47 @@ public class QueryTest extends AdempiereTestCase assertEquals(101, ids[0]); assertEquals(102, ids[1]); } - + + public void testAggregate() throws Exception + { + final int AD_Client_ID = Env.getAD_Client_ID(getCtx()); + final String sqlFrom = "FROM C_InvoiceLine WHERE IsActive='Y' AND AD_Client_ID="+AD_Client_ID; + final Query query = new Query(getCtx(), "C_InvoiceLine", null, getTrxName()) + .setOnlyActiveRecords(true) + .setClient_ID(); + // + // Test COUNT: + assertEquals("COUNT not match", + DB.getSQLValueBDEx(getTrxName(), "SELECT COUNT(*) "+sqlFrom), + query.aggregate(null, Query.AGGREGATE_COUNT)); + // + // Test SUM: + assertEquals("SUM not match", + DB.getSQLValueBDEx(getTrxName(), "SELECT SUM(LineNetAmt+TaxAmt) "+sqlFrom), + query.aggregate("LineNetAmt+TaxAmt", Query.AGGREGATE_SUM)); + // + // Test MIN: + assertEquals("MIN not match", + DB.getSQLValueBDEx(getTrxName(), "SELECT MIN(LineNetAmt) "+sqlFrom), + query.aggregate("LineNetAmt", Query.AGGREGATE_MIN)); + // + // Test MAX: + assertEquals("MAX not match", + DB.getSQLValueBDEx(getTrxName(), "SELECT MAX(LineNetAmt) "+sqlFrom), + query.aggregate("LineNetAmt", Query.AGGREGATE_MAX)); + // + // Test Exception : No Aggregate Function defined + assertExceptionThrowed("No Aggregate Function defined", DBException.class, new Runnable(){ + public void run() + { + query.aggregate("*", null); + }}); + // + // Test Exception : No Expression defined + assertExceptionThrowed("No Expression defined", DBException.class, new Runnable(){ + public void run() + { + query.aggregate(null, Query.AGGREGATE_SUM); + }}); + } }