[#734] Add support for Oracle/SQL Server CUBE and ROLLUP clauses
This commit is contained in:
parent
a8aa1fe48e
commit
36f560f5c4
@ -45,6 +45,7 @@ import static org.jooq.impl.Factory.cast;
|
||||
import static org.jooq.impl.Factory.castNull;
|
||||
import static org.jooq.impl.Factory.count;
|
||||
import static org.jooq.impl.Factory.countOver;
|
||||
import static org.jooq.impl.Factory.cube;
|
||||
import static org.jooq.impl.Factory.cumeDistOver;
|
||||
import static org.jooq.impl.Factory.currentDate;
|
||||
import static org.jooq.impl.Factory.currentTime;
|
||||
@ -61,6 +62,7 @@ import static org.jooq.impl.Factory.percentRankOver;
|
||||
import static org.jooq.impl.Factory.pi;
|
||||
import static org.jooq.impl.Factory.rand;
|
||||
import static org.jooq.impl.Factory.rankOver;
|
||||
import static org.jooq.impl.Factory.rollup;
|
||||
import static org.jooq.impl.Factory.rowNumberOver;
|
||||
import static org.jooq.impl.Factory.table;
|
||||
import static org.jooq.impl.Factory.trueCondition;
|
||||
@ -2966,6 +2968,108 @@ public abstract class jOOQAbstractTest<
|
||||
assertEquals("Paulo", result.getValue(0, VLibrary_AUTHOR()).substring(0, 5));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGroupByCubeRollup() throws Exception {
|
||||
switch (getDialect()) {
|
||||
case ASE:
|
||||
case DERBY:
|
||||
case H2:
|
||||
case HSQLDB:
|
||||
case INGRES:
|
||||
case MYSQL:
|
||||
case POSTGRES:
|
||||
case SQLITE:
|
||||
log.info("SKIPPING", "Group by CUBE / ROLLUP tests");
|
||||
return;
|
||||
}
|
||||
|
||||
Result<Record> result;
|
||||
|
||||
result = create().select(
|
||||
TBook_ID(),
|
||||
TBook_AUTHOR_ID())
|
||||
.from(TBook())
|
||||
.groupBy(cube(
|
||||
TBook_ID(),
|
||||
TBook_AUTHOR_ID()))
|
||||
.orderBy(
|
||||
TBook_ID().asc().nullsFirst(),
|
||||
TBook_AUTHOR_ID().asc().nullsFirst()).fetch();
|
||||
|
||||
assertEquals(11, result.size());
|
||||
assertNull(result.getValue(0, 0));
|
||||
assertNull(result.getValue(0, 1));
|
||||
|
||||
assertNull(result.getValue(1, 0));
|
||||
assertEquals(1, result.getValue(1, 1));
|
||||
|
||||
assertNull(result.getValue(2, 0));
|
||||
assertEquals(2, result.getValue(2, 1));
|
||||
|
||||
assertEquals(1, result.getValue(3, 0));
|
||||
assertNull(result.getValue(3, 1));
|
||||
|
||||
assertEquals(1, result.getValue(4, 0));
|
||||
assertEquals(1, result.getValue(4, 1));
|
||||
|
||||
assertEquals(2, result.getValue(5, 0));
|
||||
assertNull(result.getValue(5, 1));
|
||||
|
||||
assertEquals(2, result.getValue(6, 0));
|
||||
assertEquals(1, result.getValue(6, 1));
|
||||
|
||||
assertEquals(3, result.getValue(7, 0));
|
||||
assertNull(result.getValue(7, 1));
|
||||
|
||||
assertEquals(3, result.getValue(8, 0));
|
||||
assertEquals(2, result.getValue(8, 1));
|
||||
|
||||
assertEquals(4, result.getValue(9, 0));
|
||||
assertNull(result.getValue(9, 1));
|
||||
|
||||
assertEquals(4, result.getValue(10, 0));
|
||||
assertEquals(2, result.getValue(10, 1));
|
||||
|
||||
result = create().select(
|
||||
TBook_ID(),
|
||||
TBook_AUTHOR_ID())
|
||||
.from(TBook())
|
||||
.groupBy(rollup(
|
||||
TBook_ID(),
|
||||
TBook_AUTHOR_ID()))
|
||||
.orderBy(
|
||||
TBook_ID().asc().nullsFirst(),
|
||||
TBook_AUTHOR_ID().asc().nullsFirst()).fetch();
|
||||
|
||||
assertEquals(9, result.size());
|
||||
assertNull(result.getValue(0, 0));
|
||||
assertNull(result.getValue(0, 1));
|
||||
|
||||
assertEquals(1, result.getValue(1, 0));
|
||||
assertNull(result.getValue(1, 1));
|
||||
|
||||
assertEquals(1, result.getValue(2, 1));
|
||||
assertEquals(1, result.getValue(2, 1));
|
||||
|
||||
assertEquals(2, result.getValue(3, 0));
|
||||
assertNull(result.getValue(3, 1));
|
||||
|
||||
assertEquals(2, result.getValue(4, 0));
|
||||
assertEquals(1, result.getValue(4, 1));
|
||||
|
||||
assertEquals(3, result.getValue(5, 0));
|
||||
assertNull(result.getValue(5, 1));
|
||||
|
||||
assertEquals(3, result.getValue(6, 0));
|
||||
assertEquals(2, result.getValue(6, 1));
|
||||
|
||||
assertEquals(4, result.getValue(7, 0));
|
||||
assertNull(result.getValue(7, 1));
|
||||
|
||||
assertEquals(4, result.getValue(8, 0));
|
||||
assertEquals(2, result.getValue(8, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHavingWithoutGrouping() throws Exception {
|
||||
try {
|
||||
@ -4997,9 +5101,9 @@ public abstract class jOOQAbstractTest<
|
||||
record = create().select(tomorrow, ts, yesterday).fetchOne();
|
||||
|
||||
// Ingres truncates milliseconds. Ignore this fact
|
||||
assertEquals(24 * 60 * 60,
|
||||
(record.getValue(ts).getTime() / 1000 - record.getValue(yesterday).getTime() / 1000));
|
||||
assertEquals(24 * 60 * 60 + 60 * 60,
|
||||
(record.getValue(ts).getTime() / 1000 - record.getValue(yesterday).getTime() / 1000));
|
||||
assertEquals(24 * 60 * 60,
|
||||
(record.getValue(tomorrow).getTime() / 1000 - record.getValue(ts).getTime() / 1000));
|
||||
}
|
||||
|
||||
|
||||
@ -202,18 +202,6 @@ public class Factory implements Configuration {
|
||||
return mapping;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Access to the loader API
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Create a new <code>Loader</code> object to load data from a CSV or XML
|
||||
* source
|
||||
*/
|
||||
public final <R extends TableRecord<R>> LoaderOptionsStep<R> loadInto(Table<R> table) {
|
||||
return new LoaderImpl<R>(this, table);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// RenderContext and BindContext accessors
|
||||
// -------------------------------------------------------------------------
|
||||
@ -310,6 +298,18 @@ public class Factory implements Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Access to the loader API
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Create a new <code>Loader</code> object to load data from a CSV or XML
|
||||
* source
|
||||
*/
|
||||
public final <R extends TableRecord<R>> LoaderOptionsStep<R> loadInto(Table<R> table) {
|
||||
return new LoaderImpl<R>(this, table);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Conversion of objects into tables
|
||||
// -------------------------------------------------------------------------
|
||||
@ -906,6 +906,10 @@ public class Factory implements Configuration {
|
||||
return new SQLResultQuery(this, sql, bindings).fetchOne();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// JDBC convenience methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Fetch all data from a JDBC {@link ResultSet} and transform it to a jOOQ
|
||||
* {@link Result}. After fetching all data, the JDBC ResultSet will be
|
||||
@ -1610,7 +1614,7 @@ public class Factory implements Configuration {
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Global Field factory
|
||||
// Global Field and Function factory
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
@ -1755,6 +1759,146 @@ public class Factory implements Configuration {
|
||||
return (Field<T>[]) castFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ROLLUP(fiel1, field2, .., fieldn) grouping field
|
||||
* <p>
|
||||
* This has been observed to work with the following databases:
|
||||
* <ul>
|
||||
* <li>DB2</li>
|
||||
* <li>Oracle</li>
|
||||
* <li>SQL Server</li>
|
||||
* <li>Sybase SQL Anywhere</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Please check the SQL Server documentation for a very nice explanation of
|
||||
* <code>CUBE</code>, <code>ROLLUP</code>, and <code>GROUPING SETS</code>
|
||||
* clauses in grouping contexts: <a
|
||||
* href="http://msdn.microsoft.com/en-US/library/bb522495.aspx"
|
||||
* >http://msdn.microsoft.com/en-US/library/bb522495.aspx</a>
|
||||
*
|
||||
* @param fields The fields that are part of the <code>ROLLUP</code>
|
||||
* function
|
||||
* @return A field to be used in a <code>GROUP BY</code> clause
|
||||
*/
|
||||
public static Field<?> rollup(Field<?>... fields) {
|
||||
return function("rollup", Object.class, fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a CUBE(fiel1, field2, .., fieldn) grouping field
|
||||
* <p>
|
||||
* This has been observed to work with the following databases:
|
||||
* <ul>
|
||||
* <li>DB2</li>
|
||||
* <li>Oracle</li>
|
||||
* <li>SQL Server</li>
|
||||
* <li>Sybase SQL Anywhere</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Please check the SQL Server documentation for a very nice explanation of
|
||||
* <code>CUBE</code>, <code>ROLLUP</code>, and <code>GROUPING SETS</code>
|
||||
* clauses in grouping contexts: <a
|
||||
* href="http://msdn.microsoft.com/en-US/library/bb522495.aspx"
|
||||
* >http://msdn.microsoft.com/en-US/library/bb522495.aspx</a>
|
||||
*
|
||||
* @param fields The fields that are part of the <code>CUBE</code>
|
||||
* function
|
||||
* @return A field to be used in a <code>GROUP BY</code> clause
|
||||
*/
|
||||
public static Field<?> cube(Field<?>... fields) {
|
||||
return function("cube", Object.class, fields);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Aggregate and window functions
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Get the count(*) function
|
||||
*
|
||||
* @see Field#count()
|
||||
* @see Field#countDistinct()
|
||||
*/
|
||||
public static Field<Integer> count() {
|
||||
return field("*", Integer.class).count();
|
||||
}
|
||||
|
||||
/**
|
||||
* The <code>count(*) over ([analytic clause])</code> function.
|
||||
* <p>
|
||||
* Window functions are supported in DB2, Postgres, Oracle, SQL Server and
|
||||
* Sybase.
|
||||
*
|
||||
* @see Field#countOver()
|
||||
*/
|
||||
public static WindowPartitionByStep<Integer> countOver() {
|
||||
return field("*", Integer.class).countOver();
|
||||
}
|
||||
|
||||
/**
|
||||
* The <code>row_number() over ([analytic clause])</code> function.
|
||||
* <p>
|
||||
* Window functions are supported in DB2, Postgres, Oracle, SQL Server and
|
||||
* Sybase.
|
||||
*/
|
||||
public static WindowPartitionByStep<Integer> rowNumberOver() {
|
||||
return new WindowFunction<Integer>("row_number", SQLDataType.INTEGER);
|
||||
}
|
||||
|
||||
/**
|
||||
* The <code>rank_over() over ([analytic clause])</code> function.
|
||||
* <p>
|
||||
* Window functions are supported in DB2, Postgres, Oracle, SQL Server and
|
||||
* Sybase.
|
||||
*/
|
||||
public static WindowPartitionByStep<Integer> rankOver() {
|
||||
return new WindowFunction<Integer>("rank", SQLDataType.INTEGER);
|
||||
}
|
||||
|
||||
/**
|
||||
* The <code>dense_rank() over ([analytic clause])</code> function.
|
||||
* <p>
|
||||
* Window functions are supported in DB2, Postgres, Oracle, SQL Server and
|
||||
* Sybase.
|
||||
*/
|
||||
public static WindowPartitionByStep<Integer> denseRankOver() {
|
||||
return new WindowFunction<Integer>("dense_rank", SQLDataType.INTEGER);
|
||||
}
|
||||
|
||||
/**
|
||||
* The <code>precent_rank() over ([analytic clause])</code> function.
|
||||
* <p>
|
||||
* Window functions are supported in DB2, Postgres, Oracle, SQL Server and
|
||||
* Sybase.
|
||||
*/
|
||||
public static WindowPartitionByStep<BigDecimal> percentRankOver() {
|
||||
return new WindowFunction<BigDecimal>("percent_rank", SQLDataType.NUMERIC);
|
||||
}
|
||||
|
||||
/**
|
||||
* The <code>cume_dist() over ([analytic clause])</code> function.
|
||||
* <p>
|
||||
* Window functions are supported in DB2, Postgres, Oracle, SQL Server and
|
||||
* Sybase.
|
||||
*/
|
||||
public static WindowPartitionByStep<BigDecimal> cumeDistOver() {
|
||||
return new WindowFunction<BigDecimal>("cume_dist", SQLDataType.NUMERIC);
|
||||
}
|
||||
|
||||
/**
|
||||
* The <code>ntile([number]) over ([analytic clause])</code> function.
|
||||
* <p>
|
||||
* Window functions are supported in DB2, Postgres, Oracle, SQL Server and
|
||||
* Sybase.
|
||||
*/
|
||||
public static WindowPartitionByStep<BigDecimal> ntile(int number) {
|
||||
return new WindowFunction<BigDecimal>("ntile", SQLDataType.NUMERIC, field("" + number, Integer.class));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Bind values
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Get a constant value
|
||||
* <p>
|
||||
@ -2129,92 +2273,6 @@ public class Factory implements Configuration {
|
||||
return new Euler();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Aggregate and window functions
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Get the count(*) function
|
||||
*
|
||||
* @see Field#count()
|
||||
* @see Field#countDistinct()
|
||||
*/
|
||||
public static Field<Integer> count() {
|
||||
return field("*", Integer.class).count();
|
||||
}
|
||||
|
||||
/**
|
||||
* The <code>count(*) over ([analytic clause])</code> function.
|
||||
* <p>
|
||||
* Window functions are supported in DB2, Postgres, Oracle, SQL Server and
|
||||
* Sybase.
|
||||
*
|
||||
* @see Field#countOver()
|
||||
*/
|
||||
public static WindowPartitionByStep<Integer> countOver() {
|
||||
return field("*", Integer.class).countOver();
|
||||
}
|
||||
|
||||
/**
|
||||
* The <code>row_number() over ([analytic clause])</code> function.
|
||||
* <p>
|
||||
* Window functions are supported in DB2, Postgres, Oracle, SQL Server and
|
||||
* Sybase.
|
||||
*/
|
||||
public static WindowPartitionByStep<Integer> rowNumberOver() {
|
||||
return new WindowFunction<Integer>("row_number", SQLDataType.INTEGER);
|
||||
}
|
||||
|
||||
/**
|
||||
* The <code>rank_over() over ([analytic clause])</code> function.
|
||||
* <p>
|
||||
* Window functions are supported in DB2, Postgres, Oracle, SQL Server and
|
||||
* Sybase.
|
||||
*/
|
||||
public static WindowPartitionByStep<Integer> rankOver() {
|
||||
return new WindowFunction<Integer>("rank", SQLDataType.INTEGER);
|
||||
}
|
||||
|
||||
/**
|
||||
* The <code>dense_rank() over ([analytic clause])</code> function.
|
||||
* <p>
|
||||
* Window functions are supported in DB2, Postgres, Oracle, SQL Server and
|
||||
* Sybase.
|
||||
*/
|
||||
public static WindowPartitionByStep<Integer> denseRankOver() {
|
||||
return new WindowFunction<Integer>("dense_rank", SQLDataType.INTEGER);
|
||||
}
|
||||
|
||||
/**
|
||||
* The <code>precent_rank() over ([analytic clause])</code> function.
|
||||
* <p>
|
||||
* Window functions are supported in DB2, Postgres, Oracle, SQL Server and
|
||||
* Sybase.
|
||||
*/
|
||||
public static WindowPartitionByStep<BigDecimal> percentRankOver() {
|
||||
return new WindowFunction<BigDecimal>("percent_rank", SQLDataType.NUMERIC);
|
||||
}
|
||||
|
||||
/**
|
||||
* The <code>cume_dist() over ([analytic clause])</code> function.
|
||||
* <p>
|
||||
* Window functions are supported in DB2, Postgres, Oracle, SQL Server and
|
||||
* Sybase.
|
||||
*/
|
||||
public static WindowPartitionByStep<BigDecimal> cumeDistOver() {
|
||||
return new WindowFunction<BigDecimal>("cume_dist", SQLDataType.NUMERIC);
|
||||
}
|
||||
|
||||
/**
|
||||
* The <code>ntile([number]) over ([analytic clause])</code> function.
|
||||
* <p>
|
||||
* Window functions are supported in DB2, Postgres, Oracle, SQL Server and
|
||||
* Sybase.
|
||||
*/
|
||||
public static WindowPartitionByStep<BigDecimal> ntile(int number) {
|
||||
return new WindowFunction<BigDecimal>("ntile", SQLDataType.NUMERIC, field("" + number, Integer.class));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Pseudo fields and date time functions
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
Loading…
Reference in New Issue
Block a user