Skip to content

Commit 22b7243

Browse files
committed
fix multiple TDs request SQL generation
1 parent 5657e2b commit 22b7243

File tree

2 files changed

+66
-17
lines changed

2 files changed

+66
-17
lines changed

packages/cubejs-schema-compiler/src/adapter/BaseQuery.js

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1413,18 +1413,59 @@ export class BaseQuery {
14131413

14141414
overTimeSeriesQuery(baseQueryFn, cumulativeMeasure, fromRollup) {
14151415
const dateJoinCondition = cumulativeMeasure.dateJoinCondition();
1416+
const uniqDateJoinCondition = R.uniqBy(djc => djc[0].dimension, dateJoinCondition);
14161417
const cumulativeMeasures = [cumulativeMeasure];
14171418
if (!this.timeDimensions.find(d => d.granularity)) {
1418-
const filters = this.segments.concat(this.filters).concat(this.dateFromStartToEndConditionSql(dateJoinCondition, fromRollup, false));
1419+
const filters = this.segments
1420+
.concat(this.filters)
1421+
.concat(this.dateFromStartToEndConditionSql(
1422+
// If the same time dimension is passed more than once, no need to build the same
1423+
// filter condition again and again. Different granularities don't play role here,
1424+
// as rollingWindow.granularity is used for filtering.
1425+
uniqDateJoinCondition,
1426+
fromRollup,
1427+
false
1428+
));
14191429
return baseQueryFn(cumulativeMeasures, filters, false);
14201430
}
1421-
const dateSeriesSql = this.timeDimensions.map(d => this.dateSeriesSql(d)).join(', ');
1422-
const filters = this.segments.concat(this.filters).concat(this.dateFromStartToEndConditionSql(dateJoinCondition, fromRollup, true));
1431+
1432+
// We can't do meaningful query if few time dimensions with different ranges passed,
1433+
// it won't be possible to join them together without loosing some rows.
1434+
const rangedTimeDimensions = this.timeDimensions.filter(d => d.dateRange && d.granularity);
1435+
const uniqTimeDimensionWithRanges = R.uniqBy(d => d.dateRange, rangedTimeDimensions);
1436+
if (uniqTimeDimensionWithRanges.length > 1) {
1437+
throw new Error('Can\'t build query for time dimensions with different date ranges');
1438+
}
1439+
1440+
// We need to generate time series table for the lowest granularity among all time dimensions
1441+
let dateSeriesDimension;
1442+
const dateSeriesGranularity = this.timeDimensions.filter(d => d.granularity)
1443+
.reduce((acc, d) => {
1444+
const mg = this.minGranularity(acc, d.resolvedGranularity());
1445+
if (mg === d.resolvedGranularity()) {
1446+
dateSeriesDimension = d;
1447+
}
1448+
return mg;
1449+
}, undefined);
1450+
1451+
const dateSeriesSql = this.dateSeriesSql(dateSeriesDimension);
1452+
1453+
// If the same time dimension is passed more than once, no need to build the same
1454+
// filter condition again and again. Different granularities don't play role here,
1455+
// as rollingWindow.granularity is used for filtering.
1456+
const filters = this.segments
1457+
.concat(this.filters)
1458+
.concat(this.dateFromStartToEndConditionSql(
1459+
uniqDateJoinCondition,
1460+
fromRollup,
1461+
true
1462+
));
14231463
const baseQuery = this.groupedUngroupedSelect(
14241464
() => baseQueryFn(cumulativeMeasures, filters),
14251465
cumulativeMeasure.shouldUngroupForCumulative(),
14261466
!cumulativeMeasure.shouldUngroupForCumulative() && this.minGranularity(
1427-
cumulativeMeasure.windowGranularity(), this.timeDimensions.find(d => d.granularity).resolvedGranularity()
1467+
cumulativeMeasure.windowGranularity(),
1468+
dateSeriesGranularity
14281469
) || undefined
14291470
);
14301471
const baseQueryAlias = this.cubeAlias('base');
@@ -1444,28 +1485,27 @@ export class BaseQuery {
14441485
dateSeriesSql,
14451486
baseQuery,
14461487
dateJoinConditionSql,
1447-
baseQueryAlias
1488+
baseQueryAlias,
1489+
dateSeriesDimension.granularity,
14481490
);
14491491
}
14501492

1451-
overTimeSeriesSelect(cumulativeMeasures, dateSeriesSql, baseQuery, dateJoinConditionSql, baseQueryAlias) {
1452-
const forSelect = this.overTimeSeriesForSelect(cumulativeMeasures);
1493+
overTimeSeriesSelect(cumulativeMeasures, dateSeriesSql, baseQuery, dateJoinConditionSql, baseQueryAlias, dateSeriesGranularity) {
1494+
const forSelect = this.overTimeSeriesForSelect(cumulativeMeasures, dateSeriesGranularity);
14531495
return `SELECT ${forSelect} FROM ${dateSeriesSql}` +
14541496
` LEFT JOIN (${baseQuery}) ${this.asSyntaxJoin} ${baseQueryAlias} ON ${dateJoinConditionSql}` +
14551497
this.groupByClause();
14561498
}
14571499

1458-
overTimeSeriesForSelect(cumulativeMeasures) {
1459-
return this.dimensions.map(s => s.cumulativeSelectColumns()).concat(this.dateSeriesSelect()).concat(
1460-
cumulativeMeasures.map(s => s.cumulativeSelectColumns()),
1461-
).filter(c => !!c)
1500+
overTimeSeriesForSelect(cumulativeMeasures, dateSeriesGranularity) {
1501+
return this.dimensions
1502+
.map(s => s.cumulativeSelectColumns())
1503+
.concat(this.timeDimensions.map(d => d.dateSeriesSelectColumn(null, dateSeriesGranularity)))
1504+
.concat(cumulativeMeasures.map(s => s.cumulativeSelectColumns()))
1505+
.filter(c => !!c)
14621506
.join(', ');
14631507
}
14641508

1465-
dateSeriesSelect() {
1466-
return this.timeDimensions.map(d => d.dateSeriesSelectColumn());
1467-
}
1468-
14691509
dateFromStartToEndConditionSql(dateJoinCondition, fromRollup, isFromStartToEnd) {
14701510
return dateJoinCondition.map(
14711511
// TODO these weird conversions to be strict typed for big query.
@@ -1646,7 +1686,8 @@ export class BaseQuery {
16461686

16471687
/**
16481688
*
1649-
* @param {{sql: string, on: {cubeName: string, expression: Function}, joinType: 'LEFT' | 'INNER', alias: string}} customJoin
1689+
* @param {{sql: string, on: {cubeName: string, expression: Function}, joinType: 'LEFT' | 'INNER', alias: string}}
1690+
* customJoin
16501691
* @returns {JoinItem}
16511692
*/
16521693
customSubQueryJoin(customJoin) {

packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,18 @@ export class BaseTimeDimension extends BaseFilter {
8585
return this.query.escapeColumnName(`${this.dimension}_series`);
8686
}
8787

88-
public dateSeriesSelectColumn(dateSeriesAliasName) {
88+
public dateSeriesSelectColumn(dateSeriesAliasName: string, dateSeriesGranularity: string) {
8989
if (!this.granularityObj) {
9090
return null;
9191
}
92+
93+
// In case of query with more than one granularity, the time series table was generated
94+
// with the minimal granularity among all. If this is our granularity, we can save
95+
// some cpu cycles without 'date_from' truncation. But if this is not our granularity,
96+
// we need to truncate it to desired.
97+
if (dateSeriesGranularity && this.granularityObj?.granularity !== dateSeriesGranularity) {
98+
return `${this.query.dimensionTimeGroupedColumn(`${dateSeriesAliasName || this.dateSeriesAliasName()}.${this.query.escapeColumnName('date_from')}`, this.granularityObj)} ${this.aliasName()}`;
99+
}
92100
return `${dateSeriesAliasName || this.dateSeriesAliasName()}.${this.query.escapeColumnName('date_from')} ${this.aliasName()}`;
93101
}
94102

0 commit comments

Comments
 (0)