Skip to content

Commit

Permalink
fix multiple TDs request SQL generation
Browse files Browse the repository at this point in the history
  • Loading branch information
KSDaemon committed Jan 22, 2025
1 parent 5657e2b commit 22b7243
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 17 deletions.
73 changes: 57 additions & 16 deletions packages/cubejs-schema-compiler/src/adapter/BaseQuery.js
Original file line number Diff line number Diff line change
Expand Up @@ -1413,18 +1413,59 @@ export class BaseQuery {

overTimeSeriesQuery(baseQueryFn, cumulativeMeasure, fromRollup) {
const dateJoinCondition = cumulativeMeasure.dateJoinCondition();
const uniqDateJoinCondition = R.uniqBy(djc => djc[0].dimension, dateJoinCondition);
const cumulativeMeasures = [cumulativeMeasure];
if (!this.timeDimensions.find(d => d.granularity)) {
const filters = this.segments.concat(this.filters).concat(this.dateFromStartToEndConditionSql(dateJoinCondition, fromRollup, false));
const filters = this.segments
.concat(this.filters)
.concat(this.dateFromStartToEndConditionSql(
// If the same time dimension is passed more than once, no need to build the same
// filter condition again and again. Different granularities don't play role here,
// as rollingWindow.granularity is used for filtering.
uniqDateJoinCondition,
fromRollup,
false
));
return baseQueryFn(cumulativeMeasures, filters, false);
}
const dateSeriesSql = this.timeDimensions.map(d => this.dateSeriesSql(d)).join(', ');
const filters = this.segments.concat(this.filters).concat(this.dateFromStartToEndConditionSql(dateJoinCondition, fromRollup, true));

// We can't do meaningful query if few time dimensions with different ranges passed,
// it won't be possible to join them together without loosing some rows.
const rangedTimeDimensions = this.timeDimensions.filter(d => d.dateRange && d.granularity);
const uniqTimeDimensionWithRanges = R.uniqBy(d => d.dateRange, rangedTimeDimensions);
if (uniqTimeDimensionWithRanges.length > 1) {
throw new Error('Can\'t build query for time dimensions with different date ranges');
}

// We need to generate time series table for the lowest granularity among all time dimensions
let dateSeriesDimension;
const dateSeriesGranularity = this.timeDimensions.filter(d => d.granularity)
.reduce((acc, d) => {
const mg = this.minGranularity(acc, d.resolvedGranularity());
if (mg === d.resolvedGranularity()) {
dateSeriesDimension = d;
}
return mg;
}, undefined);

const dateSeriesSql = this.dateSeriesSql(dateSeriesDimension);

// If the same time dimension is passed more than once, no need to build the same
// filter condition again and again. Different granularities don't play role here,
// as rollingWindow.granularity is used for filtering.
const filters = this.segments
.concat(this.filters)
.concat(this.dateFromStartToEndConditionSql(
uniqDateJoinCondition,
fromRollup,
true
));
const baseQuery = this.groupedUngroupedSelect(
() => baseQueryFn(cumulativeMeasures, filters),
cumulativeMeasure.shouldUngroupForCumulative(),
!cumulativeMeasure.shouldUngroupForCumulative() && this.minGranularity(
cumulativeMeasure.windowGranularity(), this.timeDimensions.find(d => d.granularity).resolvedGranularity()
cumulativeMeasure.windowGranularity(),
dateSeriesGranularity
) || undefined
);
const baseQueryAlias = this.cubeAlias('base');
Expand All @@ -1444,28 +1485,27 @@ export class BaseQuery {
dateSeriesSql,
baseQuery,
dateJoinConditionSql,
baseQueryAlias
baseQueryAlias,
dateSeriesDimension.granularity,
);
}

overTimeSeriesSelect(cumulativeMeasures, dateSeriesSql, baseQuery, dateJoinConditionSql, baseQueryAlias) {
const forSelect = this.overTimeSeriesForSelect(cumulativeMeasures);
overTimeSeriesSelect(cumulativeMeasures, dateSeriesSql, baseQuery, dateJoinConditionSql, baseQueryAlias, dateSeriesGranularity) {
const forSelect = this.overTimeSeriesForSelect(cumulativeMeasures, dateSeriesGranularity);
return `SELECT ${forSelect} FROM ${dateSeriesSql}` +
` LEFT JOIN (${baseQuery}) ${this.asSyntaxJoin} ${baseQueryAlias} ON ${dateJoinConditionSql}` +
this.groupByClause();
}

overTimeSeriesForSelect(cumulativeMeasures) {
return this.dimensions.map(s => s.cumulativeSelectColumns()).concat(this.dateSeriesSelect()).concat(
cumulativeMeasures.map(s => s.cumulativeSelectColumns()),
).filter(c => !!c)
overTimeSeriesForSelect(cumulativeMeasures, dateSeriesGranularity) {
return this.dimensions
.map(s => s.cumulativeSelectColumns())
.concat(this.timeDimensions.map(d => d.dateSeriesSelectColumn(null, dateSeriesGranularity)))
.concat(cumulativeMeasures.map(s => s.cumulativeSelectColumns()))
.filter(c => !!c)
.join(', ');
}

dateSeriesSelect() {
return this.timeDimensions.map(d => d.dateSeriesSelectColumn());
}

dateFromStartToEndConditionSql(dateJoinCondition, fromRollup, isFromStartToEnd) {
return dateJoinCondition.map(
// TODO these weird conversions to be strict typed for big query.
Expand Down Expand Up @@ -1646,7 +1686,8 @@ export class BaseQuery {

/**
*
* @param {{sql: string, on: {cubeName: string, expression: Function}, joinType: 'LEFT' | 'INNER', alias: string}} customJoin
* @param {{sql: string, on: {cubeName: string, expression: Function}, joinType: 'LEFT' | 'INNER', alias: string}}
* customJoin
* @returns {JoinItem}
*/
customSubQueryJoin(customJoin) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,18 @@ export class BaseTimeDimension extends BaseFilter {
return this.query.escapeColumnName(`${this.dimension}_series`);
}

public dateSeriesSelectColumn(dateSeriesAliasName) {
public dateSeriesSelectColumn(dateSeriesAliasName: string, dateSeriesGranularity: string) {
if (!this.granularityObj) {
return null;
}

// In case of query with more than one granularity, the time series table was generated
// with the minimal granularity among all. If this is our granularity, we can save
// some cpu cycles without 'date_from' truncation. But if this is not our granularity,
// we need to truncate it to desired.
if (dateSeriesGranularity && this.granularityObj?.granularity !== dateSeriesGranularity) {
return `${this.query.dimensionTimeGroupedColumn(`${dateSeriesAliasName || this.dateSeriesAliasName()}.${this.query.escapeColumnName('date_from')}`, this.granularityObj)} ${this.aliasName()}`;
}
return `${dateSeriesAliasName || this.dateSeriesAliasName()}.${this.query.escapeColumnName('date_from')} ${this.aliasName()}`;
}

Expand Down

0 comments on commit 22b7243

Please sign in to comment.