[#734] Add support for Oracle/SQL Server CUBE and ROLLUP clauses

This commit is contained in:
Lukas Eder 2011-10-30 08:18:51 +00:00
parent a8aa1fe48e
commit 36f560f5c4
2 changed files with 263 additions and 101 deletions

View File

@ -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));
}

View File

@ -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
// -------------------------------------------------------------------------