Skip to content

Commit 9c335b4

Browse files
committed
fix multiple TDs request SQL generation
1 parent ec4f4da commit 9c335b4

File tree

2 files changed

+64
-16
lines changed

2 files changed

+64
-16
lines changed

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

Lines changed: 55 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1464,18 +1464,59 @@ export class BaseQuery {
14641464

14651465
overTimeSeriesQuery(baseQueryFn, cumulativeMeasure, fromRollup) {
14661466
const dateJoinCondition = cumulativeMeasure.dateJoinCondition();
1467+
const uniqDateJoinCondition = R.uniqBy(djc => djc[0].dimension, dateJoinCondition);
14671468
const cumulativeMeasures = [cumulativeMeasure];
14681469
if (!this.timeDimensions.find(d => d.granularity)) {
1469-
const filters = this.segments.concat(this.filters).concat(this.dateFromStartToEndConditionSql(dateJoinCondition, fromRollup, false));
1470+
const filters = this.segments
1471+
.concat(this.filters)
1472+
.concat(this.dateFromStartToEndConditionSql(
1473+
// If the same time dimension is passed more than once, no need to build the same
1474+
// filter condition again and again. Different granularities don't play role here,
1475+
// as rollingWindow.granularity is used for filtering.
1476+
uniqDateJoinCondition,
1477+
fromRollup,
1478+
false
1479+
));
14701480
return baseQueryFn(cumulativeMeasures, filters, false);
14711481
}
1472-
const dateSeriesSql = this.timeDimensions.map(d => this.dateSeriesSql(d)).join(', ');
1473-
const filters = this.segments.concat(this.filters).concat(this.dateFromStartToEndConditionSql(dateJoinCondition, fromRollup, true));
1482+
1483+
// We can't do meaningful query if few time dimensions with different ranges passed,
1484+
// it won't be possible to join them together without loosing some rows.
1485+
const rangedTimeDimensions = this.timeDimensions.filter(d => d.dateRange && d.granularity);
1486+
const uniqTimeDimensionWithRanges = R.uniqBy(d => d.dateRange, rangedTimeDimensions);
1487+
if (uniqTimeDimensionWithRanges.length > 1) {
1488+
throw new Error('Can\'t build query for time dimensions with different date ranges');
1489+
}
1490+
1491+
// We need to generate time series table for the lowest granularity among all time dimensions
1492+
let dateSeriesDimension;
1493+
const dateSeriesGranularity = this.timeDimensions.filter(d => d.granularity)
1494+
.reduce((acc, d) => {
1495+
const mg = this.minGranularity(acc, d.resolvedGranularity());
1496+
if (mg === d.resolvedGranularity()) {
1497+
dateSeriesDimension = d;
1498+
}
1499+
return mg;
1500+
}, undefined);
1501+
1502+
const dateSeriesSql = this.dateSeriesSql(dateSeriesDimension);
1503+
1504+
// If the same time dimension is passed more than once, no need to build the same
1505+
// filter condition again and again. Different granularities don't play role here,
1506+
// as rollingWindow.granularity is used for filtering.
1507+
const filters = this.segments
1508+
.concat(this.filters)
1509+
.concat(this.dateFromStartToEndConditionSql(
1510+
uniqDateJoinCondition,
1511+
fromRollup,
1512+
true
1513+
));
14741514
const baseQuery = this.groupedUngroupedSelect(
14751515
() => baseQueryFn(cumulativeMeasures, filters),
14761516
cumulativeMeasure.shouldUngroupForCumulative(),
14771517
!cumulativeMeasure.shouldUngroupForCumulative() && this.minGranularity(
1478-
cumulativeMeasure.windowGranularity(), this.timeDimensions.find(d => d.granularity).resolvedGranularity()
1518+
cumulativeMeasure.windowGranularity(),
1519+
dateSeriesGranularity
14791520
) || undefined
14801521
);
14811522
const baseQueryAlias = this.cubeAlias('base');
@@ -1495,28 +1536,27 @@ export class BaseQuery {
14951536
dateSeriesSql,
14961537
baseQuery,
14971538
dateJoinConditionSql,
1498-
baseQueryAlias
1539+
baseQueryAlias,
1540+
dateSeriesDimension.granularity,
14991541
);
15001542
}
15011543

1502-
overTimeSeriesSelect(cumulativeMeasures, dateSeriesSql, baseQuery, dateJoinConditionSql, baseQueryAlias) {
1503-
const forSelect = this.overTimeSeriesForSelect(cumulativeMeasures);
1544+
overTimeSeriesSelect(cumulativeMeasures, dateSeriesSql, baseQuery, dateJoinConditionSql, baseQueryAlias, dateSeriesGranularity) {
1545+
const forSelect = this.overTimeSeriesForSelect(cumulativeMeasures, dateSeriesGranularity);
15041546
return `SELECT ${forSelect} FROM ${dateSeriesSql}` +
15051547
` LEFT JOIN (${baseQuery}) ${this.asSyntaxJoin} ${baseQueryAlias} ON ${dateJoinConditionSql}` +
15061548
this.groupByClause();
15071549
}
15081550

1509-
overTimeSeriesForSelect(cumulativeMeasures) {
1510-
return this.dimensions.map(s => s.cumulativeSelectColumns()).concat(this.dateSeriesSelect()).concat(
1511-
cumulativeMeasures.map(s => s.cumulativeSelectColumns()),
1512-
).filter(c => !!c)
1551+
overTimeSeriesForSelect(cumulativeMeasures, dateSeriesGranularity) {
1552+
return this.dimensions
1553+
.map(s => s.cumulativeSelectColumns())
1554+
.concat(this.timeDimensions.map(d => d.dateSeriesSelectColumn(null, dateSeriesGranularity)))
1555+
.concat(cumulativeMeasures.map(s => s.cumulativeSelectColumns()))
1556+
.filter(c => !!c)
15131557
.join(', ');
15141558
}
15151559

1516-
dateSeriesSelect() {
1517-
return this.timeDimensions.map(d => d.dateSeriesSelectColumn());
1518-
}
1519-
15201560
dateFromStartToEndConditionSql(dateJoinCondition, fromRollup, isFromStartToEnd) {
15211561
return dateJoinCondition.map(
15221562
// TODO these weird conversions to be strict typed for big query.

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)