[#5939] Add PRODUCT() aggregate and window function

This commit is contained in:
Lukas Eder 2018-09-20 19:26:03 +02:00
parent f43dfec59a
commit 97d652e08b
3 changed files with 74 additions and 0 deletions

View File

@ -17360,6 +17360,36 @@ public class DSL {
return new org.jooq.impl.Function<BigDecimal>("sum", true, SQLDataType.NUMERIC, nullSafe(field));
}
/**
* Get the product over a numeric field: product(field).
* <p>
* No database currently supports multiplicative aggregation natively. jOOQ
* emulates this using <code>exp(sum(log(arg)))</code> for strictly positive
* numbers, and does some additional handling for zero and negative numbers.
* <p>
* More information here: <a href=
* "https://blog.jooq.org/2018/09/21/how-to-write-a-multiplication-aggregate-function-in-sql">https://blog.jooq.org/2018/09/21/how-to-write-a-multiplication-aggregate-function-in-sql</a>.
*/
@Support
public static AggregateFunction<BigDecimal> product(Field<? extends Number> field) {
return new org.jooq.impl.Function<BigDecimal>(Term.PRODUCT, SQLDataType.NUMERIC, nullSafe(field));
}
/**
* Get the sum over a numeric field: product(distinct field).
* <p>
* No database currently supports multiplicative aggregation natively. jOOQ
* emulates this using <code>exp(sum(log(arg)))</code> for strictly positive
* numbers, and does some additional handling for zero and negative numbers.
* <p>
* More information here: <a href=
* "https://blog.jooq.org/2018/09/21/how-to-write-a-multiplication-aggregate-function-in-sql">https://blog.jooq.org/2018/09/21/how-to-write-a-multiplication-aggregate-function-in-sql</a>.
*/
@Support
public static AggregateFunction<BigDecimal> productDistinct(Field<? extends Number> field) {
return new org.jooq.impl.Function<BigDecimal>(Term.PRODUCT, true, SQLDataType.NUMERIC, nullSafe(field));
}
/**
* Get the average over a numeric field: avg(field).
*/

View File

@ -53,10 +53,13 @@ import static org.jooq.SQLDialect.POSTGRES_9_4;
import static org.jooq.SQLDialect.SQLITE;
// ...
import static org.jooq.impl.DSL.condition;
import static org.jooq.impl.DSL.inline;
import static org.jooq.impl.DSL.mode;
import static org.jooq.impl.DSL.name;
import static org.jooq.impl.DSL.one;
import static org.jooq.impl.DSL.percentileCont;
import static org.jooq.impl.DSL.when;
import static org.jooq.impl.DSL.zero;
import static org.jooq.impl.Keywords.K_AS;
import static org.jooq.impl.Keywords.K_DENSE_RANK;
import static org.jooq.impl.Keywords.K_DISTINCT;
@ -78,6 +81,7 @@ import static org.jooq.impl.Term.ARRAY_AGG;
import static org.jooq.impl.Term.LIST_AGG;
import static org.jooq.impl.Term.MEDIAN;
import static org.jooq.impl.Term.MODE;
import static org.jooq.impl.Term.PRODUCT;
import static org.jooq.impl.Term.ROW_NUMBER;
import static org.jooq.impl.Tools.DataKey.DATA_WINDOW_DEFINITIONS;
@ -237,6 +241,40 @@ class Function<T> extends AbstractField<T> implements
ctx.visit(percentileCont(new BigDecimal("0.5")).withinGroupOrderBy(fields));
}
else if (term == PRODUCT) {
@SuppressWarnings({ "unchecked", "rawtypes" })
Field<Integer> f = (Field) DSL.field("{0}", arguments.get(0));
Field<Integer> negatives = DSL.when(f.lt(zero()), inline(-1));
@SuppressWarnings("serial")
Field<BigDecimal> negativesSum = new CustomField<BigDecimal>("sum", SQLDataType.NUMERIC) {
@Override
public void accept(Context<?> c) {
c.visit(distinct
? DSL.sumDistinct(negatives)
: DSL.sum(negatives));
toSQLFilterClause(c);
toSQLOverClause(c);
}
};
@SuppressWarnings("serial")
Field<BigDecimal> logarithmsSum = new CustomField<BigDecimal>("sum", SQLDataType.NUMERIC) {
@Override
public void accept(Context<?> c) {
c.visit(DSL.sum(DSL.ln(DSL.abs(f))));
toSQLFilterClause(c);
toSQLOverClause(c);
}
};
ctx.visit(
when(negativesSum.mod(inline(2)).lt(inline(BigDecimal.ZERO)), inline(-1))
.otherwise(one()).mul(DSL.exp(logarithmsSum))
);
}
else {
toSQLArguments(ctx);
toSQLKeepDenseRankOrderByClause(ctx);

View File

@ -214,6 +214,12 @@ enum Term {
return "octet_length";
}
},
PRODUCT {
@Override
public String translate(SQLDialect dialect) {
return "product";
}
},
ROW_NUMBER {
@Override
public String translate(SQLDialect dialect) {