From 908ad9c7e62e4dc5cc5df0689d46f4efb7b946a5 Mon Sep 17 00:00:00 2001 From: Courtney Holcomb Date: Mon, 30 Oct 2023 19:16:35 -0700 Subject: [PATCH 01/11] Update metric spec to include join_to_timespine and fill_nulls_with --- metricflow/model/semantics/metric_lookup.py | 2 + metricflow/specs/specs.py | 2 + .../simple_manifest/metrics.yaml | 63 +++++++++++++++++++ 3 files changed, 67 insertions(+) diff --git a/metricflow/model/semantics/metric_lookup.py b/metricflow/model/semantics/metric_lookup.py index d5e4299b6e..92d589f62a 100644 --- a/metricflow/model/semantics/metric_lookup.py +++ b/metricflow/model/semantics/metric_lookup.py @@ -127,6 +127,8 @@ def measures_for_metric( column_association_resolver=column_association_resolver, ).create_from_where_filter_intersection(input_measure.filter), alias=input_measure.alias, + join_to_timespine=input_measure.join_to_timespine, + fill_nulls_with=input_measure.fill_nulls_with, ) input_measure_specs.append(spec) diff --git a/metricflow/specs/specs.py b/metricflow/specs/specs.py index a9c1905460..2b2e8b9544 100644 --- a/metricflow/specs/specs.py +++ b/metricflow/specs/specs.py @@ -463,6 +463,8 @@ class MetricInputMeasureSpec(SerializableDataclass): measure_spec: MeasureSpec constraint: Optional[WhereFilterSpec] = None alias: Optional[str] = None + join_to_timespine: bool = False + fill_nulls_with: Optional[int] = None @property def post_aggregation_spec(self) -> MeasureSpec: diff --git a/metricflow/test/fixtures/semantic_manifest_yamls/simple_manifest/metrics.yaml b/metricflow/test/fixtures/semantic_manifest_yamls/simple_manifest/metrics.yaml index 3ebe4bba47..b954b64895 100644 --- a/metricflow/test/fixtures/semantic_manifest_yamls/simple_manifest/metrics.yaml +++ b/metricflow/test/fixtures/semantic_manifest_yamls/simple_manifest/metrics.yaml @@ -515,3 +515,66 @@ metric: - name: every_two_days_bookers offset_window: 2 days alias: every_2_days_bookers_2_days_ago +--- +metric: + name: bookings_join_to_time_spine + description: simple metric joining to time spine + type: simple + type_params: + measure: + name: bookings + join_to_timespine: true +--- +metric: + name: bookings_fill_0_without_time_spine + description: Simple metric filling 0 without time spine. Not a commonly expected scenario. + type: simple + type_params: + measure: + name: bookings + fill_nulls_with: 0 +--- +metric: + name: bookings_fill_0 + description: simple metric filling nulls with 0 and joining to time spine + type: simple + type_params: + measure: + name: bookings + join_to_timespine: true + fill_nulls_with: 0 +--- +metric: + name: every_two_days_bookers_fill_0 + description: cumulative metric filling 0 + type: cumulative + type_params: + measure: + name: bookers + join_to_timespine: true + fill_nulls_with: 0 + window: 2 days +--- +metric: + name: bookings_growth_2_weeks_fill_0 + description: offset derived metric filling 0 for both input metrics + type: derived + type_params: + expr: bookings_fill_0 - bookings_2_weeks_ago + metrics: + - name: bookings_fill_0 + - name: bookings_fill_0 + offset_window: 14 days + alias: bookings_2_weeks_ago +--- +metric: + name: bookings_growth_2_weeks_fill_0_for_non_offset + description: offset derived metric filling 0 for one input metric, but not the other + type: derived + type_params: + expr: bookings_fill_0 - bookings_2_weeks_ago + metrics: + - name: bookings_fill_0 + - name: bookings + offset_window: 14 days + alias: bookings_2_weeks_ago From 799534a2cc4def93109240263cbc937972002089 Mon Sep 17 00:00:00 2001 From: Courtney Holcomb Date: Mon, 30 Oct 2023 19:18:45 -0700 Subject: [PATCH 02/11] Dataflow plan to join aggregated measures to time spine --- .../dataflow/builder/dataflow_plan_builder.py | 25 +++- metricflow/dataflow/dataflow_plan.py | 25 +++- .../plan_conversion/sql_join_builder.py | 3 +- .../builder/test_dataflow_plan_builder.py | 104 ++++++++++++++ .../test_dataflow_to_sql_plan.py | 14 +- ..._if_no_time_dimension_requested__dfp_0.xml | 44 ++++++ ...in_to_time_spine_derived_metric__dfp_0.xml | 135 ++++++++++++++++++ ..._to_time_spine_with_metric_time__dfp_0.xml | 58 ++++++++ ...time_spine_with_non_metric_time__dfp_0.xml | 58 ++++++++ 9 files changed, 453 insertions(+), 13 deletions(-) create mode 100644 metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_dont_join_to_time_spine_if_no_time_dimension_requested__dfp_0.xml create mode 100644 metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_derived_metric__dfp_0.xml create mode 100644 metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_with_metric_time__dfp_0.xml create mode 100644 metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_with_non_metric_time__dfp_0.xml diff --git a/metricflow/dataflow/builder/dataflow_plan_builder.py b/metricflow/dataflow/builder/dataflow_plan_builder.py index 388c8879d4..565fb09660 100644 --- a/metricflow/dataflow/builder/dataflow_plan_builder.py +++ b/metricflow/dataflow/builder/dataflow_plan_builder.py @@ -745,6 +745,7 @@ def _build_aggregated_measures_from_measure_source_node( if time_dimension_spec.element_name == self._metric_time_dimension_reference.element_name ] metric_time_dimension_requested = len(metric_time_dimension_specs) > 0 + time_dimension_requested = len(queried_linkable_specs.time_dimension_specs) > 0 measure_specs = tuple(x.measure_spec for x in metric_input_measure_specs) measure_properties = self._build_measure_spec_properties(measure_specs) non_additive_dimension_spec = measure_properties.non_additive_dimension_spec @@ -817,13 +818,15 @@ def _build_aggregated_measures_from_measure_source_node( # If querying an offset metric, join to time spine. join_to_time_spine_node: Optional[JoinToTimeSpineNode] = None if metric_spec.offset_window or metric_spec.offset_to_grain: + # TODO: update to accept any time dimensions assert metric_time_dimension_specs, "Joining to time spine requires querying with metric time." join_to_time_spine_node = JoinToTimeSpineNode( parent_node=time_range_node or measure_recipe.source_node, - metric_time_dimension_specs=metric_time_dimension_specs, + time_dimension_specs=metric_time_dimension_specs, time_range_constraint=time_range_constraint, offset_window=metric_spec.offset_window, offset_to_grain=metric_spec.offset_to_grain, + join_type=SqlJoinType.INNER, ) # Only get the required measure and the local linkable instances so that aggregations work correctly. @@ -911,7 +914,25 @@ def _build_aggregated_measures_from_measure_source_node( (InstanceSpecSet(measure_specs=measure_specs), queried_linkable_specs.as_spec_set) ), ) - return AggregateMeasuresNode( + aggregate_measures_node = AggregateMeasuresNode( parent_node=pre_aggregate_node, metric_input_measure_specs=tuple(metric_input_measure_specs), ) + + join_aggregated_measure_to_time_spine = False + for metric_input_measure in metric_input_measure_specs: + if metric_input_measure.join_to_timespine: + join_aggregated_measure_to_time_spine = True + break + + # Only join to time spine if a time dimension was requested in the query. + # TODO: if multiple measures and only some join to time spine, should we aggregate separately? + if join_aggregated_measure_to_time_spine and time_dimension_requested: + return JoinToTimeSpineNode( + parent_node=aggregate_measures_node, + time_dimension_specs=list(queried_linkable_specs.time_dimension_specs), + time_range_constraint=time_range_constraint, + join_type=SqlJoinType.LEFT_OUTER, + ) + else: + return aggregate_measures_node diff --git a/metricflow/dataflow/dataflow_plan.py b/metricflow/dataflow/dataflow_plan.py index a5f1bf9a35..3cb6fa3cfd 100644 --- a/metricflow/dataflow/dataflow_plan.py +++ b/metricflow/dataflow/dataflow_plan.py @@ -700,7 +700,8 @@ class JoinToTimeSpineNode(BaseOutput, ABC): def __init__( self, parent_node: BaseOutput, - metric_time_dimension_specs: List[TimeDimensionSpec], + time_dimension_specs: List[TimeDimensionSpec], + join_type: SqlJoinType, time_range_constraint: Optional[TimeRangeConstraint] = None, offset_window: Optional[MetricTimeWindow] = None, offset_to_grain: Optional[TimeGranularity] = None, @@ -709,7 +710,7 @@ def __init__( Args: parent_node: Node that returns desired dataset to join to time spine. - metric_time_dimension_specs: Metric time dimensions requested in query. Used to determine granularities. + time_dimension_specs: Time dimensions requested in query. Used to determine granularities. time_range_constraint: Time range to constrain the time spine to. offset_window: Time window to offset the parent dataset by when joining to time spine. offset_to_grain: Granularity period to offset the parent dataset to when joining to time spine. @@ -720,10 +721,11 @@ def __init__( offset_window and offset_to_grain ), "Can't set both offset_window and offset_to_grain when joining to time spine. Choose one or the other." self._parent_node = parent_node - self._metric_time_dimension_specs = metric_time_dimension_specs + self._time_dimension_specs = time_dimension_specs self._offset_window = offset_window self._offset_to_grain = offset_to_grain self._time_range_constraint = time_range_constraint + self._join_type = join_type super().__init__(node_id=self.create_unique_id(), parent_nodes=[self._parent_node]) @@ -732,9 +734,9 @@ def id_prefix(cls) -> str: # noqa: D return DATAFLOW_NODE_JOIN_TO_TIME_SPINE_ID_PREFIX @property - def metric_time_dimension_specs(self) -> List[TimeDimensionSpec]: # noqa: D + def time_dimension_specs(self) -> List[TimeDimensionSpec]: # noqa: D """Time dimension specs to use when creating time spine table.""" - return self._metric_time_dimension_specs + return self._time_dimension_specs @property def time_range_constraint(self) -> Optional[TimeRangeConstraint]: # noqa: D @@ -751,6 +753,11 @@ def offset_to_grain(self) -> Optional[TimeGranularity]: # noqa: D """Time range constraint to apply when querying time spine table.""" return self._offset_to_grain + @property + def join_type(self) -> SqlJoinType: # noqa: D + """Join type to use when joining to time spine.""" + return self._join_type + def accept(self, visitor: DataflowPlanNodeVisitor[VisitorOutputT]) -> VisitorOutputT: # noqa: D return visitor.visit_join_to_time_spine_node(self) @@ -761,9 +768,11 @@ def description(self) -> str: # noqa: D @property def displayed_properties(self) -> List[DisplayedProperty]: # noqa: D return super().displayed_properties + [ + DisplayedProperty("time_dimension_specs", self._time_dimension_specs), DisplayedProperty("time_range_constraint", self._time_range_constraint), DisplayedProperty("offset_window", self._offset_window), DisplayedProperty("offset_to_grain", self._offset_to_grain), + DisplayedProperty("join_type", self._join_type), ] @property @@ -776,17 +785,19 @@ def functionally_identical(self, other_node: DataflowPlanNode) -> bool: # noqa: and other_node.time_range_constraint == self.time_range_constraint and other_node.offset_window == self.offset_window and other_node.offset_to_grain == self.offset_to_grain - and other_node.metric_time_dimension_specs == self.metric_time_dimension_specs + and other_node.time_dimension_specs == self.time_dimension_specs + and other_node.join_type == self.join_type ) def with_new_parents(self, new_parent_nodes: Sequence[BaseOutput]) -> JoinToTimeSpineNode: # noqa: D assert len(new_parent_nodes) == 1 return JoinToTimeSpineNode( parent_node=new_parent_nodes[0], - metric_time_dimension_specs=self.metric_time_dimension_specs, + time_dimension_specs=self.time_dimension_specs, time_range_constraint=self.time_range_constraint, offset_window=self.offset_window, offset_to_grain=self.offset_to_grain, + join_type=self.join_type, ) diff --git a/metricflow/plan_conversion/sql_join_builder.py b/metricflow/plan_conversion/sql_join_builder.py index 20b98d6ab5..26b6f5223f 100644 --- a/metricflow/plan_conversion/sql_join_builder.py +++ b/metricflow/plan_conversion/sql_join_builder.py @@ -475,6 +475,7 @@ def make_join_to_time_spine_join_description( metric_time_dimension_column_name: str, parent_sql_select_node: SqlSelectStatementNode, parent_alias: str, + join_type: SqlJoinType, ) -> SqlJoinDescription: """Build join expression used to join a metric to a time spine dataset.""" left_expr: SqlExpressionNode = SqlColumnReferenceExpression( @@ -497,5 +498,5 @@ def make_join_to_time_spine_join_description( col_ref=SqlColumnReference(table_alias=parent_alias, column_name=metric_time_dimension_column_name) ), ), - join_type=SqlJoinType.INNER, + join_type=join_type, ) diff --git a/metricflow/test/dataflow/builder/test_dataflow_plan_builder.py b/metricflow/test/dataflow/builder/test_dataflow_plan_builder.py index 81c6e05726..23081b701e 100644 --- a/metricflow/test/dataflow/builder/test_dataflow_plan_builder.py +++ b/metricflow/test/dataflow/builder/test_dataflow_plan_builder.py @@ -18,6 +18,7 @@ MetricFlowQuerySpec, MetricSpec, OrderBySpec, + TimeDimensionSpec, ) from metricflow.specs.where_filter_transform import WhereSpecFactory from metricflow.test.dataflow_plan_to_svg import display_graph_if_requested @@ -868,3 +869,106 @@ def test_derived_offset_cumulative_metric( # noqa: D mf_test_session_state=mf_test_session_state, dag_graph=dataflow_plan, ) + + +def test_join_to_time_spine_with_metric_time( # noqa: D + request: FixtureRequest, + mf_test_session_state: MetricFlowTestSessionState, + dataflow_plan_builder: DataflowPlanBuilder, +) -> None: + dataflow_plan = dataflow_plan_builder.build_plan( + MetricFlowQuerySpec( + metric_specs=(MetricSpec(element_name="bookings_fill_0"),), + time_dimension_specs=(DataSet.metric_time_dimension_spec(TimeGranularity.DAY),), + ) + ) + + assert_plan_snapshot_text_equal( + request=request, + mf_test_session_state=mf_test_session_state, + plan=dataflow_plan, + plan_snapshot_text=dataflow_plan_as_text(dataflow_plan), + ) + + display_graph_if_requested( + request=request, + mf_test_session_state=mf_test_session_state, + dag_graph=dataflow_plan, + ) + + +def test_join_to_time_spine_derived_metric( # noqa: D + request: FixtureRequest, + mf_test_session_state: MetricFlowTestSessionState, + dataflow_plan_builder: DataflowPlanBuilder, +) -> None: + dataflow_plan = dataflow_plan_builder.build_plan( + MetricFlowQuerySpec( + metric_specs=(MetricSpec(element_name="bookings_growth_2_weeks_fill_0"),), + time_dimension_specs=(DataSet.metric_time_dimension_spec(TimeGranularity.DAY),), + ) + ) + + assert_plan_snapshot_text_equal( + request=request, + mf_test_session_state=mf_test_session_state, + plan=dataflow_plan, + plan_snapshot_text=dataflow_plan_as_text(dataflow_plan), + ) + + display_graph_if_requested( + request=request, + mf_test_session_state=mf_test_session_state, + dag_graph=dataflow_plan, + ) + + +def test_join_to_time_spine_with_non_metric_time( # noqa: D + request: FixtureRequest, + mf_test_session_state: MetricFlowTestSessionState, + dataflow_plan_builder: DataflowPlanBuilder, +) -> None: + dataflow_plan = dataflow_plan_builder.build_plan( + MetricFlowQuerySpec( + metric_specs=(MetricSpec(element_name="bookings_fill_0"),), + time_dimension_specs=( + TimeDimensionSpec(element_name="paid_at", entity_links=(EntityReference("booking"),)), + ), + ) + ) + + assert_plan_snapshot_text_equal( + request=request, + mf_test_session_state=mf_test_session_state, + plan=dataflow_plan, + plan_snapshot_text=dataflow_plan_as_text(dataflow_plan), + ) + + display_graph_if_requested( + request=request, + mf_test_session_state=mf_test_session_state, + dag_graph=dataflow_plan, + ) + + +def test_dont_join_to_time_spine_if_no_time_dimension_requested( # noqa: D + request: FixtureRequest, + mf_test_session_state: MetricFlowTestSessionState, + dataflow_plan_builder: DataflowPlanBuilder, +) -> None: + dataflow_plan = dataflow_plan_builder.build_plan( + MetricFlowQuerySpec(metric_specs=(MetricSpec(element_name="bookings_fill_0"),)) + ) + + assert_plan_snapshot_text_equal( + request=request, + mf_test_session_state=mf_test_session_state, + plan=dataflow_plan, + plan_snapshot_text=dataflow_plan_as_text(dataflow_plan), + ) + + display_graph_if_requested( + request=request, + mf_test_session_state=mf_test_session_state, + dag_graph=dataflow_plan, + ) diff --git a/metricflow/test/plan_conversion/test_dataflow_to_sql_plan.py b/metricflow/test/plan_conversion/test_dataflow_to_sql_plan.py index 6bed5e97ff..f3a17a84e5 100644 --- a/metricflow/test/plan_conversion/test_dataflow_to_sql_plan.py +++ b/metricflow/test/plan_conversion/test_dataflow_to_sql_plan.py @@ -50,6 +50,7 @@ ) from metricflow.specs.where_filter_transform import WhereSpecFactory from metricflow.sql.optimizer.optimization_levels import SqlQueryOptimizationLevel +from metricflow.sql.sql_plan import SqlJoinType from metricflow.test.dataflow_plan_to_svg import display_graph_if_requested from metricflow.test.fixtures.model_fixtures import ConsistentIdObjectRepository from metricflow.test.fixtures.setup_fixtures import MetricFlowTestSessionState @@ -559,10 +560,11 @@ def test_join_to_time_spine_node_without_offset( # noqa: D compute_metrics_node = ComputeMetricsNode(parent_node=aggregated_measures_node, metric_specs=[metric_spec]) join_to_time_spine_node = JoinToTimeSpineNode( parent_node=compute_metrics_node, - metric_time_dimension_specs=[MTD_SPEC_DAY], + time_dimension_specs=[MTD_SPEC_DAY], time_range_constraint=TimeRangeConstraint( start_time=as_datetime("2020-01-01"), end_time=as_datetime("2021-01-01") ), + join_type=SqlJoinType.INNER, ) sink_node = WriteToResultDataframeNode(join_to_time_spine_node) dataflow_plan = DataflowPlan("plan0", sink_output_nodes=[sink_node]) @@ -622,11 +624,12 @@ def test_join_to_time_spine_node_with_offset_window( # noqa: D compute_metrics_node = ComputeMetricsNode(parent_node=aggregated_measures_node, metric_specs=[metric_spec]) join_to_time_spine_node = JoinToTimeSpineNode( parent_node=compute_metrics_node, - metric_time_dimension_specs=[MTD_SPEC_DAY], + time_dimension_specs=[MTD_SPEC_DAY], time_range_constraint=TimeRangeConstraint( start_time=as_datetime("2020-01-01"), end_time=as_datetime("2021-01-01") ), offset_window=PydanticMetricTimeWindow(count=10, granularity=TimeGranularity.DAY), + join_type=SqlJoinType.INNER, ) sink_node = WriteToResultDataframeNode(join_to_time_spine_node) @@ -687,12 +690,13 @@ def test_join_to_time_spine_node_with_offset_to_grain( compute_metrics_node = ComputeMetricsNode(parent_node=aggregated_measures_node, metric_specs=[metric_spec]) join_to_time_spine_node = JoinToTimeSpineNode( parent_node=compute_metrics_node, - metric_time_dimension_specs=[MTD_SPEC_DAY], + time_dimension_specs=[MTD_SPEC_DAY], time_range_constraint=TimeRangeConstraint( start_time=as_datetime("2020-01-01"), end_time=as_datetime("2021-01-01") ), offset_window=None, offset_to_grain=TimeGranularity.MONTH, + join_type=SqlJoinType.INNER, ) sink_node = WriteToResultDataframeNode(join_to_time_spine_node) @@ -1994,3 +1998,7 @@ def test_offset_window_with_date_part( # noqa: D sql_client=sql_client, node=dataflow_plan.sink_output_nodes[0].parent_node, ) + + +def test_stuff() -> None: # noqa: D + assert 0, "write some tests here!!!" diff --git a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_dont_join_to_time_spine_if_no_time_dimension_requested__dfp_0.xml b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_dont_join_to_time_spine_if_no_time_dimension_requested__dfp_0.xml new file mode 100644 index 0000000000..370ce6ce8a --- /dev/null +++ b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_dont_join_to_time_spine_if_no_time_dimension_requested__dfp_0.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_derived_metric__dfp_0.xml b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_derived_metric__dfp_0.xml new file mode 100644 index 0000000000..bf3e06e22d --- /dev/null +++ b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_derived_metric__dfp_0.xml @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_with_metric_time__dfp_0.xml b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_with_metric_time__dfp_0.xml new file mode 100644 index 0000000000..0027136dd6 --- /dev/null +++ b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_with_metric_time__dfp_0.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_with_non_metric_time__dfp_0.xml b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_with_non_metric_time__dfp_0.xml new file mode 100644 index 0000000000..8bbbcd2343 --- /dev/null +++ b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_with_non_metric_time__dfp_0.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 22ab86b1ae168486870fcd67e9c1d406848299a4 Mon Sep 17 00:00:00 2001 From: Courtney Holcomb Date: Mon, 30 Oct 2023 19:21:32 -0700 Subject: [PATCH 03/11] SQL to coalesce nulls --- metricflow/plan_conversion/dataflow_to_sql.py | 60 +++++++++++-------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/metricflow/plan_conversion/dataflow_to_sql.py b/metricflow/plan_conversion/dataflow_to_sql.py index 9b61cdde9d..21c381b1fc 100644 --- a/metricflow/plan_conversion/dataflow_to_sql.py +++ b/metricflow/plan_conversion/dataflow_to_sql.py @@ -5,7 +5,7 @@ from typing import List, Optional, Sequence, Union from dbt_semantic_interfaces.enum_extension import assert_values_exhausted -from dbt_semantic_interfaces.protocols.metric import MetricType +from dbt_semantic_interfaces.protocols.metric import MetricInputMeasure, MetricType from dbt_semantic_interfaces.references import MetricModelReference from dbt_semantic_interfaces.type_enums.aggregation_type import AggregationType @@ -81,6 +81,7 @@ SqlQueryOptimizerConfiguration, ) from metricflow.sql.sql_exprs import ( + SqlAggregateFunctionExpression, SqlBetweenExpression, SqlColumnReference, SqlColumnReferenceExpression, @@ -89,6 +90,7 @@ SqlDateTruncExpression, SqlExpressionNode, SqlExtractExpression, + SqlFunction, SqlFunctionExpression, SqlLogicalExpression, SqlLogicalOperator, @@ -633,6 +635,7 @@ def visit_compute_metrics_node(self, node: ComputeMetricsNode) -> SqlDataSet: metric = self._metric_lookup.get_metric(metric_spec.as_reference) metric_expr: Optional[SqlExpressionNode] = None + input_measure: Optional[MetricInputMeasure] = None if metric.type is MetricType.RATIO: numerator = metric.type_params.numerator denominator = metric.type_params.denominator @@ -664,33 +667,26 @@ def visit_compute_metrics_node(self, node: ComputeMetricsNode) -> SqlDataSet: if len(metric.input_measures) > 0: assert ( len(metric.input_measures) == 1 - ), "Measure proxy metrics should always source from exactly 1 measure." + ), "Simple metrics should always source from exactly 1 measure." + input_measure = metric.input_measures[0] expr = self._column_association_resolver.resolve_spec( - MeasureSpec( - element_name=metric.input_measures[0].post_aggregation_measure_reference.element_name - ) + MeasureSpec(element_name=input_measure.post_aggregation_measure_reference.element_name) ).column_name else: expr = metric.name - # Use a column reference to improve query optimization. - metric_expr = SqlColumnReferenceExpression( - SqlColumnReference( - table_alias=from_data_set_alias, - column_name=expr, - ) + metric_expr = self.__make_col_reference_or_coalesce_expr( + column_name=expr, input_measure=input_measure, from_data_set_alias=from_data_set_alias ) elif metric.type is MetricType.CUMULATIVE: assert ( len(metric.measure_references) == 1 ), "Cumulative metrics should always source from exactly 1 measure." + input_measure = metric.input_measures[0] expr = self._column_association_resolver.resolve_spec( - MeasureSpec(element_name=metric.input_measures[0].post_aggregation_measure_reference.element_name) + MeasureSpec(element_name=input_measure.post_aggregation_measure_reference.element_name) ).column_name - metric_expr = SqlColumnReferenceExpression( - SqlColumnReference( - table_alias=from_data_set_alias, - column_name=expr, - ) + metric_expr = self.__make_col_reference_or_coalesce_expr( + column_name=expr, input_measure=input_measure, from_data_set_alias=from_data_set_alias ) elif metric.type is MetricType.DERIVED: assert metric.type_params.expr @@ -734,6 +730,21 @@ def visit_compute_metrics_node(self, node: ComputeMetricsNode) -> SqlDataSet: ), ) + def __make_col_reference_or_coalesce_expr( + self, column_name: str, input_measure: Optional[MetricInputMeasure], from_data_set_alias: str + ) -> SqlExpressionNode: + # Use a column reference to improve query optimization. + metric_expr: SqlExpressionNode = SqlColumnReferenceExpression( + SqlColumnReference(table_alias=from_data_set_alias, column_name=column_name) + ) + # Coalesce nulls to requested integer value, if requested. + if input_measure and input_measure.fill_nulls_with is not None: + metric_expr = SqlAggregateFunctionExpression( + sql_function=SqlFunction.COALESCE, + sql_function_args=[metric_expr, SqlStringExpression(str(input_measure.fill_nulls_with))], + ) + return metric_expr + def visit_order_by_limit_node(self, node: OrderByLimitNode) -> SqlDataSet: # noqa: D from_data_set: SqlDataSet = node.parent_node.accept(self) output_instance_set = from_data_set.instance_set @@ -1331,6 +1342,7 @@ def visit_join_to_time_spine_node(self, node: JoinToTimeSpineNode) -> SqlDataSet metric_time_dimension_column_name=metric_time_dimension_column_name, parent_sql_select_node=parent_data_set.sql_select_node, parent_alias=parent_alias, + join_type=node.join_type, ) # Use all instances EXCEPT metric_time from parent data set. @@ -1366,13 +1378,13 @@ def visit_join_to_time_spine_node(self, node: JoinToTimeSpineNode) -> SqlDataSet metric_time_select_columns = [] metric_time_dimension_instances = [] where: Optional[SqlExpressionNode] = None - for metric_time_dimension_spec in node.metric_time_dimension_specs: + for time_dimension_spec in node.time_dimension_specs: # Apply granularity to SQL. - if metric_time_dimension_spec.time_granularity == self._time_spine_source.time_column_granularity: + if time_dimension_spec.time_granularity == self._time_spine_source.time_column_granularity: select_expr: SqlExpressionNode = time_spine_column_select_expr else: select_expr = SqlDateTruncExpression( - time_granularity=metric_time_dimension_spec.time_granularity, arg=time_spine_column_select_expr + time_granularity=time_dimension_spec.time_granularity, arg=time_spine_column_select_expr ) if node.offset_to_grain: # Filter down to one row per granularity period @@ -1384,13 +1396,13 @@ def visit_join_to_time_spine_node(self, node: JoinToTimeSpineNode) -> SqlDataSet else: where = SqlLogicalExpression(operator=SqlLogicalOperator.OR, args=(where, new_filter)) # Apply date_part to SQL. - if metric_time_dimension_spec.date_part: - select_expr = SqlExtractExpression(date_part=metric_time_dimension_spec.date_part, arg=select_expr) + if time_dimension_spec.date_part: + select_expr = SqlExtractExpression(date_part=time_dimension_spec.date_part, arg=select_expr) time_dim_spec = TimeDimensionSpec( element_name=time_dim_instance.spec.element_name, entity_links=time_dim_instance.spec.entity_links, - time_granularity=metric_time_dimension_spec.time_granularity, - date_part=metric_time_dimension_spec.date_part, + time_granularity=time_dimension_spec.time_granularity, + date_part=time_dimension_spec.date_part, aggregation_state=time_dim_instance.spec.aggregation_state, ) time_dim_instance = TimeDimensionInstance( From 7b8105cf1c38e1ec4b05bffd26c8792cb35f0dc7 Mon Sep 17 00:00:00 2001 From: Courtney Holcomb Date: Mon, 30 Oct 2023 19:22:11 -0700 Subject: [PATCH 04/11] WIP - write integration test cases --- .../integration/test_cases/itest_metrics.yaml | 164 ++++++++++++++++++ 1 file changed, 164 insertions(+) diff --git a/metricflow/test/integration/test_cases/itest_metrics.yaml b/metricflow/test/integration/test_cases/itest_metrics.yaml index 14530dfaf3..4278f6796e 100644 --- a/metricflow/test/integration/test_cases/itest_metrics.yaml +++ b/metricflow/test/integration/test_cases/itest_metrics.yaml @@ -1127,3 +1127,167 @@ integration_test: , {{ render_extract("ds", DatePart.DOW) }} AS metric_time__extract_dow FROM {{ source_schema }}.fct_bookings GROUP BY {{ render_extract("ds", DatePart.DOW) }}; +--- +integration_test: + name: simple_fill_nulls_with_0_metric_time + description: Test a simple metric that joins to time spine with metric time + model: SIMPLE_MODEL + metrics: ["bookings_fill_0"] + group_by_objs: [{"name": "metric_time"}] + check_query: | + SELECT 1 +--- +integration_test: + name: simple_fill_nulls_with_0_month + description: Test a simple metric that joins to time spine with metric time + model: SIMPLE_MODEL + metrics: ["bookings_fill_0"] + group_by_objs: [{"name": "metric_time", "grain": "month"}] + check_query: | + SELECT 1 +--- +integration_test: + name: simple_fill_nulls_with_0_non_metric_time + description: Test a simple metric that joins to time spine with time dim that's not metric time + model: SIMPLE_MODEL + metrics: ["bookings_fill_0"] + group_by_objs: [{"name": "booking__paid_at"}] + check_query: | + SELECT 1 +--- +integration_test: + name: simple_should_not_join_to_time_spine + description: Test a simple metric that normally joins to time spine, but shouldn't if no time dim is requested + model: SIMPLE_MODEL + metrics: ["bookings_fill_0"] + group_by_objs: [{"name": "booking__is_instant"}] + check_query: | + SELECT + booking__is_instant + , COALESCE(bookings, 0) AS bookings_fill_0 + FROM ( + SELECT + is_instant AS booking__is_instant + , SUM(1) AS bookings + FROM {{ source_schema }}.fct_bookings bookings_source_src_1 + GROUP BY + booking__is_instant + ) +--- +integration_test: + name: simple_join_to_time_spine + description: Test a simple metric that joins to time spine, but doesn't fill nulls + model: SIMPLE_MODEL + metrics: ["bookings_join_to_time_spine"] + group_by_objs: [{"name": "metric_time"}] + check_query: | + SELECT + subq_5.ds AS metric_time__day + , subq_3.bookings AS bookings_join_to_time_spine + FROM {{ source_schema }}.mf_time_spine subq_5 + LEFT JOIN ( + SELECT + DATE_TRUNC('day', ds) AS metric_time__day + , SUM(1) AS bookings + FROM {{ source_schema }}.fct_bookings bookings_source_src_1 + GROUP BY + metric_time__day + ) subq_3 + ON + subq_5.ds = subq_3.metric_time__day +--- +integration_test: + name: simple_fill_nulls_without_time_spine + description: Test a simple metric that fills nulls, but doesn't join to time spine + model: SIMPLE_MODEL + metrics: ["bookings_fill_0_without_time_spine"] + group_by_objs: [{"name": "metric_time"}] + check_query: | + SELECT + metric_time__day + , COALESCE(bookings, 0) AS bookings_fill_0_without_time_spine + FROM ( + SELECT + DATE_TRUNC('day', ds) AS metric_time__day + , SUM(1) AS bookings + FROM {{ source_schema }}.fct_bookings bookings_source_src_1 + GROUP BY + metric_time__day + ) +--- +integration_test: + name: cumulative_fill_nulls + description: Test a cumulative metric that fills nulls + model: SIMPLE_MODEL + metrics: ["every_two_days_bookers_fill_0"] + group_by_objs: [{"name": "metric_time"}] + check_query: | + SELECT + metric_time__day + , COALESCE(bookers, 0) AS every_two_days_bookers_fill_0 + FROM ( + SELECT + subq_8.ds AS metric_time__day + , subq_6.bookers AS bookers + FROM {{ source_schema }}.mf_time_spine subq_8 + LEFT JOIN ( + SELECT + subq_3.ds AS metric_time__day + , COUNT(DISTINCT bookings_source_src_1.guest_id) AS bookers + FROM {{ source_schema }}.mf_time_spine subq_3 + INNER JOIN + {{ source_schema }}.fct_bookings bookings_source_src_1 + ON (DATE_TRUNC('day', bookings_source_src_1.ds) <= subq_3.ds) + AND (DATE_TRUNC('day', bookings_source_src_1.ds) > subq_3.ds - INTERVAL 2 day) + GROUP BY + subq_3.ds + ) subq_6 + ON subq_8.ds = subq_6.metric_time__day + ) subq_9 +--- +integration_test: + name: derived_fill_nulls_one_input_metric + description: Test a derived metric that fills nulls on one input metric but not the other + model: SIMPLE_MODEL + metrics: ["bookings_growth_2_weeks_fill_0_for_non_offset"] + group_by_objs: [{"name": "metric_time"}] + check_query: | + SELECT + metric_time__day + , bookings_fill_0 - bookings_2_weeks_ago AS bookings_growth_2_weeks_fill_0_for_non_offset + FROM ( + SELECT + subq_7.metric_time__day + , subq_7.bookings_fill_0 AS bookings_fill_0 + , subq_15.bookings_2_weeks_ago AS bookings_2_weeks_ago + FROM ( + SELECT + subq_5.ds AS metric_time__day + , COALESCE(subq_3.bookings, 0) AS bookings_fill_0 + FROM {{ source_schema }}.mf_time_spine subq_5 + LEFT JOIN ( + SELECT + DATE_TRUNC('day', ds) AS metric_time__day + , SUM(1) AS bookings + FROM {{ source_schema }}.fct_bookings bookings_source_src_1 + GROUP BY + metric_time__day + ) subq_3 + ON subq_5.ds = subq_3.metric_time__day + ) subq_7 + INNER JOIN ( + SELECT + subq_11.ds AS metric_time__day + , SUM(subq_9.bookings) AS bookings_2_weeks_ago + FROM {{ source_schema }}.mf_time_spine subq_11 + INNER JOIN ( + SELECT + DATE_TRUNC('day', ds) AS metric_time__day + , 1 AS bookings + FROM {{ source_schema }}.fct_bookings bookings_source_src_1 + ) subq_9 + ON subq_11.ds - INTERVAL 14 day = subq_9.metric_time__day + GROUP BY subq_11.ds + ) subq_15 + ON subq_7.metric_time__day = subq_15.metric_time__day + ) subq_16 From 7d3e57bfae6a4f00f09ca425f797dc190b99213a Mon Sep 17 00:00:00 2001 From: Courtney Holcomb Date: Tue, 31 Oct 2023 10:25:01 -0700 Subject: [PATCH 05/11] Update dateaflow plan snapshots --- ..._derived_metric_offset_to_grain__dfp_0.xml | 8 +++++++ ...st_derived_metric_offset_window__dfp_0.xml | 8 +++++++ ..._metric_offset_with_granularity__dfp_0.xml | 8 +++++++ ...erived_offset_cumulative_metric__dfp_0.xml | 8 +++++++ ...in_to_time_spine_derived_metric__dfp_0.xml | 24 +++++++++++++++++++ ..._to_time_spine_with_metric_time__dfp_0.xml | 8 +++++++ ...time_spine_with_non_metric_time__dfp_0.xml | 8 +++++++ ...spine_node_with_offset_to_grain__plan0.xml | 8 +++++++ ...e_spine_node_with_offset_window__plan0.xml | 8 +++++++ ..._time_spine_node_without_offset__plan0.xml | 8 +++++++ 10 files changed, 96 insertions(+) diff --git a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_to_grain__dfp_0.xml b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_to_grain__dfp_0.xml index 7704ab793b..f06ee9427e 100644 --- a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_to_grain__dfp_0.xml +++ b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_to_grain__dfp_0.xml @@ -94,9 +94,17 @@ + + + + + + + + diff --git a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_window__dfp_0.xml b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_window__dfp_0.xml index bedaa4a5af..1538565dde 100644 --- a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_window__dfp_0.xml +++ b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_window__dfp_0.xml @@ -47,9 +47,17 @@ + + + + + + + + diff --git a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_with_granularity__dfp_0.xml b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_with_granularity__dfp_0.xml index 72074aa37a..c4994be8e6 100644 --- a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_with_granularity__dfp_0.xml +++ b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_with_granularity__dfp_0.xml @@ -47,9 +47,17 @@ + + + + + + + + diff --git a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_offset_cumulative_metric__dfp_0.xml b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_offset_cumulative_metric__dfp_0.xml index 1f10a03b3f..279469e982 100644 --- a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_offset_cumulative_metric__dfp_0.xml +++ b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_offset_cumulative_metric__dfp_0.xml @@ -47,9 +47,17 @@ + + + + + + + + diff --git a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_derived_metric__dfp_0.xml b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_derived_metric__dfp_0.xml index bf3e06e22d..6ba06819d1 100644 --- a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_derived_metric__dfp_0.xml +++ b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_derived_metric__dfp_0.xml @@ -29,9 +29,17 @@ + + + + + + + + @@ -83,9 +91,17 @@ + + + + + + + + @@ -109,9 +125,17 @@ + + + + + + + + diff --git a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_with_metric_time__dfp_0.xml b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_with_metric_time__dfp_0.xml index 0027136dd6..3589bc91a1 100644 --- a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_with_metric_time__dfp_0.xml +++ b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_with_metric_time__dfp_0.xml @@ -15,9 +15,17 @@ + + + + + + + + diff --git a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_with_non_metric_time__dfp_0.xml b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_with_non_metric_time__dfp_0.xml index 8bbbcd2343..a6fccdc15f 100644 --- a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_with_non_metric_time__dfp_0.xml +++ b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_with_non_metric_time__dfp_0.xml @@ -15,9 +15,17 @@ + + + + + + + + diff --git a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_with_offset_to_grain__plan0.xml b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_with_offset_to_grain__plan0.xml index 4a4e2a24f4..bdd3bf94e0 100644 --- a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_with_offset_to_grain__plan0.xml +++ b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_with_offset_to_grain__plan0.xml @@ -5,9 +5,17 @@ + + + + + + + + diff --git a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_with_offset_window__plan0.xml b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_with_offset_window__plan0.xml index b0c2d678ce..0ef78381ba 100644 --- a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_with_offset_window__plan0.xml +++ b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_with_offset_window__plan0.xml @@ -5,9 +5,17 @@ + + + + + + + + diff --git a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_without_offset__plan0.xml b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_without_offset__plan0.xml index 5a31f12687..5c67156c62 100644 --- a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_without_offset__plan0.xml +++ b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_without_offset__plan0.xml @@ -5,9 +5,17 @@ + + + + + + + + From f2d99805fd7310c8e2eab823ff41530bd9031516 Mon Sep 17 00:00:00 2001 From: Courtney Holcomb Date: Tue, 31 Oct 2023 11:23:24 -0700 Subject: [PATCH 06/11] Update JoinToTimeSpineNode to allow non-metric time dimension --- metricflow/plan_conversion/dataflow_to_sql.py | 72 +++++++++++-------- .../plan_conversion/sql_join_builder.py | 6 +- 2 files changed, 47 insertions(+), 31 deletions(-) diff --git a/metricflow/plan_conversion/dataflow_to_sql.py b/metricflow/plan_conversion/dataflow_to_sql.py index 21c381b1fc..1f314eaa78 100644 --- a/metricflow/plan_conversion/dataflow_to_sql.py +++ b/metricflow/plan_conversion/dataflow_to_sql.py @@ -164,8 +164,8 @@ def _next_unique_table_alias(self) -> str: def _make_time_spine_data_set( self, - metric_time_dimension_instance: TimeDimensionInstance, - metric_time_dimension_column_name: str, + time_dimension_instance: TimeDimensionInstance, + time_dimension_column_name: str, time_spine_source: TimeSpineSource, time_range_constraint: Optional[TimeRangeConstraint] = None, ) -> SqlDataSet: @@ -176,14 +176,14 @@ def _make_time_spine_data_set( """ time_spine_instance = ( TimeDimensionInstance( - defined_from=metric_time_dimension_instance.defined_from, + defined_from=time_dimension_instance.defined_from, associated_columns=( ColumnAssociation( - column_name=metric_time_dimension_column_name, + column_name=time_dimension_column_name, single_column_correlation_key=SingleColumnCorrelationKey(), ), ), - spec=metric_time_dimension_instance.spec, + spec=time_dimension_instance.spec, ), ) time_spine_instance_set = InstanceSet( @@ -193,7 +193,7 @@ def _make_time_spine_data_set( time_spine_table_alias = self._next_unique_table_alias() # If the requested granularity is the same as the granularity of the spine, do a direct select. - if metric_time_dimension_instance.spec.time_granularity == time_spine_source.time_column_granularity: + if time_dimension_instance.spec.time_granularity == time_spine_source.time_column_granularity: return SqlDataSet( instance_set=time_spine_instance_set, sql_select_node=SqlSelectStatementNode( @@ -207,7 +207,7 @@ def _make_time_spine_data_set( column_name=time_spine_source.time_column_name, ), ), - column_alias=metric_time_dimension_column_name, + column_alias=time_dimension_column_name, ), ), from_source=SqlTableFromClauseNode(sql_table=time_spine_source.spine_table), @@ -229,7 +229,7 @@ def _make_time_spine_data_set( select_columns = ( SqlSelectColumn( expr=SqlDateTruncExpression( - time_granularity=metric_time_dimension_instance.spec.time_granularity, + time_granularity=time_dimension_instance.spec.time_granularity, arg=SqlColumnReferenceExpression( SqlColumnReference( table_alias=time_spine_table_alias, @@ -237,7 +237,7 @@ def _make_time_spine_data_set( ), ), ), - column_alias=metric_time_dimension_column_name, + column_alias=time_dimension_column_name, ), ) return SqlDataSet( @@ -294,8 +294,8 @@ def visit_join_over_time_range_node(self, node: JoinOverTimeRangeNode) -> SqlDat # Granularity of time_spine column should match granularity of metric_time column from parent dataset. assert metric_time_dimension_instance time_spine_data_set = self._make_time_spine_data_set( - metric_time_dimension_instance=metric_time_dimension_instance, - metric_time_dimension_column_name=metric_time_dimension_column_name, + time_dimension_instance=metric_time_dimension_instance, + time_dimension_column_name=metric_time_dimension_column_name, time_spine_source=self._time_spine_source, time_range_constraint=node.time_range_constraint, ) @@ -1312,25 +1312,40 @@ def visit_join_to_time_spine_node(self, node: JoinToTimeSpineNode) -> SqlDataSet parent_data_set = node.parent_node.accept(self) parent_alias = self._next_unique_table_alias() - # Build time spine dataset + # Validate time dimensions & choose the one to join time spine onto. + time_dimension_instance_to_join: Optional[TimeDimensionInstance] = None metric_time_dimension_instance: Optional[TimeDimensionInstance] = None + # If metric_time is requested, choose the one with the lowest granularity. for instance in parent_data_set.metric_time_dimension_instances: - if len(instance.spec.entity_links) == 0: - # Use the instance with the lowest granularity - if not metric_time_dimension_instance or ( - instance.spec.time_granularity < metric_time_dimension_instance.spec.time_granularity + if not metric_time_dimension_instance or ( + instance.spec.time_granularity < metric_time_dimension_instance.spec.time_granularity + ): + metric_time_dimension_instance = instance + if node.offset_window or node.offset_to_grain: + assert ( + metric_time_dimension_instance + ), "Can't query offset metric without metric time. Validations should have prevented this." + + # If there were no metric_time dimensions requested, choose the time dimension with the lowest granularity. + time_dimension_instance_to_join = metric_time_dimension_instance + if not time_dimension_instance_to_join: + for instance in parent_data_set.instance_set.time_dimension_instances: + if not time_dimension_instance_to_join or ( + instance.spec.time_granularity < time_dimension_instance_to_join.spec.time_granularity ): - metric_time_dimension_instance = instance + time_dimension_instance_to_join = instance assert ( - metric_time_dimension_instance - ), "Can't query offset metric without a time dimension. Validations should have prevented this." - metric_time_dimension_column_name = self.column_association_resolver.resolve_spec( - metric_time_dimension_instance.spec + time_dimension_instance_to_join # TODO: update validations to prevent this lol + ), "Can't join to time spine without a time dimension. Validations should have prevented this." + + # Build time spine dataset + time_dimension_column_name = self.column_association_resolver.resolve_spec( + time_dimension_instance_to_join.spec ).column_name time_spine_alias = self._next_unique_table_alias() time_spine_dataset = self._make_time_spine_data_set( - metric_time_dimension_instance=metric_time_dimension_instance, - metric_time_dimension_column_name=metric_time_dimension_column_name, + time_dimension_instance=time_dimension_instance_to_join, + time_dimension_column_name=time_dimension_column_name, time_spine_source=self._time_spine_source, time_range_constraint=node.time_range_constraint, ) @@ -1339,19 +1354,20 @@ def visit_join_to_time_spine_node(self, node: JoinToTimeSpineNode) -> SqlDataSet join_description = SqlQueryPlanJoinBuilder.make_join_to_time_spine_join_description( node=node, time_spine_alias=time_spine_alias, - metric_time_dimension_column_name=metric_time_dimension_column_name, + time_dimension_column_name=time_dimension_column_name, parent_sql_select_node=parent_data_set.sql_select_node, parent_alias=parent_alias, join_type=node.join_type, ) - # Use all instances EXCEPT metric_time from parent data set. - non_metric_time_parent_instance_set = InstanceSet( + # Use all instances EXCEPT joined time dimension from parent data set. + parent_instance_set_to_keep = InstanceSet( measure_instances=parent_data_set.instance_set.measure_instances, dimension_instances=parent_data_set.instance_set.dimension_instances, time_dimension_instances=tuple( time_dimension_instance for time_dimension_instance in parent_data_set.instance_set.time_dimension_instances + # TODO: replace logic below - remove the one selected. maybe do that logic above if time_dimension_instance.spec.element_name != DataSet.metric_time_dimension_reference().element_name ), entity_instances=parent_data_set.instance_set.entity_instances, @@ -1359,7 +1375,7 @@ def visit_join_to_time_spine_node(self, node: JoinToTimeSpineNode) -> SqlDataSet metadata_instances=parent_data_set.instance_set.metadata_instances, ) non_metric_time_select_columns = create_select_columns_for_instance_sets( - self._column_association_resolver, OrderedDict({parent_alias: non_metric_time_parent_instance_set}) + self._column_association_resolver, OrderedDict({parent_alias: parent_instance_set_to_keep}) ) # Use metric_time column from time spine. @@ -1417,7 +1433,7 @@ def visit_join_to_time_spine_node(self, node: JoinToTimeSpineNode) -> SqlDataSet metric_time_instance_set = InstanceSet(time_dimension_instances=tuple(metric_time_dimension_instances)) return SqlDataSet( - instance_set=InstanceSet.merge([metric_time_instance_set, non_metric_time_parent_instance_set]), + instance_set=InstanceSet.merge([metric_time_instance_set, parent_instance_set_to_keep]), sql_select_node=SqlSelectStatementNode( description=node.description, select_columns=tuple(metric_time_select_columns) + non_metric_time_select_columns, diff --git a/metricflow/plan_conversion/sql_join_builder.py b/metricflow/plan_conversion/sql_join_builder.py index 26b6f5223f..ee8c5ae00d 100644 --- a/metricflow/plan_conversion/sql_join_builder.py +++ b/metricflow/plan_conversion/sql_join_builder.py @@ -472,14 +472,14 @@ def make_cumulative_metric_time_range_join_description( def make_join_to_time_spine_join_description( node: JoinToTimeSpineNode, time_spine_alias: str, - metric_time_dimension_column_name: str, + time_dimension_column_name: str, parent_sql_select_node: SqlSelectStatementNode, parent_alias: str, join_type: SqlJoinType, ) -> SqlJoinDescription: """Build join expression used to join a metric to a time spine dataset.""" left_expr: SqlExpressionNode = SqlColumnReferenceExpression( - col_ref=SqlColumnReference(table_alias=time_spine_alias, column_name=metric_time_dimension_column_name) + col_ref=SqlColumnReference(table_alias=time_spine_alias, column_name=time_dimension_column_name) ) if node.offset_window: left_expr = SqlSubtractTimeIntervalExpression( @@ -495,7 +495,7 @@ def make_join_to_time_spine_join_description( left_expr=left_expr, comparison=SqlComparison.EQUALS, right_expr=SqlColumnReferenceExpression( - col_ref=SqlColumnReference(table_alias=parent_alias, column_name=metric_time_dimension_column_name) + col_ref=SqlColumnReference(table_alias=parent_alias, column_name=time_dimension_column_name) ), ), join_type=join_type, From 15cdea64ec17c7d1f3ed9d6805ba500038e25f31 Mon Sep 17 00:00:00 2001 From: Courtney Holcomb Date: Tue, 31 Oct 2023 13:36:18 -0700 Subject: [PATCH 07/11] Revert to only using metric time --- .../dataflow/builder/dataflow_plan_builder.py | 11 ++- metricflow/dataflow/dataflow_plan.py | 16 ++-- metricflow/plan_conversion/dataflow_to_sql.py | 74 +++++++---------- .../plan_conversion/sql_join_builder.py | 6 +- .../integration/test_cases/itest_metrics.yaml | 2 +- .../test/integration/test_configured_cases.py | 2 +- .../test_dataflow_to_sql_plan.py | 6 +- ..._derived_metric_offset_to_grain__dfp_0.xml | 2 +- ...st_derived_metric_offset_window__dfp_0.xml | 2 +- ..._metric_offset_with_granularity__dfp_0.xml | 2 +- ...erived_offset_cumulative_metric__dfp_0.xml | 2 +- ...in_to_time_spine_derived_metric__dfp_0.xml | 6 +- ..._to_time_spine_with_metric_time__dfp_0.xml | 2 +- ...time_spine_with_non_metric_time__dfp_0.xml | 83 ++++++++----------- ...spine_node_with_offset_to_grain__plan0.xml | 2 +- ...e_spine_node_with_offset_window__plan0.xml | 2 +- ..._time_spine_node_without_offset__plan0.xml | 2 +- 17 files changed, 95 insertions(+), 127 deletions(-) diff --git a/metricflow/dataflow/builder/dataflow_plan_builder.py b/metricflow/dataflow/builder/dataflow_plan_builder.py index 565fb09660..f4cf805ac6 100644 --- a/metricflow/dataflow/builder/dataflow_plan_builder.py +++ b/metricflow/dataflow/builder/dataflow_plan_builder.py @@ -745,7 +745,6 @@ def _build_aggregated_measures_from_measure_source_node( if time_dimension_spec.element_name == self._metric_time_dimension_reference.element_name ] metric_time_dimension_requested = len(metric_time_dimension_specs) > 0 - time_dimension_requested = len(queried_linkable_specs.time_dimension_specs) > 0 measure_specs = tuple(x.measure_spec for x in metric_input_measure_specs) measure_properties = self._build_measure_spec_properties(measure_specs) non_additive_dimension_spec = measure_properties.non_additive_dimension_spec @@ -818,11 +817,10 @@ def _build_aggregated_measures_from_measure_source_node( # If querying an offset metric, join to time spine. join_to_time_spine_node: Optional[JoinToTimeSpineNode] = None if metric_spec.offset_window or metric_spec.offset_to_grain: - # TODO: update to accept any time dimensions assert metric_time_dimension_specs, "Joining to time spine requires querying with metric time." join_to_time_spine_node = JoinToTimeSpineNode( parent_node=time_range_node or measure_recipe.source_node, - time_dimension_specs=metric_time_dimension_specs, + metric_time_dimension_specs=metric_time_dimension_specs, time_range_constraint=time_range_constraint, offset_window=metric_spec.offset_window, offset_to_grain=metric_spec.offset_to_grain, @@ -925,12 +923,13 @@ def _build_aggregated_measures_from_measure_source_node( join_aggregated_measure_to_time_spine = True break - # Only join to time spine if a time dimension was requested in the query. # TODO: if multiple measures and only some join to time spine, should we aggregate separately? - if join_aggregated_measure_to_time_spine and time_dimension_requested: + # TODO: what is time range constraint here? does it need to be smaller? + # Only join to time spine if metric time was requested in the query. + if join_aggregated_measure_to_time_spine and metric_time_dimension_requested: return JoinToTimeSpineNode( parent_node=aggregate_measures_node, - time_dimension_specs=list(queried_linkable_specs.time_dimension_specs), + metric_time_dimension_specs=metric_time_dimension_specs, time_range_constraint=time_range_constraint, join_type=SqlJoinType.LEFT_OUTER, ) diff --git a/metricflow/dataflow/dataflow_plan.py b/metricflow/dataflow/dataflow_plan.py index 3cb6fa3cfd..e9843b7b95 100644 --- a/metricflow/dataflow/dataflow_plan.py +++ b/metricflow/dataflow/dataflow_plan.py @@ -700,7 +700,7 @@ class JoinToTimeSpineNode(BaseOutput, ABC): def __init__( self, parent_node: BaseOutput, - time_dimension_specs: List[TimeDimensionSpec], + metric_time_dimension_specs: List[TimeDimensionSpec], join_type: SqlJoinType, time_range_constraint: Optional[TimeRangeConstraint] = None, offset_window: Optional[MetricTimeWindow] = None, @@ -710,7 +710,7 @@ def __init__( Args: parent_node: Node that returns desired dataset to join to time spine. - time_dimension_specs: Time dimensions requested in query. Used to determine granularities. + metric_time_dimension_specs: Time dimensions requested in query. Used to determine granularities. time_range_constraint: Time range to constrain the time spine to. offset_window: Time window to offset the parent dataset by when joining to time spine. offset_to_grain: Granularity period to offset the parent dataset to when joining to time spine. @@ -721,7 +721,7 @@ def __init__( offset_window and offset_to_grain ), "Can't set both offset_window and offset_to_grain when joining to time spine. Choose one or the other." self._parent_node = parent_node - self._time_dimension_specs = time_dimension_specs + self._metric_time_dimension_specs = metric_time_dimension_specs self._offset_window = offset_window self._offset_to_grain = offset_to_grain self._time_range_constraint = time_range_constraint @@ -734,9 +734,9 @@ def id_prefix(cls) -> str: # noqa: D return DATAFLOW_NODE_JOIN_TO_TIME_SPINE_ID_PREFIX @property - def time_dimension_specs(self) -> List[TimeDimensionSpec]: # noqa: D + def metric_time_dimension_specs(self) -> List[TimeDimensionSpec]: # noqa: D """Time dimension specs to use when creating time spine table.""" - return self._time_dimension_specs + return self._metric_time_dimension_specs @property def time_range_constraint(self) -> Optional[TimeRangeConstraint]: # noqa: D @@ -768,7 +768,7 @@ def description(self) -> str: # noqa: D @property def displayed_properties(self) -> List[DisplayedProperty]: # noqa: D return super().displayed_properties + [ - DisplayedProperty("time_dimension_specs", self._time_dimension_specs), + DisplayedProperty("metric_time_dimension_specs", self._metric_time_dimension_specs), DisplayedProperty("time_range_constraint", self._time_range_constraint), DisplayedProperty("offset_window", self._offset_window), DisplayedProperty("offset_to_grain", self._offset_to_grain), @@ -785,7 +785,7 @@ def functionally_identical(self, other_node: DataflowPlanNode) -> bool: # noqa: and other_node.time_range_constraint == self.time_range_constraint and other_node.offset_window == self.offset_window and other_node.offset_to_grain == self.offset_to_grain - and other_node.time_dimension_specs == self.time_dimension_specs + and other_node.metric_time_dimension_specs == self.metric_time_dimension_specs and other_node.join_type == self.join_type ) @@ -793,7 +793,7 @@ def with_new_parents(self, new_parent_nodes: Sequence[BaseOutput]) -> JoinToTime assert len(new_parent_nodes) == 1 return JoinToTimeSpineNode( parent_node=new_parent_nodes[0], - time_dimension_specs=self.time_dimension_specs, + metric_time_dimension_specs=self.metric_time_dimension_specs, time_range_constraint=self.time_range_constraint, offset_window=self.offset_window, offset_to_grain=self.offset_to_grain, diff --git a/metricflow/plan_conversion/dataflow_to_sql.py b/metricflow/plan_conversion/dataflow_to_sql.py index 1f314eaa78..c4b5e8a216 100644 --- a/metricflow/plan_conversion/dataflow_to_sql.py +++ b/metricflow/plan_conversion/dataflow_to_sql.py @@ -164,8 +164,8 @@ def _next_unique_table_alias(self) -> str: def _make_time_spine_data_set( self, - time_dimension_instance: TimeDimensionInstance, - time_dimension_column_name: str, + metric_time_dimension_instance: TimeDimensionInstance, + metric_time_dimension_column_name: str, time_spine_source: TimeSpineSource, time_range_constraint: Optional[TimeRangeConstraint] = None, ) -> SqlDataSet: @@ -176,14 +176,14 @@ def _make_time_spine_data_set( """ time_spine_instance = ( TimeDimensionInstance( - defined_from=time_dimension_instance.defined_from, + defined_from=metric_time_dimension_instance.defined_from, associated_columns=( ColumnAssociation( - column_name=time_dimension_column_name, + column_name=metric_time_dimension_column_name, single_column_correlation_key=SingleColumnCorrelationKey(), ), ), - spec=time_dimension_instance.spec, + spec=metric_time_dimension_instance.spec, ), ) time_spine_instance_set = InstanceSet( @@ -193,7 +193,7 @@ def _make_time_spine_data_set( time_spine_table_alias = self._next_unique_table_alias() # If the requested granularity is the same as the granularity of the spine, do a direct select. - if time_dimension_instance.spec.time_granularity == time_spine_source.time_column_granularity: + if metric_time_dimension_instance.spec.time_granularity == time_spine_source.time_column_granularity: return SqlDataSet( instance_set=time_spine_instance_set, sql_select_node=SqlSelectStatementNode( @@ -207,7 +207,7 @@ def _make_time_spine_data_set( column_name=time_spine_source.time_column_name, ), ), - column_alias=time_dimension_column_name, + column_alias=metric_time_dimension_column_name, ), ), from_source=SqlTableFromClauseNode(sql_table=time_spine_source.spine_table), @@ -229,7 +229,7 @@ def _make_time_spine_data_set( select_columns = ( SqlSelectColumn( expr=SqlDateTruncExpression( - time_granularity=time_dimension_instance.spec.time_granularity, + time_granularity=metric_time_dimension_instance.spec.time_granularity, arg=SqlColumnReferenceExpression( SqlColumnReference( table_alias=time_spine_table_alias, @@ -237,7 +237,7 @@ def _make_time_spine_data_set( ), ), ), - column_alias=time_dimension_column_name, + column_alias=metric_time_dimension_column_name, ), ) return SqlDataSet( @@ -294,8 +294,8 @@ def visit_join_over_time_range_node(self, node: JoinOverTimeRangeNode) -> SqlDat # Granularity of time_spine column should match granularity of metric_time column from parent dataset. assert metric_time_dimension_instance time_spine_data_set = self._make_time_spine_data_set( - time_dimension_instance=metric_time_dimension_instance, - time_dimension_column_name=metric_time_dimension_column_name, + metric_time_dimension_instance=metric_time_dimension_instance, + metric_time_dimension_column_name=metric_time_dimension_column_name, time_spine_source=self._time_spine_source, time_range_constraint=node.time_range_constraint, ) @@ -1312,40 +1312,25 @@ def visit_join_to_time_spine_node(self, node: JoinToTimeSpineNode) -> SqlDataSet parent_data_set = node.parent_node.accept(self) parent_alias = self._next_unique_table_alias() - # Validate time dimensions & choose the one to join time spine onto. - time_dimension_instance_to_join: Optional[TimeDimensionInstance] = None + # Build time spine dataset metric_time_dimension_instance: Optional[TimeDimensionInstance] = None - # If metric_time is requested, choose the one with the lowest granularity. for instance in parent_data_set.metric_time_dimension_instances: - if not metric_time_dimension_instance or ( - instance.spec.time_granularity < metric_time_dimension_instance.spec.time_granularity - ): - metric_time_dimension_instance = instance - if node.offset_window or node.offset_to_grain: - assert ( - metric_time_dimension_instance - ), "Can't query offset metric without metric time. Validations should have prevented this." - - # If there were no metric_time dimensions requested, choose the time dimension with the lowest granularity. - time_dimension_instance_to_join = metric_time_dimension_instance - if not time_dimension_instance_to_join: - for instance in parent_data_set.instance_set.time_dimension_instances: - if not time_dimension_instance_to_join or ( - instance.spec.time_granularity < time_dimension_instance_to_join.spec.time_granularity + if len(instance.spec.entity_links) == 0: + # Use the instance with the lowest granularity + if not metric_time_dimension_instance or ( + instance.spec.time_granularity < metric_time_dimension_instance.spec.time_granularity ): - time_dimension_instance_to_join = instance + metric_time_dimension_instance = instance assert ( - time_dimension_instance_to_join # TODO: update validations to prevent this lol - ), "Can't join to time spine without a time dimension. Validations should have prevented this." - - # Build time spine dataset - time_dimension_column_name = self.column_association_resolver.resolve_spec( - time_dimension_instance_to_join.spec + metric_time_dimension_instance + ), "Can't query offset metric without metric time. Validations should have prevented this." + metric_time_dimension_column_name = self.column_association_resolver.resolve_spec( + metric_time_dimension_instance.spec ).column_name time_spine_alias = self._next_unique_table_alias() time_spine_dataset = self._make_time_spine_data_set( - time_dimension_instance=time_dimension_instance_to_join, - time_dimension_column_name=time_dimension_column_name, + metric_time_dimension_instance=metric_time_dimension_instance, + metric_time_dimension_column_name=metric_time_dimension_column_name, time_spine_source=self._time_spine_source, time_range_constraint=node.time_range_constraint, ) @@ -1354,20 +1339,19 @@ def visit_join_to_time_spine_node(self, node: JoinToTimeSpineNode) -> SqlDataSet join_description = SqlQueryPlanJoinBuilder.make_join_to_time_spine_join_description( node=node, time_spine_alias=time_spine_alias, - time_dimension_column_name=time_dimension_column_name, + metric_time_dimension_column_name=metric_time_dimension_column_name, parent_sql_select_node=parent_data_set.sql_select_node, parent_alias=parent_alias, join_type=node.join_type, ) - # Use all instances EXCEPT joined time dimension from parent data set. - parent_instance_set_to_keep = InstanceSet( + # Use all instances EXCEPT metric_time from parent data set. + non_metric_time_parent_instance_set = InstanceSet( measure_instances=parent_data_set.instance_set.measure_instances, dimension_instances=parent_data_set.instance_set.dimension_instances, time_dimension_instances=tuple( time_dimension_instance for time_dimension_instance in parent_data_set.instance_set.time_dimension_instances - # TODO: replace logic below - remove the one selected. maybe do that logic above if time_dimension_instance.spec.element_name != DataSet.metric_time_dimension_reference().element_name ), entity_instances=parent_data_set.instance_set.entity_instances, @@ -1375,7 +1359,7 @@ def visit_join_to_time_spine_node(self, node: JoinToTimeSpineNode) -> SqlDataSet metadata_instances=parent_data_set.instance_set.metadata_instances, ) non_metric_time_select_columns = create_select_columns_for_instance_sets( - self._column_association_resolver, OrderedDict({parent_alias: parent_instance_set_to_keep}) + self._column_association_resolver, OrderedDict({parent_alias: non_metric_time_parent_instance_set}) ) # Use metric_time column from time spine. @@ -1394,7 +1378,7 @@ def visit_join_to_time_spine_node(self, node: JoinToTimeSpineNode) -> SqlDataSet metric_time_select_columns = [] metric_time_dimension_instances = [] where: Optional[SqlExpressionNode] = None - for time_dimension_spec in node.time_dimension_specs: + for time_dimension_spec in node.metric_time_dimension_specs: # Apply granularity to SQL. if time_dimension_spec.time_granularity == self._time_spine_source.time_column_granularity: select_expr: SqlExpressionNode = time_spine_column_select_expr @@ -1433,7 +1417,7 @@ def visit_join_to_time_spine_node(self, node: JoinToTimeSpineNode) -> SqlDataSet metric_time_instance_set = InstanceSet(time_dimension_instances=tuple(metric_time_dimension_instances)) return SqlDataSet( - instance_set=InstanceSet.merge([metric_time_instance_set, parent_instance_set_to_keep]), + instance_set=InstanceSet.merge([metric_time_instance_set, non_metric_time_parent_instance_set]), sql_select_node=SqlSelectStatementNode( description=node.description, select_columns=tuple(metric_time_select_columns) + non_metric_time_select_columns, diff --git a/metricflow/plan_conversion/sql_join_builder.py b/metricflow/plan_conversion/sql_join_builder.py index ee8c5ae00d..26b6f5223f 100644 --- a/metricflow/plan_conversion/sql_join_builder.py +++ b/metricflow/plan_conversion/sql_join_builder.py @@ -472,14 +472,14 @@ def make_cumulative_metric_time_range_join_description( def make_join_to_time_spine_join_description( node: JoinToTimeSpineNode, time_spine_alias: str, - time_dimension_column_name: str, + metric_time_dimension_column_name: str, parent_sql_select_node: SqlSelectStatementNode, parent_alias: str, join_type: SqlJoinType, ) -> SqlJoinDescription: """Build join expression used to join a metric to a time spine dataset.""" left_expr: SqlExpressionNode = SqlColumnReferenceExpression( - col_ref=SqlColumnReference(table_alias=time_spine_alias, column_name=time_dimension_column_name) + col_ref=SqlColumnReference(table_alias=time_spine_alias, column_name=metric_time_dimension_column_name) ) if node.offset_window: left_expr = SqlSubtractTimeIntervalExpression( @@ -495,7 +495,7 @@ def make_join_to_time_spine_join_description( left_expr=left_expr, comparison=SqlComparison.EQUALS, right_expr=SqlColumnReferenceExpression( - col_ref=SqlColumnReference(table_alias=parent_alias, column_name=time_dimension_column_name) + col_ref=SqlColumnReference(table_alias=parent_alias, column_name=metric_time_dimension_column_name) ), ), join_type=join_type, diff --git a/metricflow/test/integration/test_cases/itest_metrics.yaml b/metricflow/test/integration/test_cases/itest_metrics.yaml index 4278f6796e..b89ecf3574 100644 --- a/metricflow/test/integration/test_cases/itest_metrics.yaml +++ b/metricflow/test/integration/test_cases/itest_metrics.yaml @@ -1147,7 +1147,7 @@ integration_test: SELECT 1 --- integration_test: - name: simple_fill_nulls_with_0_non_metric_time + name: dont_join_or_fill_nulls_for_non_metric_time description: Test a simple metric that joins to time spine with time dim that's not metric time model: SIMPLE_MODEL metrics: ["bookings_fill_0"] diff --git a/metricflow/test/integration/test_configured_cases.py b/metricflow/test/integration/test_configured_cases.py index 95579a11d1..d88de4e567 100644 --- a/metricflow/test/integration/test_configured_cases.py +++ b/metricflow/test/integration/test_configured_cases.py @@ -219,7 +219,7 @@ def test_case( extended_date_semantic_manifest_lookup: SemanticManifestLookup, scd_semantic_manifest_lookup: SemanticManifestLookup, sql_client: SqlClient, - create_source_tables: bool, + create_source_tables: bool, # Param not used in funciton, but needed for test data to exist ) -> None: """Runs all integration tests configured in the test case YAML directory.""" case = CONFIGURED_INTEGRATION_TESTS_REPOSITORY.get_test_case(name) diff --git a/metricflow/test/plan_conversion/test_dataflow_to_sql_plan.py b/metricflow/test/plan_conversion/test_dataflow_to_sql_plan.py index f3a17a84e5..1a86e54ee2 100644 --- a/metricflow/test/plan_conversion/test_dataflow_to_sql_plan.py +++ b/metricflow/test/plan_conversion/test_dataflow_to_sql_plan.py @@ -560,7 +560,7 @@ def test_join_to_time_spine_node_without_offset( # noqa: D compute_metrics_node = ComputeMetricsNode(parent_node=aggregated_measures_node, metric_specs=[metric_spec]) join_to_time_spine_node = JoinToTimeSpineNode( parent_node=compute_metrics_node, - time_dimension_specs=[MTD_SPEC_DAY], + metric_time_dimension_specs=[MTD_SPEC_DAY], time_range_constraint=TimeRangeConstraint( start_time=as_datetime("2020-01-01"), end_time=as_datetime("2021-01-01") ), @@ -624,7 +624,7 @@ def test_join_to_time_spine_node_with_offset_window( # noqa: D compute_metrics_node = ComputeMetricsNode(parent_node=aggregated_measures_node, metric_specs=[metric_spec]) join_to_time_spine_node = JoinToTimeSpineNode( parent_node=compute_metrics_node, - time_dimension_specs=[MTD_SPEC_DAY], + metric_time_dimension_specs=[MTD_SPEC_DAY], time_range_constraint=TimeRangeConstraint( start_time=as_datetime("2020-01-01"), end_time=as_datetime("2021-01-01") ), @@ -690,7 +690,7 @@ def test_join_to_time_spine_node_with_offset_to_grain( compute_metrics_node = ComputeMetricsNode(parent_node=aggregated_measures_node, metric_specs=[metric_spec]) join_to_time_spine_node = JoinToTimeSpineNode( parent_node=compute_metrics_node, - time_dimension_specs=[MTD_SPEC_DAY], + metric_time_dimension_specs=[MTD_SPEC_DAY], time_range_constraint=TimeRangeConstraint( start_time=as_datetime("2020-01-01"), end_time=as_datetime("2021-01-01") ), diff --git a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_to_grain__dfp_0.xml b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_to_grain__dfp_0.xml index f06ee9427e..6019a0d8d1 100644 --- a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_to_grain__dfp_0.xml +++ b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_to_grain__dfp_0.xml @@ -94,7 +94,7 @@ - + diff --git a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_window__dfp_0.xml b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_window__dfp_0.xml index 1538565dde..85e3e02bf7 100644 --- a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_window__dfp_0.xml +++ b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_window__dfp_0.xml @@ -47,7 +47,7 @@ - + diff --git a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_with_granularity__dfp_0.xml b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_with_granularity__dfp_0.xml index c4994be8e6..78a6d6a1f2 100644 --- a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_with_granularity__dfp_0.xml +++ b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_with_granularity__dfp_0.xml @@ -47,7 +47,7 @@ - + diff --git a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_offset_cumulative_metric__dfp_0.xml b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_offset_cumulative_metric__dfp_0.xml index 279469e982..747eee2a09 100644 --- a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_offset_cumulative_metric__dfp_0.xml +++ b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_offset_cumulative_metric__dfp_0.xml @@ -47,7 +47,7 @@ - + diff --git a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_derived_metric__dfp_0.xml b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_derived_metric__dfp_0.xml index 6ba06819d1..03fe9fe989 100644 --- a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_derived_metric__dfp_0.xml +++ b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_derived_metric__dfp_0.xml @@ -29,7 +29,7 @@ - + @@ -91,7 +91,7 @@ - + @@ -125,7 +125,7 @@ - + diff --git a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_with_metric_time__dfp_0.xml b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_with_metric_time__dfp_0.xml index 3589bc91a1..d4296f63a9 100644 --- a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_with_metric_time__dfp_0.xml +++ b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_with_metric_time__dfp_0.xml @@ -15,7 +15,7 @@ - + diff --git a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_with_non_metric_time__dfp_0.xml b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_with_non_metric_time__dfp_0.xml index a6fccdc15f..1fa2b98a7a 100644 --- a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_with_non_metric_time__dfp_0.xml +++ b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_with_non_metric_time__dfp_0.xml @@ -12,55 +12,40 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_with_offset_to_grain__plan0.xml b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_with_offset_to_grain__plan0.xml index bdd3bf94e0..d5189e59a2 100644 --- a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_with_offset_to_grain__plan0.xml +++ b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_with_offset_to_grain__plan0.xml @@ -5,7 +5,7 @@ - + diff --git a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_with_offset_window__plan0.xml b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_with_offset_window__plan0.xml index 0ef78381ba..a2961937f6 100644 --- a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_with_offset_window__plan0.xml +++ b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_with_offset_window__plan0.xml @@ -5,7 +5,7 @@ - + diff --git a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_without_offset__plan0.xml b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_without_offset__plan0.xml index 5c67156c62..877637716a 100644 --- a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_without_offset__plan0.xml +++ b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_without_offset__plan0.xml @@ -5,7 +5,7 @@ - + From 79b12a4ecb9bdffbcc6b382de4e7875f1d2c6b6b Mon Sep 17 00:00:00 2001 From: Courtney Holcomb Date: Tue, 31 Oct 2023 14:47:39 -0700 Subject: [PATCH 08/11] Fix granularity bug & generate remaining SQL --- .../dataflow/builder/dataflow_plan_builder.py | 4 +- metricflow/dataflow/dataflow_plan.py | 16 +- metricflow/plan_conversion/dataflow_to_sql.py | 58 +- .../integration/test_cases/itest_metrics.yaml | 117 +- .../test_dataflow_to_sql_plan.py | 200 +- ..._derived_metric_offset_to_grain__dfp_0.xml | 2 +- ...st_derived_metric_offset_window__dfp_0.xml | 2 +- ..._metric_offset_with_granularity__dfp_0.xml | 2 +- ...erived_offset_cumulative_metric__dfp_0.xml | 2 +- ...in_to_time_spine_derived_metric__dfp_0.xml | 6 +- ..._to_time_spine_with_metric_time__dfp_0.xml | 2 +- ...spine_node_with_offset_to_grain__plan0.xml | 2 +- ...e_spine_node_with_offset_window__plan0.xml | 2 +- ..._time_spine_node_without_offset__plan0.xml | 2 +- .../test_cumulative_fill_nulls__plan0.sql | 342 +++ ...cumulative_fill_nulls__plan0_optimized.sql | 33 + ...fill_nulls_for_one_input_metric__plan0.sql | 567 +++++ ..._for_one_input_metric__plan0_optimized.sql | 77 + ...ll_0_with_categorical_dimension__plan0.sql | 214 ++ ...categorical_dimension__plan0_optimized.sql | 22 + ...ple_fill_0_with_non_metric_time__plan0.sql | 214 ++ ..._with_non_metric_time__plan0_optimized.sql | 22 + ...e_fill_nulls_with_0_metric_time__plan0.sql | 228 ++ ...ls_with_0_metric_time__plan0_optimized.sql | 31 + ..._simple_fill_nulls_with_0_month__plan0.sql | 230 ++ ...ll_nulls_with_0_month__plan0_optimized.sql | 38 + ...e_fill_nulls_without_time_spine__plan0.sql | 214 ++ ...ls_without_time_spine__plan0_optimized.sql | 22 + .../test_simple_join_to_time_spine__plan0.sql | 228 ++ ...le_join_to_time_spine__plan0_optimized.sql | 26 + .../test_cumulative_fill_nulls__plan0.xml | 1265 ++++++++++ ...fill_nulls_for_one_input_metric__plan0.xml | 2072 +++++++++++++++++ ...ll_0_with_categorical_dimension__plan0.xml | 811 +++++++ ...ple_fill_0_with_non_metric_time__plan0.xml | 811 +++++++ ...e_fill_nulls_with_0_metric_time__plan0.xml | 846 +++++++ ..._simple_fill_nulls_with_0_month__plan0.xml | 850 +++++++ ...e_fill_nulls_without_time_spine__plan0.xml | 811 +++++++ .../test_simple_join_to_time_spine__plan0.xml | 846 +++++++ 38 files changed, 11131 insertions(+), 106 deletions(-) create mode 100644 metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_cumulative_fill_nulls__plan0.sql create mode 100644 metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_cumulative_fill_nulls__plan0_optimized.sql create mode 100644 metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_derived_fill_nulls_for_one_input_metric__plan0.sql create mode 100644 metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_derived_fill_nulls_for_one_input_metric__plan0_optimized.sql create mode 100644 metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_0_with_categorical_dimension__plan0.sql create mode 100644 metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_0_with_categorical_dimension__plan0_optimized.sql create mode 100644 metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_0_with_non_metric_time__plan0.sql create mode 100644 metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_0_with_non_metric_time__plan0_optimized.sql create mode 100644 metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_nulls_with_0_metric_time__plan0.sql create mode 100644 metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_nulls_with_0_metric_time__plan0_optimized.sql create mode 100644 metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_nulls_with_0_month__plan0.sql create mode 100644 metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_nulls_with_0_month__plan0_optimized.sql create mode 100644 metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_nulls_without_time_spine__plan0.sql create mode 100644 metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_nulls_without_time_spine__plan0_optimized.sql create mode 100644 metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_join_to_time_spine__plan0.sql create mode 100644 metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_join_to_time_spine__plan0_optimized.sql create mode 100644 metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/test_cumulative_fill_nulls__plan0.xml create mode 100644 metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/test_derived_fill_nulls_for_one_input_metric__plan0.xml create mode 100644 metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/test_simple_fill_0_with_categorical_dimension__plan0.xml create mode 100644 metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/test_simple_fill_0_with_non_metric_time__plan0.xml create mode 100644 metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/test_simple_fill_nulls_with_0_metric_time__plan0.xml create mode 100644 metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/test_simple_fill_nulls_with_0_month__plan0.xml create mode 100644 metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/test_simple_fill_nulls_without_time_spine__plan0.xml create mode 100644 metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/test_simple_join_to_time_spine__plan0.xml diff --git a/metricflow/dataflow/builder/dataflow_plan_builder.py b/metricflow/dataflow/builder/dataflow_plan_builder.py index f4cf805ac6..18fee96bdc 100644 --- a/metricflow/dataflow/builder/dataflow_plan_builder.py +++ b/metricflow/dataflow/builder/dataflow_plan_builder.py @@ -820,7 +820,7 @@ def _build_aggregated_measures_from_measure_source_node( assert metric_time_dimension_specs, "Joining to time spine requires querying with metric time." join_to_time_spine_node = JoinToTimeSpineNode( parent_node=time_range_node or measure_recipe.source_node, - metric_time_dimension_specs=metric_time_dimension_specs, + requested_metric_time_dimension_specs=metric_time_dimension_specs, time_range_constraint=time_range_constraint, offset_window=metric_spec.offset_window, offset_to_grain=metric_spec.offset_to_grain, @@ -929,7 +929,7 @@ def _build_aggregated_measures_from_measure_source_node( if join_aggregated_measure_to_time_spine and metric_time_dimension_requested: return JoinToTimeSpineNode( parent_node=aggregate_measures_node, - metric_time_dimension_specs=metric_time_dimension_specs, + requested_metric_time_dimension_specs=metric_time_dimension_specs, time_range_constraint=time_range_constraint, join_type=SqlJoinType.LEFT_OUTER, ) diff --git a/metricflow/dataflow/dataflow_plan.py b/metricflow/dataflow/dataflow_plan.py index e9843b7b95..0a2140aedb 100644 --- a/metricflow/dataflow/dataflow_plan.py +++ b/metricflow/dataflow/dataflow_plan.py @@ -700,7 +700,7 @@ class JoinToTimeSpineNode(BaseOutput, ABC): def __init__( self, parent_node: BaseOutput, - metric_time_dimension_specs: List[TimeDimensionSpec], + requested_metric_time_dimension_specs: List[TimeDimensionSpec], join_type: SqlJoinType, time_range_constraint: Optional[TimeRangeConstraint] = None, offset_window: Optional[MetricTimeWindow] = None, @@ -710,7 +710,7 @@ def __init__( Args: parent_node: Node that returns desired dataset to join to time spine. - metric_time_dimension_specs: Time dimensions requested in query. Used to determine granularities. + requested_metric_time_dimension_specs: Time dimensions requested in query. Used to determine granularities. time_range_constraint: Time range to constrain the time spine to. offset_window: Time window to offset the parent dataset by when joining to time spine. offset_to_grain: Granularity period to offset the parent dataset to when joining to time spine. @@ -721,7 +721,7 @@ def __init__( offset_window and offset_to_grain ), "Can't set both offset_window and offset_to_grain when joining to time spine. Choose one or the other." self._parent_node = parent_node - self._metric_time_dimension_specs = metric_time_dimension_specs + self._requested_metric_time_dimension_specs = requested_metric_time_dimension_specs self._offset_window = offset_window self._offset_to_grain = offset_to_grain self._time_range_constraint = time_range_constraint @@ -734,9 +734,9 @@ def id_prefix(cls) -> str: # noqa: D return DATAFLOW_NODE_JOIN_TO_TIME_SPINE_ID_PREFIX @property - def metric_time_dimension_specs(self) -> List[TimeDimensionSpec]: # noqa: D + def requested_metric_time_dimension_specs(self) -> List[TimeDimensionSpec]: # noqa: D """Time dimension specs to use when creating time spine table.""" - return self._metric_time_dimension_specs + return self._requested_metric_time_dimension_specs @property def time_range_constraint(self) -> Optional[TimeRangeConstraint]: # noqa: D @@ -768,7 +768,7 @@ def description(self) -> str: # noqa: D @property def displayed_properties(self) -> List[DisplayedProperty]: # noqa: D return super().displayed_properties + [ - DisplayedProperty("metric_time_dimension_specs", self._metric_time_dimension_specs), + DisplayedProperty("requested_metric_time_dimension_specs", self._requested_metric_time_dimension_specs), DisplayedProperty("time_range_constraint", self._time_range_constraint), DisplayedProperty("offset_window", self._offset_window), DisplayedProperty("offset_to_grain", self._offset_to_grain), @@ -785,7 +785,7 @@ def functionally_identical(self, other_node: DataflowPlanNode) -> bool: # noqa: and other_node.time_range_constraint == self.time_range_constraint and other_node.offset_window == self.offset_window and other_node.offset_to_grain == self.offset_to_grain - and other_node.metric_time_dimension_specs == self.metric_time_dimension_specs + and other_node.requested_metric_time_dimension_specs == self.requested_metric_time_dimension_specs and other_node.join_type == self.join_type ) @@ -793,7 +793,7 @@ def with_new_parents(self, new_parent_nodes: Sequence[BaseOutput]) -> JoinToTime assert len(new_parent_nodes) == 1 return JoinToTimeSpineNode( parent_node=new_parent_nodes[0], - metric_time_dimension_specs=self.metric_time_dimension_specs, + requested_metric_time_dimension_specs=self.requested_metric_time_dimension_specs, time_range_constraint=self.time_range_constraint, offset_window=self.offset_window, offset_to_grain=self.offset_to_grain, diff --git a/metricflow/plan_conversion/dataflow_to_sql.py b/metricflow/plan_conversion/dataflow_to_sql.py index c4b5e8a216..12cb14e43e 100644 --- a/metricflow/plan_conversion/dataflow_to_sql.py +++ b/metricflow/plan_conversion/dataflow_to_sql.py @@ -1316,14 +1316,14 @@ def visit_join_to_time_spine_node(self, node: JoinToTimeSpineNode) -> SqlDataSet metric_time_dimension_instance: Optional[TimeDimensionInstance] = None for instance in parent_data_set.metric_time_dimension_instances: if len(instance.spec.entity_links) == 0: - # Use the instance with the lowest granularity + # Use metric time with the lowest supported granularity. if not metric_time_dimension_instance or ( instance.spec.time_granularity < metric_time_dimension_instance.spec.time_granularity ): metric_time_dimension_instance = instance assert ( metric_time_dimension_instance - ), "Can't query offset metric without metric time. Validations should have prevented this." + ), "Can't join to time spine without metric time. Validations should have prevented this." metric_time_dimension_column_name = self.column_association_resolver.resolve_spec( metric_time_dimension_instance.spec ).column_name @@ -1358,33 +1358,33 @@ def visit_join_to_time_spine_node(self, node: JoinToTimeSpineNode) -> SqlDataSet metric_instances=parent_data_set.instance_set.metric_instances, metadata_instances=parent_data_set.instance_set.metadata_instances, ) - non_metric_time_select_columns = create_select_columns_for_instance_sets( + non_time_spine_select_columns = create_select_columns_for_instance_sets( self._column_association_resolver, OrderedDict({parent_alias: non_metric_time_parent_instance_set}) ) - # Use metric_time column from time spine. + # Use time column from time spine to replace metric_time instances. assert ( len(time_spine_dataset.instance_set.time_dimension_instances) == 1 and len(time_spine_dataset.sql_select_node.select_columns) == 1 ), "Time spine dataset not configured properly. Expected exactly one column." - time_dim_instance = time_spine_dataset.instance_set.time_dimension_instances[0] + time_spine_dim_instance = time_spine_dataset.instance_set.time_dimension_instances[0] time_spine_column_select_expr: Union[ SqlColumnReferenceExpression, SqlDateTruncExpression ] = SqlColumnReferenceExpression( - SqlColumnReference(table_alias=time_spine_alias, column_name=time_dim_instance.spec.qualified_name) + SqlColumnReference(table_alias=time_spine_alias, column_name=time_spine_dim_instance.spec.qualified_name) ) - # Add requested granularities (skip for default granularity) and date_parts. - metric_time_select_columns = [] - metric_time_dimension_instances = [] + # Add requested granularities (skip for default granularity) and date_parts to time spine column. + time_spine_select_columns = [] + time_spine_dim_instances = [] where: Optional[SqlExpressionNode] = None - for time_dimension_spec in node.metric_time_dimension_specs: - # Apply granularity to SQL. - if time_dimension_spec.time_granularity == self._time_spine_source.time_column_granularity: + for requested_time_dimension_spec in node.requested_metric_time_dimension_specs: + # Apply granularity to time spine column select expression. + if requested_time_dimension_spec.time_granularity == time_spine_dim_instance.spec.time_granularity: select_expr: SqlExpressionNode = time_spine_column_select_expr else: select_expr = SqlDateTruncExpression( - time_granularity=time_dimension_spec.time_granularity, arg=time_spine_column_select_expr + time_granularity=requested_time_dimension_spec.time_granularity, arg=time_spine_column_select_expr ) if node.offset_to_grain: # Filter down to one row per granularity period @@ -1395,32 +1395,32 @@ def visit_join_to_time_spine_node(self, node: JoinToTimeSpineNode) -> SqlDataSet where = new_filter else: where = SqlLogicalExpression(operator=SqlLogicalOperator.OR, args=(where, new_filter)) - # Apply date_part to SQL. - if time_dimension_spec.date_part: - select_expr = SqlExtractExpression(date_part=time_dimension_spec.date_part, arg=select_expr) + # Apply date_part to time spine column select expression. + if requested_time_dimension_spec.date_part: + select_expr = SqlExtractExpression(date_part=requested_time_dimension_spec.date_part, arg=select_expr) time_dim_spec = TimeDimensionSpec( - element_name=time_dim_instance.spec.element_name, - entity_links=time_dim_instance.spec.entity_links, - time_granularity=time_dimension_spec.time_granularity, - date_part=time_dimension_spec.date_part, - aggregation_state=time_dim_instance.spec.aggregation_state, + element_name=time_spine_dim_instance.spec.element_name, + entity_links=time_spine_dim_instance.spec.entity_links, + time_granularity=requested_time_dimension_spec.time_granularity, + date_part=requested_time_dimension_spec.date_part, + aggregation_state=time_spine_dim_instance.spec.aggregation_state, ) - time_dim_instance = TimeDimensionInstance( - defined_from=time_dim_instance.defined_from, + time_spine_dim_instance = TimeDimensionInstance( + defined_from=time_spine_dim_instance.defined_from, associated_columns=(self._column_association_resolver.resolve_spec(time_dim_spec),), spec=time_dim_spec, ) - metric_time_dimension_instances.append(time_dim_instance) - metric_time_select_columns.append( - SqlSelectColumn(expr=select_expr, column_alias=time_dim_instance.associated_column.column_name) + time_spine_dim_instances.append(time_spine_dim_instance) + time_spine_select_columns.append( + SqlSelectColumn(expr=select_expr, column_alias=time_spine_dim_instance.associated_column.column_name) ) - metric_time_instance_set = InstanceSet(time_dimension_instances=tuple(metric_time_dimension_instances)) + time_spine_instance_set = InstanceSet(time_dimension_instances=tuple(time_spine_dim_instances)) return SqlDataSet( - instance_set=InstanceSet.merge([metric_time_instance_set, non_metric_time_parent_instance_set]), + instance_set=InstanceSet.merge([time_spine_instance_set, non_metric_time_parent_instance_set]), sql_select_node=SqlSelectStatementNode( description=node.description, - select_columns=tuple(metric_time_select_columns) + non_metric_time_select_columns, + select_columns=tuple(time_spine_select_columns) + non_time_spine_select_columns, from_source=time_spine_dataset.sql_select_node, from_source_alias=time_spine_alias, joins_descs=(join_description,), diff --git a/metricflow/test/integration/test_cases/itest_metrics.yaml b/metricflow/test/integration/test_cases/itest_metrics.yaml index b89ecf3574..4db86f3159 100644 --- a/metricflow/test/integration/test_cases/itest_metrics.yaml +++ b/metricflow/test/integration/test_cases/itest_metrics.yaml @@ -1130,49 +1130,75 @@ integration_test: --- integration_test: name: simple_fill_nulls_with_0_metric_time - description: Test a simple metric that joins to time spine with metric time + description: Test a simple query that joins to time spine and fills nulls model: SIMPLE_MODEL metrics: ["bookings_fill_0"] group_by_objs: [{"name": "metric_time"}] check_query: | - SELECT 1 + SELECT + subq_5.ds AS metric_time__day + , COALESCE(subq_3.bookings, 0) AS bookings_fill_0 + FROM {{ source_schema }}.mf_time_spine subq_5 + LEFT OUTER JOIN ( + SELECT + DATE_TRUNC('day', ds) AS metric_time__day + , SUM(1) AS bookings + FROM {{ source_schema }}.fct_bookings bookings_source_src_1 + GROUP BY metric_time__day + ) subq_3 + ON subq_5.ds = subq_3.metric_time__day --- integration_test: name: simple_fill_nulls_with_0_month - description: Test a simple metric that joins to time spine with metric time + description: Test a simple query that joins to time spine and fills nulls, with non-default granularity model: SIMPLE_MODEL metrics: ["bookings_fill_0"] group_by_objs: [{"name": "metric_time", "grain": "month"}] check_query: | - SELECT 1 + SELECT + subq_4.metric_time__month AS metric_time__month + , COALESCE(subq_3.bookings, 0) AS bookings_fill_0 + FROM ( + SELECT + DATE_TRUNC('month', ds) AS metric_time__month + FROM {{ source_schema }}.mf_time_spine subq_5 + GROUP BY metric_time__month + ) subq_4 + LEFT OUTER JOIN ( + SELECT + DATE_TRUNC('month', ds) AS metric_time__month + , SUM(1) AS bookings + FROM {{ source_schema }}.fct_bookings bookings_source_src_1 + GROUP BY metric_time__month + ) subq_3 + ON subq_4.metric_time__month = subq_3.metric_time__month --- integration_test: - name: dont_join_or_fill_nulls_for_non_metric_time - description: Test a simple metric that joins to time spine with time dim that's not metric time + name: simple_fill_0_with_non_metric_time + description: Test simple query that fills nulls but doesn't join to time spine (non-metric time dimension) model: SIMPLE_MODEL metrics: ["bookings_fill_0"] group_by_objs: [{"name": "booking__paid_at"}] check_query: | - SELECT 1 + SELECT + DATE_TRUNC('day', paid_at) AS booking__paid_at__day + , COALESCE(SUM(1), 0) AS bookings_fill_0 + FROM {{ source_schema }}.fct_bookings bookings_source_src_1 + GROUP BY + booking__paid_at__day --- integration_test: - name: simple_should_not_join_to_time_spine - description: Test a simple metric that normally joins to time spine, but shouldn't if no time dim is requested + name: simple_fill_0_with_categorical_dimension + description: Test simple query that fills nulls but doesn't join to time spine (categorical dimension) model: SIMPLE_MODEL metrics: ["bookings_fill_0"] group_by_objs: [{"name": "booking__is_instant"}] check_query: | SELECT - booking__is_instant - , COALESCE(bookings, 0) AS bookings_fill_0 - FROM ( - SELECT - is_instant AS booking__is_instant - , SUM(1) AS bookings - FROM {{ source_schema }}.fct_bookings bookings_source_src_1 - GROUP BY - booking__is_instant - ) + is_instant AS booking__is_instant + , COALESCE(SUM(1), 0) AS bookings_fill_0 + FROM {{ source_schema }}.fct_bookings bookings_source_src_1 + GROUP BY booking__is_instant --- integration_test: name: simple_join_to_time_spine @@ -1198,22 +1224,16 @@ integration_test: --- integration_test: name: simple_fill_nulls_without_time_spine - description: Test a simple metric that fills nulls, but doesn't join to time spine + description: Test a simple query that fills nulls, but doesn't join to time spine (even for metric time) model: SIMPLE_MODEL metrics: ["bookings_fill_0_without_time_spine"] group_by_objs: [{"name": "metric_time"}] check_query: | SELECT - metric_time__day - , COALESCE(bookings, 0) AS bookings_fill_0_without_time_spine - FROM ( - SELECT - DATE_TRUNC('day', ds) AS metric_time__day - , SUM(1) AS bookings - FROM {{ source_schema }}.fct_bookings bookings_source_src_1 - GROUP BY - metric_time__day - ) + DATE_TRUNC('day', ds) AS metric_time__day + , COALESCE(SUM(1), 0) AS bookings_fill_0_without_time_spine + FROM {{ source_schema }}.fct_bookings bookings_source_src_1 + GROUP BY metric_time__day --- integration_test: name: cumulative_fill_nulls @@ -1223,30 +1243,25 @@ integration_test: group_by_objs: [{"name": "metric_time"}] check_query: | SELECT - metric_time__day - , COALESCE(bookers, 0) AS every_two_days_bookers_fill_0 - FROM ( + subq_8.ds AS metric_time__day + , COALESCE(subq_6.bookers, 0) AS every_two_days_bookers_fill_0 + FROM {{ source_schema }}.mf_time_spine subq_8 + LEFT JOIN ( SELECT - subq_8.ds AS metric_time__day - , subq_6.bookers AS bookers - FROM {{ source_schema }}.mf_time_spine subq_8 - LEFT JOIN ( - SELECT - subq_3.ds AS metric_time__day - , COUNT(DISTINCT bookings_source_src_1.guest_id) AS bookers - FROM {{ source_schema }}.mf_time_spine subq_3 - INNER JOIN - {{ source_schema }}.fct_bookings bookings_source_src_1 - ON (DATE_TRUNC('day', bookings_source_src_1.ds) <= subq_3.ds) - AND (DATE_TRUNC('day', bookings_source_src_1.ds) > subq_3.ds - INTERVAL 2 day) - GROUP BY - subq_3.ds - ) subq_6 - ON subq_8.ds = subq_6.metric_time__day - ) subq_9 + subq_3.ds AS metric_time__day + , COUNT(DISTINCT bookings_source_src_1.guest_id) AS bookers + FROM {{ source_schema }}.mf_time_spine subq_3 + INNER JOIN + {{ source_schema }}.fct_bookings bookings_source_src_1 + ON (DATE_TRUNC('day', bookings_source_src_1.ds) <= subq_3.ds) + AND (DATE_TRUNC('day', bookings_source_src_1.ds) > subq_3.ds - INTERVAL 2 day) + GROUP BY + subq_3.ds + ) subq_6 + ON subq_8.ds = subq_6.metric_time__day --- integration_test: - name: derived_fill_nulls_one_input_metric + name: derived_fill_nulls_for_one_input_metric description: Test a derived metric that fills nulls on one input metric but not the other model: SIMPLE_MODEL metrics: ["bookings_growth_2_weeks_fill_0_for_non_offset"] diff --git a/metricflow/test/plan_conversion/test_dataflow_to_sql_plan.py b/metricflow/test/plan_conversion/test_dataflow_to_sql_plan.py index 1a86e54ee2..bcc1fc1d42 100644 --- a/metricflow/test/plan_conversion/test_dataflow_to_sql_plan.py +++ b/metricflow/test/plan_conversion/test_dataflow_to_sql_plan.py @@ -560,7 +560,7 @@ def test_join_to_time_spine_node_without_offset( # noqa: D compute_metrics_node = ComputeMetricsNode(parent_node=aggregated_measures_node, metric_specs=[metric_spec]) join_to_time_spine_node = JoinToTimeSpineNode( parent_node=compute_metrics_node, - metric_time_dimension_specs=[MTD_SPEC_DAY], + requested_metric_time_dimension_specs=[MTD_SPEC_DAY], time_range_constraint=TimeRangeConstraint( start_time=as_datetime("2020-01-01"), end_time=as_datetime("2021-01-01") ), @@ -624,7 +624,7 @@ def test_join_to_time_spine_node_with_offset_window( # noqa: D compute_metrics_node = ComputeMetricsNode(parent_node=aggregated_measures_node, metric_specs=[metric_spec]) join_to_time_spine_node = JoinToTimeSpineNode( parent_node=compute_metrics_node, - metric_time_dimension_specs=[MTD_SPEC_DAY], + requested_metric_time_dimension_specs=[MTD_SPEC_DAY], time_range_constraint=TimeRangeConstraint( start_time=as_datetime("2020-01-01"), end_time=as_datetime("2021-01-01") ), @@ -690,7 +690,7 @@ def test_join_to_time_spine_node_with_offset_to_grain( compute_metrics_node = ComputeMetricsNode(parent_node=aggregated_measures_node, metric_specs=[metric_spec]) join_to_time_spine_node = JoinToTimeSpineNode( parent_node=compute_metrics_node, - metric_time_dimension_specs=[MTD_SPEC_DAY], + requested_metric_time_dimension_specs=[MTD_SPEC_DAY], time_range_constraint=TimeRangeConstraint( start_time=as_datetime("2020-01-01"), end_time=as_datetime("2021-01-01") ), @@ -2000,5 +2000,195 @@ def test_offset_window_with_date_part( # noqa: D ) -def test_stuff() -> None: # noqa: D - assert 0, "write some tests here!!!" +@pytest.mark.sql_engine_snapshot +def test_simple_fill_nulls_with_0_metric_time( # noqa: D + request: FixtureRequest, + mf_test_session_state: MetricFlowTestSessionState, + dataflow_plan_builder: DataflowPlanBuilder, + dataflow_to_sql_converter: DataflowToSqlQueryPlanConverter, + sql_client: SqlClient, +) -> None: + dataflow_plan = dataflow_plan_builder.build_plan( + MetricFlowQuerySpec( + metric_specs=(MetricSpec(element_name="bookings_fill_0"),), + time_dimension_specs=(DataSet.metric_time_dimension_spec(time_granularity=TimeGranularity.DAY),), + ) + ) + + convert_and_check( + request=request, + mf_test_session_state=mf_test_session_state, + dataflow_to_sql_converter=dataflow_to_sql_converter, + sql_client=sql_client, + node=dataflow_plan.sink_output_nodes[0].parent_node, + ) + + +@pytest.mark.sql_engine_snapshot +def test_simple_fill_nulls_with_0_month( # noqa: D + request: FixtureRequest, + mf_test_session_state: MetricFlowTestSessionState, + dataflow_plan_builder: DataflowPlanBuilder, + dataflow_to_sql_converter: DataflowToSqlQueryPlanConverter, + sql_client: SqlClient, +) -> None: + dataflow_plan = dataflow_plan_builder.build_plan( + MetricFlowQuerySpec( + metric_specs=(MetricSpec(element_name="bookings_fill_0"),), + time_dimension_specs=(DataSet.metric_time_dimension_spec(time_granularity=TimeGranularity.MONTH),), + ) + ) + + convert_and_check( + request=request, + mf_test_session_state=mf_test_session_state, + dataflow_to_sql_converter=dataflow_to_sql_converter, + sql_client=sql_client, + node=dataflow_plan.sink_output_nodes[0].parent_node, + ) + + +@pytest.mark.sql_engine_snapshot +def test_simple_fill_0_with_non_metric_time( # noqa: D + request: FixtureRequest, + mf_test_session_state: MetricFlowTestSessionState, + dataflow_plan_builder: DataflowPlanBuilder, + dataflow_to_sql_converter: DataflowToSqlQueryPlanConverter, + sql_client: SqlClient, +) -> None: + dataflow_plan = dataflow_plan_builder.build_plan( + MetricFlowQuerySpec( + metric_specs=(MetricSpec(element_name="bookings_fill_0"),), + time_dimension_specs=( + TimeDimensionSpec(element_name="paid_at", entity_links=(EntityReference("booking"),)), + ), + ) + ) + + convert_and_check( + request=request, + mf_test_session_state=mf_test_session_state, + dataflow_to_sql_converter=dataflow_to_sql_converter, + sql_client=sql_client, + node=dataflow_plan.sink_output_nodes[0].parent_node, + ) + + +@pytest.mark.sql_engine_snapshot +def test_simple_fill_0_with_categorical_dimension( # noqa: D + request: FixtureRequest, + mf_test_session_state: MetricFlowTestSessionState, + dataflow_plan_builder: DataflowPlanBuilder, + dataflow_to_sql_converter: DataflowToSqlQueryPlanConverter, + sql_client: SqlClient, +) -> None: + dataflow_plan = dataflow_plan_builder.build_plan( + MetricFlowQuerySpec( + metric_specs=(MetricSpec(element_name="bookings_fill_0"),), + dimension_specs=(DimensionSpec(element_name="is_instant", entity_links=(EntityReference("booking"),)),), + ) + ) + + convert_and_check( + request=request, + mf_test_session_state=mf_test_session_state, + dataflow_to_sql_converter=dataflow_to_sql_converter, + sql_client=sql_client, + node=dataflow_plan.sink_output_nodes[0].parent_node, + ) + + +@pytest.mark.sql_engine_snapshot +def test_simple_join_to_time_spine( # noqa: D + request: FixtureRequest, + mf_test_session_state: MetricFlowTestSessionState, + dataflow_plan_builder: DataflowPlanBuilder, + dataflow_to_sql_converter: DataflowToSqlQueryPlanConverter, + sql_client: SqlClient, +) -> None: + dataflow_plan = dataflow_plan_builder.build_plan( + MetricFlowQuerySpec( + metric_specs=(MetricSpec(element_name="bookings_join_to_time_spine"),), + time_dimension_specs=(DataSet.metric_time_dimension_spec(time_granularity=TimeGranularity.DAY),), + ) + ) + + convert_and_check( + request=request, + mf_test_session_state=mf_test_session_state, + dataflow_to_sql_converter=dataflow_to_sql_converter, + sql_client=sql_client, + node=dataflow_plan.sink_output_nodes[0].parent_node, + ) + + +@pytest.mark.sql_engine_snapshot +def test_simple_fill_nulls_without_time_spine( # noqa: D + request: FixtureRequest, + mf_test_session_state: MetricFlowTestSessionState, + dataflow_plan_builder: DataflowPlanBuilder, + dataflow_to_sql_converter: DataflowToSqlQueryPlanConverter, + sql_client: SqlClient, +) -> None: + dataflow_plan = dataflow_plan_builder.build_plan( + MetricFlowQuerySpec( + metric_specs=(MetricSpec(element_name="bookings_fill_0_without_time_spine"),), + time_dimension_specs=(DataSet.metric_time_dimension_spec(time_granularity=TimeGranularity.DAY),), + ) + ) + + convert_and_check( + request=request, + mf_test_session_state=mf_test_session_state, + dataflow_to_sql_converter=dataflow_to_sql_converter, + sql_client=sql_client, + node=dataflow_plan.sink_output_nodes[0].parent_node, + ) + + +@pytest.mark.sql_engine_snapshot +def test_cumulative_fill_nulls( # noqa: D + request: FixtureRequest, + mf_test_session_state: MetricFlowTestSessionState, + dataflow_plan_builder: DataflowPlanBuilder, + dataflow_to_sql_converter: DataflowToSqlQueryPlanConverter, + sql_client: SqlClient, +) -> None: + dataflow_plan = dataflow_plan_builder.build_plan( + MetricFlowQuerySpec( + metric_specs=(MetricSpec(element_name="every_two_days_bookers_fill_0"),), + time_dimension_specs=(DataSet.metric_time_dimension_spec(time_granularity=TimeGranularity.DAY),), + ) + ) + + convert_and_check( + request=request, + mf_test_session_state=mf_test_session_state, + dataflow_to_sql_converter=dataflow_to_sql_converter, + sql_client=sql_client, + node=dataflow_plan.sink_output_nodes[0].parent_node, + ) + + +@pytest.mark.sql_engine_snapshot +def test_derived_fill_nulls_for_one_input_metric( # noqa: D + request: FixtureRequest, + mf_test_session_state: MetricFlowTestSessionState, + dataflow_plan_builder: DataflowPlanBuilder, + dataflow_to_sql_converter: DataflowToSqlQueryPlanConverter, + sql_client: SqlClient, +) -> None: + dataflow_plan = dataflow_plan_builder.build_plan( + MetricFlowQuerySpec( + metric_specs=(MetricSpec(element_name="bookings_growth_2_weeks_fill_0_for_non_offset"),), + time_dimension_specs=(DataSet.metric_time_dimension_spec(time_granularity=TimeGranularity.DAY),), + ) + ) + + convert_and_check( + request=request, + mf_test_session_state=mf_test_session_state, + dataflow_to_sql_converter=dataflow_to_sql_converter, + sql_client=sql_client, + node=dataflow_plan.sink_output_nodes[0].parent_node, + ) diff --git a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_to_grain__dfp_0.xml b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_to_grain__dfp_0.xml index 6019a0d8d1..5c641dbf52 100644 --- a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_to_grain__dfp_0.xml +++ b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_to_grain__dfp_0.xml @@ -94,7 +94,7 @@ - + diff --git a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_window__dfp_0.xml b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_window__dfp_0.xml index 85e3e02bf7..a3ee6dc4f1 100644 --- a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_window__dfp_0.xml +++ b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_window__dfp_0.xml @@ -47,7 +47,7 @@ - + diff --git a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_with_granularity__dfp_0.xml b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_with_granularity__dfp_0.xml index 78a6d6a1f2..ff144a368d 100644 --- a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_with_granularity__dfp_0.xml +++ b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_metric_offset_with_granularity__dfp_0.xml @@ -47,7 +47,7 @@ - + diff --git a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_offset_cumulative_metric__dfp_0.xml b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_offset_cumulative_metric__dfp_0.xml index 747eee2a09..e2968a3ac6 100644 --- a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_offset_cumulative_metric__dfp_0.xml +++ b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_derived_offset_cumulative_metric__dfp_0.xml @@ -47,7 +47,7 @@ - + diff --git a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_derived_metric__dfp_0.xml b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_derived_metric__dfp_0.xml index 03fe9fe989..5a8fd29f0c 100644 --- a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_derived_metric__dfp_0.xml +++ b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_derived_metric__dfp_0.xml @@ -29,7 +29,7 @@ - + @@ -91,7 +91,7 @@ - + @@ -125,7 +125,7 @@ - + diff --git a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_with_metric_time__dfp_0.xml b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_with_metric_time__dfp_0.xml index d4296f63a9..d9e5deb618 100644 --- a/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_with_metric_time__dfp_0.xml +++ b/metricflow/test/snapshots/test_dataflow_plan_builder.py/DataflowPlan/test_join_to_time_spine_with_metric_time__dfp_0.xml @@ -15,7 +15,7 @@ - + diff --git a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_with_offset_to_grain__plan0.xml b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_with_offset_to_grain__plan0.xml index d5189e59a2..0d03e41f1c 100644 --- a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_with_offset_to_grain__plan0.xml +++ b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_with_offset_to_grain__plan0.xml @@ -5,7 +5,7 @@ - + diff --git a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_with_offset_window__plan0.xml b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_with_offset_window__plan0.xml index a2961937f6..00cb173b2c 100644 --- a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_with_offset_window__plan0.xml +++ b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_with_offset_window__plan0.xml @@ -5,7 +5,7 @@ - + diff --git a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_without_offset__plan0.xml b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_without_offset__plan0.xml index 877637716a..187ce156e4 100644 --- a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_without_offset__plan0.xml +++ b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/DataflowPlan/test_join_to_time_spine_node_without_offset__plan0.xml @@ -5,7 +5,7 @@ - + diff --git a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_cumulative_fill_nulls__plan0.sql b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_cumulative_fill_nulls__plan0.sql new file mode 100644 index 0000000000..44b982513a --- /dev/null +++ b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_cumulative_fill_nulls__plan0.sql @@ -0,0 +1,342 @@ +-- Compute Metrics via Expressions +SELECT + subq_9.metric_time__day + , COALESCE(subq_9.bookers, 0) AS every_two_days_bookers_fill_0 +FROM ( + -- Join to Time Spine Dataset + SELECT + subq_7.metric_time__day AS metric_time__day + , subq_6.bookers AS bookers + FROM ( + -- Date Spine + SELECT + subq_8.ds AS metric_time__day + FROM ***************************.mf_time_spine subq_8 + ) subq_7 + LEFT OUTER JOIN ( + -- Aggregate Measures + SELECT + subq_5.metric_time__day + , COUNT(DISTINCT subq_5.bookers) AS bookers + FROM ( + -- Pass Only Elements: + -- ['bookers', 'metric_time__day'] + SELECT + subq_4.metric_time__day + , subq_4.bookers + FROM ( + -- Join Self Over Time Range + SELECT + subq_2.metric_time__day AS metric_time__day + , subq_1.ds__day AS ds__day + , subq_1.ds__week AS ds__week + , subq_1.ds__month AS ds__month + , subq_1.ds__quarter AS ds__quarter + , subq_1.ds__year AS ds__year + , subq_1.ds__extract_year AS ds__extract_year + , subq_1.ds__extract_quarter AS ds__extract_quarter + , subq_1.ds__extract_month AS ds__extract_month + , subq_1.ds__extract_day AS ds__extract_day + , subq_1.ds__extract_dow AS ds__extract_dow + , subq_1.ds__extract_doy AS ds__extract_doy + , subq_1.ds_partitioned__day AS ds_partitioned__day + , subq_1.ds_partitioned__week AS ds_partitioned__week + , subq_1.ds_partitioned__month AS ds_partitioned__month + , subq_1.ds_partitioned__quarter AS ds_partitioned__quarter + , subq_1.ds_partitioned__year AS ds_partitioned__year + , subq_1.ds_partitioned__extract_year AS ds_partitioned__extract_year + , subq_1.ds_partitioned__extract_quarter AS ds_partitioned__extract_quarter + , subq_1.ds_partitioned__extract_month AS ds_partitioned__extract_month + , subq_1.ds_partitioned__extract_day AS ds_partitioned__extract_day + , subq_1.ds_partitioned__extract_dow AS ds_partitioned__extract_dow + , subq_1.ds_partitioned__extract_doy AS ds_partitioned__extract_doy + , subq_1.paid_at__day AS paid_at__day + , subq_1.paid_at__week AS paid_at__week + , subq_1.paid_at__month AS paid_at__month + , subq_1.paid_at__quarter AS paid_at__quarter + , subq_1.paid_at__year AS paid_at__year + , subq_1.paid_at__extract_year AS paid_at__extract_year + , subq_1.paid_at__extract_quarter AS paid_at__extract_quarter + , subq_1.paid_at__extract_month AS paid_at__extract_month + , subq_1.paid_at__extract_day AS paid_at__extract_day + , subq_1.paid_at__extract_dow AS paid_at__extract_dow + , subq_1.paid_at__extract_doy AS paid_at__extract_doy + , subq_1.booking__ds__day AS booking__ds__day + , subq_1.booking__ds__week AS booking__ds__week + , subq_1.booking__ds__month AS booking__ds__month + , subq_1.booking__ds__quarter AS booking__ds__quarter + , subq_1.booking__ds__year AS booking__ds__year + , subq_1.booking__ds__extract_year AS booking__ds__extract_year + , subq_1.booking__ds__extract_quarter AS booking__ds__extract_quarter + , subq_1.booking__ds__extract_month AS booking__ds__extract_month + , subq_1.booking__ds__extract_day AS booking__ds__extract_day + , subq_1.booking__ds__extract_dow AS booking__ds__extract_dow + , subq_1.booking__ds__extract_doy AS booking__ds__extract_doy + , subq_1.booking__ds_partitioned__day AS booking__ds_partitioned__day + , subq_1.booking__ds_partitioned__week AS booking__ds_partitioned__week + , subq_1.booking__ds_partitioned__month AS booking__ds_partitioned__month + , subq_1.booking__ds_partitioned__quarter AS booking__ds_partitioned__quarter + , subq_1.booking__ds_partitioned__year AS booking__ds_partitioned__year + , subq_1.booking__ds_partitioned__extract_year AS booking__ds_partitioned__extract_year + , subq_1.booking__ds_partitioned__extract_quarter AS booking__ds_partitioned__extract_quarter + , subq_1.booking__ds_partitioned__extract_month AS booking__ds_partitioned__extract_month + , subq_1.booking__ds_partitioned__extract_day AS booking__ds_partitioned__extract_day + , subq_1.booking__ds_partitioned__extract_dow AS booking__ds_partitioned__extract_dow + , subq_1.booking__ds_partitioned__extract_doy AS booking__ds_partitioned__extract_doy + , subq_1.booking__paid_at__day AS booking__paid_at__day + , subq_1.booking__paid_at__week AS booking__paid_at__week + , subq_1.booking__paid_at__month AS booking__paid_at__month + , subq_1.booking__paid_at__quarter AS booking__paid_at__quarter + , subq_1.booking__paid_at__year AS booking__paid_at__year + , subq_1.booking__paid_at__extract_year AS booking__paid_at__extract_year + , subq_1.booking__paid_at__extract_quarter AS booking__paid_at__extract_quarter + , subq_1.booking__paid_at__extract_month AS booking__paid_at__extract_month + , subq_1.booking__paid_at__extract_day AS booking__paid_at__extract_day + , subq_1.booking__paid_at__extract_dow AS booking__paid_at__extract_dow + , subq_1.booking__paid_at__extract_doy AS booking__paid_at__extract_doy + , subq_1.metric_time__week AS metric_time__week + , subq_1.metric_time__month AS metric_time__month + , subq_1.metric_time__quarter AS metric_time__quarter + , subq_1.metric_time__year AS metric_time__year + , subq_1.metric_time__extract_year AS metric_time__extract_year + , subq_1.metric_time__extract_quarter AS metric_time__extract_quarter + , subq_1.metric_time__extract_month AS metric_time__extract_month + , subq_1.metric_time__extract_day AS metric_time__extract_day + , subq_1.metric_time__extract_dow AS metric_time__extract_dow + , subq_1.metric_time__extract_doy AS metric_time__extract_doy + , subq_1.listing AS listing + , subq_1.guest AS guest + , subq_1.host AS host + , subq_1.booking__listing AS booking__listing + , subq_1.booking__guest AS booking__guest + , subq_1.booking__host AS booking__host + , subq_1.is_instant AS is_instant + , subq_1.booking__is_instant AS booking__is_instant + , subq_1.bookings AS bookings + , subq_1.instant_bookings AS instant_bookings + , subq_1.booking_value AS booking_value + , subq_1.max_booking_value AS max_booking_value + , subq_1.min_booking_value AS min_booking_value + , subq_1.bookers AS bookers + , subq_1.average_booking_value AS average_booking_value + , subq_1.referred_bookings AS referred_bookings + , subq_1.median_booking_value AS median_booking_value + , subq_1.booking_value_p99 AS booking_value_p99 + , subq_1.discrete_booking_value_p99 AS discrete_booking_value_p99 + , subq_1.approximate_continuous_booking_value_p99 AS approximate_continuous_booking_value_p99 + , subq_1.approximate_discrete_booking_value_p99 AS approximate_discrete_booking_value_p99 + FROM ( + -- Date Spine + SELECT + subq_3.ds AS metric_time__day + FROM ***************************.mf_time_spine subq_3 + ) subq_2 + INNER JOIN ( + -- Metric Time Dimension 'ds' + SELECT + subq_0.ds__day + , subq_0.ds__week + , subq_0.ds__month + , subq_0.ds__quarter + , subq_0.ds__year + , subq_0.ds__extract_year + , subq_0.ds__extract_quarter + , subq_0.ds__extract_month + , subq_0.ds__extract_day + , subq_0.ds__extract_dow + , subq_0.ds__extract_doy + , subq_0.ds_partitioned__day + , subq_0.ds_partitioned__week + , subq_0.ds_partitioned__month + , subq_0.ds_partitioned__quarter + , subq_0.ds_partitioned__year + , subq_0.ds_partitioned__extract_year + , subq_0.ds_partitioned__extract_quarter + , subq_0.ds_partitioned__extract_month + , subq_0.ds_partitioned__extract_day + , subq_0.ds_partitioned__extract_dow + , subq_0.ds_partitioned__extract_doy + , subq_0.paid_at__day + , subq_0.paid_at__week + , subq_0.paid_at__month + , subq_0.paid_at__quarter + , subq_0.paid_at__year + , subq_0.paid_at__extract_year + , subq_0.paid_at__extract_quarter + , subq_0.paid_at__extract_month + , subq_0.paid_at__extract_day + , subq_0.paid_at__extract_dow + , subq_0.paid_at__extract_doy + , subq_0.booking__ds__day + , subq_0.booking__ds__week + , subq_0.booking__ds__month + , subq_0.booking__ds__quarter + , subq_0.booking__ds__year + , subq_0.booking__ds__extract_year + , subq_0.booking__ds__extract_quarter + , subq_0.booking__ds__extract_month + , subq_0.booking__ds__extract_day + , subq_0.booking__ds__extract_dow + , subq_0.booking__ds__extract_doy + , subq_0.booking__ds_partitioned__day + , subq_0.booking__ds_partitioned__week + , subq_0.booking__ds_partitioned__month + , subq_0.booking__ds_partitioned__quarter + , subq_0.booking__ds_partitioned__year + , subq_0.booking__ds_partitioned__extract_year + , subq_0.booking__ds_partitioned__extract_quarter + , subq_0.booking__ds_partitioned__extract_month + , subq_0.booking__ds_partitioned__extract_day + , subq_0.booking__ds_partitioned__extract_dow + , subq_0.booking__ds_partitioned__extract_doy + , subq_0.booking__paid_at__day + , subq_0.booking__paid_at__week + , subq_0.booking__paid_at__month + , subq_0.booking__paid_at__quarter + , subq_0.booking__paid_at__year + , subq_0.booking__paid_at__extract_year + , subq_0.booking__paid_at__extract_quarter + , subq_0.booking__paid_at__extract_month + , subq_0.booking__paid_at__extract_day + , subq_0.booking__paid_at__extract_dow + , subq_0.booking__paid_at__extract_doy + , subq_0.ds__day AS metric_time__day + , subq_0.ds__week AS metric_time__week + , subq_0.ds__month AS metric_time__month + , subq_0.ds__quarter AS metric_time__quarter + , subq_0.ds__year AS metric_time__year + , subq_0.ds__extract_year AS metric_time__extract_year + , subq_0.ds__extract_quarter AS metric_time__extract_quarter + , subq_0.ds__extract_month AS metric_time__extract_month + , subq_0.ds__extract_day AS metric_time__extract_day + , subq_0.ds__extract_dow AS metric_time__extract_dow + , subq_0.ds__extract_doy AS metric_time__extract_doy + , subq_0.listing + , subq_0.guest + , subq_0.host + , subq_0.booking__listing + , subq_0.booking__guest + , subq_0.booking__host + , subq_0.is_instant + , subq_0.booking__is_instant + , subq_0.bookings + , subq_0.instant_bookings + , subq_0.booking_value + , subq_0.max_booking_value + , subq_0.min_booking_value + , subq_0.bookers + , subq_0.average_booking_value + , subq_0.referred_bookings + , subq_0.median_booking_value + , subq_0.booking_value_p99 + , subq_0.discrete_booking_value_p99 + , subq_0.approximate_continuous_booking_value_p99 + , subq_0.approximate_discrete_booking_value_p99 + FROM ( + -- Read Elements From Semantic Model 'bookings_source' + SELECT + 1 AS bookings + , CASE WHEN is_instant THEN 1 ELSE 0 END AS instant_bookings + , bookings_source_src_10001.booking_value + , bookings_source_src_10001.booking_value AS max_booking_value + , bookings_source_src_10001.booking_value AS min_booking_value + , bookings_source_src_10001.guest_id AS bookers + , bookings_source_src_10001.booking_value AS average_booking_value + , bookings_source_src_10001.booking_value AS booking_payments + , CASE WHEN referrer_id IS NOT NULL THEN 1 ELSE 0 END AS referred_bookings + , bookings_source_src_10001.booking_value AS median_booking_value + , bookings_source_src_10001.booking_value AS booking_value_p99 + , bookings_source_src_10001.booking_value AS discrete_booking_value_p99 + , bookings_source_src_10001.booking_value AS approximate_continuous_booking_value_p99 + , bookings_source_src_10001.booking_value AS approximate_discrete_booking_value_p99 + , bookings_source_src_10001.is_instant + , DATE_TRUNC('day', bookings_source_src_10001.ds) AS ds__day + , DATE_TRUNC('week', bookings_source_src_10001.ds) AS ds__week + , DATE_TRUNC('month', bookings_source_src_10001.ds) AS ds__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds) AS ds__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds) AS ds__year + , EXTRACT(year FROM bookings_source_src_10001.ds) AS ds__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds) AS ds__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds) AS ds__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds) AS ds__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds) AS ds__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds) AS ds__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__day + , DATE_TRUNC('week', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__week + , DATE_TRUNC('month', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__year + , EXTRACT(year FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.paid_at) AS paid_at__day + , DATE_TRUNC('week', bookings_source_src_10001.paid_at) AS paid_at__week + , DATE_TRUNC('month', bookings_source_src_10001.paid_at) AS paid_at__month + , DATE_TRUNC('quarter', bookings_source_src_10001.paid_at) AS paid_at__quarter + , DATE_TRUNC('year', bookings_source_src_10001.paid_at) AS paid_at__year + , EXTRACT(year FROM bookings_source_src_10001.paid_at) AS paid_at__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.paid_at) AS paid_at__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.paid_at) AS paid_at__extract_month + , EXTRACT(day FROM bookings_source_src_10001.paid_at) AS paid_at__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.paid_at) AS paid_at__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.paid_at) AS paid_at__extract_doy + , bookings_source_src_10001.is_instant AS booking__is_instant + , DATE_TRUNC('day', bookings_source_src_10001.ds) AS booking__ds__day + , DATE_TRUNC('week', bookings_source_src_10001.ds) AS booking__ds__week + , DATE_TRUNC('month', bookings_source_src_10001.ds) AS booking__ds__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds) AS booking__ds__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds) AS booking__ds__year + , EXTRACT(year FROM bookings_source_src_10001.ds) AS booking__ds__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds) AS booking__ds__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds) AS booking__ds__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds) AS booking__ds__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds) AS booking__ds__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds) AS booking__ds__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__day + , DATE_TRUNC('week', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__week + , DATE_TRUNC('month', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__year + , EXTRACT(year FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.paid_at) AS booking__paid_at__day + , DATE_TRUNC('week', bookings_source_src_10001.paid_at) AS booking__paid_at__week + , DATE_TRUNC('month', bookings_source_src_10001.paid_at) AS booking__paid_at__month + , DATE_TRUNC('quarter', bookings_source_src_10001.paid_at) AS booking__paid_at__quarter + , DATE_TRUNC('year', bookings_source_src_10001.paid_at) AS booking__paid_at__year + , EXTRACT(year FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_month + , EXTRACT(day FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_doy + , bookings_source_src_10001.listing_id AS listing + , bookings_source_src_10001.guest_id AS guest + , bookings_source_src_10001.host_id AS host + , bookings_source_src_10001.listing_id AS booking__listing + , bookings_source_src_10001.guest_id AS booking__guest + , bookings_source_src_10001.host_id AS booking__host + FROM ***************************.fct_bookings bookings_source_src_10001 + ) subq_0 + ) subq_1 + ON + ( + subq_1.metric_time__day <= subq_2.metric_time__day + ) AND ( + subq_1.metric_time__day > subq_2.metric_time__day - INTERVAL 2 day + ) + ) subq_4 + ) subq_5 + GROUP BY + subq_5.metric_time__day + ) subq_6 + ON + subq_7.metric_time__day = subq_6.metric_time__day +) subq_9 diff --git a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_cumulative_fill_nulls__plan0_optimized.sql b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_cumulative_fill_nulls__plan0_optimized.sql new file mode 100644 index 0000000000..1a9f8bb222 --- /dev/null +++ b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_cumulative_fill_nulls__plan0_optimized.sql @@ -0,0 +1,33 @@ +-- Compute Metrics via Expressions +SELECT + metric_time__day + , COALESCE(bookers, 0) AS every_two_days_bookers_fill_0 +FROM ( + -- Join to Time Spine Dataset + SELECT + subq_18.ds AS metric_time__day + , subq_16.bookers AS bookers + FROM ***************************.mf_time_spine subq_18 + LEFT OUTER JOIN ( + -- Join Self Over Time Range + -- Pass Only Elements: + -- ['bookers', 'metric_time__day'] + -- Aggregate Measures + SELECT + subq_13.ds AS metric_time__day + , COUNT(DISTINCT bookings_source_src_10001.guest_id) AS bookers + FROM ***************************.mf_time_spine subq_13 + INNER JOIN + ***************************.fct_bookings bookings_source_src_10001 + ON + ( + DATE_TRUNC('day', bookings_source_src_10001.ds) <= subq_13.ds + ) AND ( + DATE_TRUNC('day', bookings_source_src_10001.ds) > subq_13.ds - INTERVAL 2 day + ) + GROUP BY + subq_13.ds + ) subq_16 + ON + subq_18.ds = subq_16.metric_time__day +) subq_19 diff --git a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_derived_fill_nulls_for_one_input_metric__plan0.sql b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_derived_fill_nulls_for_one_input_metric__plan0.sql new file mode 100644 index 0000000000..e29d76ac94 --- /dev/null +++ b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_derived_fill_nulls_for_one_input_metric__plan0.sql @@ -0,0 +1,567 @@ +-- Compute Metrics via Expressions +SELECT + subq_16.metric_time__day + , bookings_fill_0 - bookings_2_weeks_ago AS bookings_growth_2_weeks_fill_0_for_non_offset +FROM ( + -- Combine Metrics + SELECT + COALESCE(subq_7.metric_time__day, subq_15.metric_time__day) AS metric_time__day + , subq_7.bookings_fill_0 AS bookings_fill_0 + , subq_15.bookings_2_weeks_ago AS bookings_2_weeks_ago + FROM ( + -- Compute Metrics via Expressions + SELECT + subq_6.metric_time__day + , COALESCE(subq_6.bookings, 0) AS bookings_fill_0 + FROM ( + -- Join to Time Spine Dataset + SELECT + subq_4.metric_time__day AS metric_time__day + , subq_3.bookings AS bookings + FROM ( + -- Date Spine + SELECT + subq_5.ds AS metric_time__day + FROM ***************************.mf_time_spine subq_5 + ) subq_4 + LEFT OUTER JOIN ( + -- Aggregate Measures + SELECT + subq_2.metric_time__day + , SUM(subq_2.bookings) AS bookings + FROM ( + -- Pass Only Elements: + -- ['bookings', 'metric_time__day'] + SELECT + subq_1.metric_time__day + , subq_1.bookings + FROM ( + -- Metric Time Dimension 'ds' + SELECT + subq_0.ds__day + , subq_0.ds__week + , subq_0.ds__month + , subq_0.ds__quarter + , subq_0.ds__year + , subq_0.ds__extract_year + , subq_0.ds__extract_quarter + , subq_0.ds__extract_month + , subq_0.ds__extract_day + , subq_0.ds__extract_dow + , subq_0.ds__extract_doy + , subq_0.ds_partitioned__day + , subq_0.ds_partitioned__week + , subq_0.ds_partitioned__month + , subq_0.ds_partitioned__quarter + , subq_0.ds_partitioned__year + , subq_0.ds_partitioned__extract_year + , subq_0.ds_partitioned__extract_quarter + , subq_0.ds_partitioned__extract_month + , subq_0.ds_partitioned__extract_day + , subq_0.ds_partitioned__extract_dow + , subq_0.ds_partitioned__extract_doy + , subq_0.paid_at__day + , subq_0.paid_at__week + , subq_0.paid_at__month + , subq_0.paid_at__quarter + , subq_0.paid_at__year + , subq_0.paid_at__extract_year + , subq_0.paid_at__extract_quarter + , subq_0.paid_at__extract_month + , subq_0.paid_at__extract_day + , subq_0.paid_at__extract_dow + , subq_0.paid_at__extract_doy + , subq_0.booking__ds__day + , subq_0.booking__ds__week + , subq_0.booking__ds__month + , subq_0.booking__ds__quarter + , subq_0.booking__ds__year + , subq_0.booking__ds__extract_year + , subq_0.booking__ds__extract_quarter + , subq_0.booking__ds__extract_month + , subq_0.booking__ds__extract_day + , subq_0.booking__ds__extract_dow + , subq_0.booking__ds__extract_doy + , subq_0.booking__ds_partitioned__day + , subq_0.booking__ds_partitioned__week + , subq_0.booking__ds_partitioned__month + , subq_0.booking__ds_partitioned__quarter + , subq_0.booking__ds_partitioned__year + , subq_0.booking__ds_partitioned__extract_year + , subq_0.booking__ds_partitioned__extract_quarter + , subq_0.booking__ds_partitioned__extract_month + , subq_0.booking__ds_partitioned__extract_day + , subq_0.booking__ds_partitioned__extract_dow + , subq_0.booking__ds_partitioned__extract_doy + , subq_0.booking__paid_at__day + , subq_0.booking__paid_at__week + , subq_0.booking__paid_at__month + , subq_0.booking__paid_at__quarter + , subq_0.booking__paid_at__year + , subq_0.booking__paid_at__extract_year + , subq_0.booking__paid_at__extract_quarter + , subq_0.booking__paid_at__extract_month + , subq_0.booking__paid_at__extract_day + , subq_0.booking__paid_at__extract_dow + , subq_0.booking__paid_at__extract_doy + , subq_0.ds__day AS metric_time__day + , subq_0.ds__week AS metric_time__week + , subq_0.ds__month AS metric_time__month + , subq_0.ds__quarter AS metric_time__quarter + , subq_0.ds__year AS metric_time__year + , subq_0.ds__extract_year AS metric_time__extract_year + , subq_0.ds__extract_quarter AS metric_time__extract_quarter + , subq_0.ds__extract_month AS metric_time__extract_month + , subq_0.ds__extract_day AS metric_time__extract_day + , subq_0.ds__extract_dow AS metric_time__extract_dow + , subq_0.ds__extract_doy AS metric_time__extract_doy + , subq_0.listing + , subq_0.guest + , subq_0.host + , subq_0.booking__listing + , subq_0.booking__guest + , subq_0.booking__host + , subq_0.is_instant + , subq_0.booking__is_instant + , subq_0.bookings + , subq_0.instant_bookings + , subq_0.booking_value + , subq_0.max_booking_value + , subq_0.min_booking_value + , subq_0.bookers + , subq_0.average_booking_value + , subq_0.referred_bookings + , subq_0.median_booking_value + , subq_0.booking_value_p99 + , subq_0.discrete_booking_value_p99 + , subq_0.approximate_continuous_booking_value_p99 + , subq_0.approximate_discrete_booking_value_p99 + FROM ( + -- Read Elements From Semantic Model 'bookings_source' + SELECT + 1 AS bookings + , CASE WHEN is_instant THEN 1 ELSE 0 END AS instant_bookings + , bookings_source_src_10001.booking_value + , bookings_source_src_10001.booking_value AS max_booking_value + , bookings_source_src_10001.booking_value AS min_booking_value + , bookings_source_src_10001.guest_id AS bookers + , bookings_source_src_10001.booking_value AS average_booking_value + , bookings_source_src_10001.booking_value AS booking_payments + , CASE WHEN referrer_id IS NOT NULL THEN 1 ELSE 0 END AS referred_bookings + , bookings_source_src_10001.booking_value AS median_booking_value + , bookings_source_src_10001.booking_value AS booking_value_p99 + , bookings_source_src_10001.booking_value AS discrete_booking_value_p99 + , bookings_source_src_10001.booking_value AS approximate_continuous_booking_value_p99 + , bookings_source_src_10001.booking_value AS approximate_discrete_booking_value_p99 + , bookings_source_src_10001.is_instant + , DATE_TRUNC('day', bookings_source_src_10001.ds) AS ds__day + , DATE_TRUNC('week', bookings_source_src_10001.ds) AS ds__week + , DATE_TRUNC('month', bookings_source_src_10001.ds) AS ds__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds) AS ds__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds) AS ds__year + , EXTRACT(year FROM bookings_source_src_10001.ds) AS ds__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds) AS ds__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds) AS ds__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds) AS ds__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds) AS ds__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds) AS ds__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__day + , DATE_TRUNC('week', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__week + , DATE_TRUNC('month', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__year + , EXTRACT(year FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.paid_at) AS paid_at__day + , DATE_TRUNC('week', bookings_source_src_10001.paid_at) AS paid_at__week + , DATE_TRUNC('month', bookings_source_src_10001.paid_at) AS paid_at__month + , DATE_TRUNC('quarter', bookings_source_src_10001.paid_at) AS paid_at__quarter + , DATE_TRUNC('year', bookings_source_src_10001.paid_at) AS paid_at__year + , EXTRACT(year FROM bookings_source_src_10001.paid_at) AS paid_at__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.paid_at) AS paid_at__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.paid_at) AS paid_at__extract_month + , EXTRACT(day FROM bookings_source_src_10001.paid_at) AS paid_at__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.paid_at) AS paid_at__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.paid_at) AS paid_at__extract_doy + , bookings_source_src_10001.is_instant AS booking__is_instant + , DATE_TRUNC('day', bookings_source_src_10001.ds) AS booking__ds__day + , DATE_TRUNC('week', bookings_source_src_10001.ds) AS booking__ds__week + , DATE_TRUNC('month', bookings_source_src_10001.ds) AS booking__ds__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds) AS booking__ds__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds) AS booking__ds__year + , EXTRACT(year FROM bookings_source_src_10001.ds) AS booking__ds__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds) AS booking__ds__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds) AS booking__ds__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds) AS booking__ds__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds) AS booking__ds__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds) AS booking__ds__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__day + , DATE_TRUNC('week', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__week + , DATE_TRUNC('month', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__year + , EXTRACT(year FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.paid_at) AS booking__paid_at__day + , DATE_TRUNC('week', bookings_source_src_10001.paid_at) AS booking__paid_at__week + , DATE_TRUNC('month', bookings_source_src_10001.paid_at) AS booking__paid_at__month + , DATE_TRUNC('quarter', bookings_source_src_10001.paid_at) AS booking__paid_at__quarter + , DATE_TRUNC('year', bookings_source_src_10001.paid_at) AS booking__paid_at__year + , EXTRACT(year FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_month + , EXTRACT(day FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_doy + , bookings_source_src_10001.listing_id AS listing + , bookings_source_src_10001.guest_id AS guest + , bookings_source_src_10001.host_id AS host + , bookings_source_src_10001.listing_id AS booking__listing + , bookings_source_src_10001.guest_id AS booking__guest + , bookings_source_src_10001.host_id AS booking__host + FROM ***************************.fct_bookings bookings_source_src_10001 + ) subq_0 + ) subq_1 + ) subq_2 + GROUP BY + subq_2.metric_time__day + ) subq_3 + ON + subq_4.metric_time__day = subq_3.metric_time__day + ) subq_6 + ) subq_7 + INNER JOIN ( + -- Compute Metrics via Expressions + SELECT + subq_14.metric_time__day + , subq_14.bookings AS bookings_2_weeks_ago + FROM ( + -- Aggregate Measures + SELECT + subq_13.metric_time__day + , SUM(subq_13.bookings) AS bookings + FROM ( + -- Pass Only Elements: + -- ['bookings', 'metric_time__day'] + SELECT + subq_12.metric_time__day + , subq_12.bookings + FROM ( + -- Join to Time Spine Dataset + SELECT + subq_10.metric_time__day AS metric_time__day + , subq_9.ds__day AS ds__day + , subq_9.ds__week AS ds__week + , subq_9.ds__month AS ds__month + , subq_9.ds__quarter AS ds__quarter + , subq_9.ds__year AS ds__year + , subq_9.ds__extract_year AS ds__extract_year + , subq_9.ds__extract_quarter AS ds__extract_quarter + , subq_9.ds__extract_month AS ds__extract_month + , subq_9.ds__extract_day AS ds__extract_day + , subq_9.ds__extract_dow AS ds__extract_dow + , subq_9.ds__extract_doy AS ds__extract_doy + , subq_9.ds_partitioned__day AS ds_partitioned__day + , subq_9.ds_partitioned__week AS ds_partitioned__week + , subq_9.ds_partitioned__month AS ds_partitioned__month + , subq_9.ds_partitioned__quarter AS ds_partitioned__quarter + , subq_9.ds_partitioned__year AS ds_partitioned__year + , subq_9.ds_partitioned__extract_year AS ds_partitioned__extract_year + , subq_9.ds_partitioned__extract_quarter AS ds_partitioned__extract_quarter + , subq_9.ds_partitioned__extract_month AS ds_partitioned__extract_month + , subq_9.ds_partitioned__extract_day AS ds_partitioned__extract_day + , subq_9.ds_partitioned__extract_dow AS ds_partitioned__extract_dow + , subq_9.ds_partitioned__extract_doy AS ds_partitioned__extract_doy + , subq_9.paid_at__day AS paid_at__day + , subq_9.paid_at__week AS paid_at__week + , subq_9.paid_at__month AS paid_at__month + , subq_9.paid_at__quarter AS paid_at__quarter + , subq_9.paid_at__year AS paid_at__year + , subq_9.paid_at__extract_year AS paid_at__extract_year + , subq_9.paid_at__extract_quarter AS paid_at__extract_quarter + , subq_9.paid_at__extract_month AS paid_at__extract_month + , subq_9.paid_at__extract_day AS paid_at__extract_day + , subq_9.paid_at__extract_dow AS paid_at__extract_dow + , subq_9.paid_at__extract_doy AS paid_at__extract_doy + , subq_9.booking__ds__day AS booking__ds__day + , subq_9.booking__ds__week AS booking__ds__week + , subq_9.booking__ds__month AS booking__ds__month + , subq_9.booking__ds__quarter AS booking__ds__quarter + , subq_9.booking__ds__year AS booking__ds__year + , subq_9.booking__ds__extract_year AS booking__ds__extract_year + , subq_9.booking__ds__extract_quarter AS booking__ds__extract_quarter + , subq_9.booking__ds__extract_month AS booking__ds__extract_month + , subq_9.booking__ds__extract_day AS booking__ds__extract_day + , subq_9.booking__ds__extract_dow AS booking__ds__extract_dow + , subq_9.booking__ds__extract_doy AS booking__ds__extract_doy + , subq_9.booking__ds_partitioned__day AS booking__ds_partitioned__day + , subq_9.booking__ds_partitioned__week AS booking__ds_partitioned__week + , subq_9.booking__ds_partitioned__month AS booking__ds_partitioned__month + , subq_9.booking__ds_partitioned__quarter AS booking__ds_partitioned__quarter + , subq_9.booking__ds_partitioned__year AS booking__ds_partitioned__year + , subq_9.booking__ds_partitioned__extract_year AS booking__ds_partitioned__extract_year + , subq_9.booking__ds_partitioned__extract_quarter AS booking__ds_partitioned__extract_quarter + , subq_9.booking__ds_partitioned__extract_month AS booking__ds_partitioned__extract_month + , subq_9.booking__ds_partitioned__extract_day AS booking__ds_partitioned__extract_day + , subq_9.booking__ds_partitioned__extract_dow AS booking__ds_partitioned__extract_dow + , subq_9.booking__ds_partitioned__extract_doy AS booking__ds_partitioned__extract_doy + , subq_9.booking__paid_at__day AS booking__paid_at__day + , subq_9.booking__paid_at__week AS booking__paid_at__week + , subq_9.booking__paid_at__month AS booking__paid_at__month + , subq_9.booking__paid_at__quarter AS booking__paid_at__quarter + , subq_9.booking__paid_at__year AS booking__paid_at__year + , subq_9.booking__paid_at__extract_year AS booking__paid_at__extract_year + , subq_9.booking__paid_at__extract_quarter AS booking__paid_at__extract_quarter + , subq_9.booking__paid_at__extract_month AS booking__paid_at__extract_month + , subq_9.booking__paid_at__extract_day AS booking__paid_at__extract_day + , subq_9.booking__paid_at__extract_dow AS booking__paid_at__extract_dow + , subq_9.booking__paid_at__extract_doy AS booking__paid_at__extract_doy + , subq_9.listing AS listing + , subq_9.guest AS guest + , subq_9.host AS host + , subq_9.booking__listing AS booking__listing + , subq_9.booking__guest AS booking__guest + , subq_9.booking__host AS booking__host + , subq_9.is_instant AS is_instant + , subq_9.booking__is_instant AS booking__is_instant + , subq_9.bookings AS bookings + , subq_9.instant_bookings AS instant_bookings + , subq_9.booking_value AS booking_value + , subq_9.max_booking_value AS max_booking_value + , subq_9.min_booking_value AS min_booking_value + , subq_9.bookers AS bookers + , subq_9.average_booking_value AS average_booking_value + , subq_9.referred_bookings AS referred_bookings + , subq_9.median_booking_value AS median_booking_value + , subq_9.booking_value_p99 AS booking_value_p99 + , subq_9.discrete_booking_value_p99 AS discrete_booking_value_p99 + , subq_9.approximate_continuous_booking_value_p99 AS approximate_continuous_booking_value_p99 + , subq_9.approximate_discrete_booking_value_p99 AS approximate_discrete_booking_value_p99 + FROM ( + -- Date Spine + SELECT + subq_11.ds AS metric_time__day + FROM ***************************.mf_time_spine subq_11 + ) subq_10 + INNER JOIN ( + -- Metric Time Dimension 'ds' + SELECT + subq_8.ds__day + , subq_8.ds__week + , subq_8.ds__month + , subq_8.ds__quarter + , subq_8.ds__year + , subq_8.ds__extract_year + , subq_8.ds__extract_quarter + , subq_8.ds__extract_month + , subq_8.ds__extract_day + , subq_8.ds__extract_dow + , subq_8.ds__extract_doy + , subq_8.ds_partitioned__day + , subq_8.ds_partitioned__week + , subq_8.ds_partitioned__month + , subq_8.ds_partitioned__quarter + , subq_8.ds_partitioned__year + , subq_8.ds_partitioned__extract_year + , subq_8.ds_partitioned__extract_quarter + , subq_8.ds_partitioned__extract_month + , subq_8.ds_partitioned__extract_day + , subq_8.ds_partitioned__extract_dow + , subq_8.ds_partitioned__extract_doy + , subq_8.paid_at__day + , subq_8.paid_at__week + , subq_8.paid_at__month + , subq_8.paid_at__quarter + , subq_8.paid_at__year + , subq_8.paid_at__extract_year + , subq_8.paid_at__extract_quarter + , subq_8.paid_at__extract_month + , subq_8.paid_at__extract_day + , subq_8.paid_at__extract_dow + , subq_8.paid_at__extract_doy + , subq_8.booking__ds__day + , subq_8.booking__ds__week + , subq_8.booking__ds__month + , subq_8.booking__ds__quarter + , subq_8.booking__ds__year + , subq_8.booking__ds__extract_year + , subq_8.booking__ds__extract_quarter + , subq_8.booking__ds__extract_month + , subq_8.booking__ds__extract_day + , subq_8.booking__ds__extract_dow + , subq_8.booking__ds__extract_doy + , subq_8.booking__ds_partitioned__day + , subq_8.booking__ds_partitioned__week + , subq_8.booking__ds_partitioned__month + , subq_8.booking__ds_partitioned__quarter + , subq_8.booking__ds_partitioned__year + , subq_8.booking__ds_partitioned__extract_year + , subq_8.booking__ds_partitioned__extract_quarter + , subq_8.booking__ds_partitioned__extract_month + , subq_8.booking__ds_partitioned__extract_day + , subq_8.booking__ds_partitioned__extract_dow + , subq_8.booking__ds_partitioned__extract_doy + , subq_8.booking__paid_at__day + , subq_8.booking__paid_at__week + , subq_8.booking__paid_at__month + , subq_8.booking__paid_at__quarter + , subq_8.booking__paid_at__year + , subq_8.booking__paid_at__extract_year + , subq_8.booking__paid_at__extract_quarter + , subq_8.booking__paid_at__extract_month + , subq_8.booking__paid_at__extract_day + , subq_8.booking__paid_at__extract_dow + , subq_8.booking__paid_at__extract_doy + , subq_8.ds__day AS metric_time__day + , subq_8.ds__week AS metric_time__week + , subq_8.ds__month AS metric_time__month + , subq_8.ds__quarter AS metric_time__quarter + , subq_8.ds__year AS metric_time__year + , subq_8.ds__extract_year AS metric_time__extract_year + , subq_8.ds__extract_quarter AS metric_time__extract_quarter + , subq_8.ds__extract_month AS metric_time__extract_month + , subq_8.ds__extract_day AS metric_time__extract_day + , subq_8.ds__extract_dow AS metric_time__extract_dow + , subq_8.ds__extract_doy AS metric_time__extract_doy + , subq_8.listing + , subq_8.guest + , subq_8.host + , subq_8.booking__listing + , subq_8.booking__guest + , subq_8.booking__host + , subq_8.is_instant + , subq_8.booking__is_instant + , subq_8.bookings + , subq_8.instant_bookings + , subq_8.booking_value + , subq_8.max_booking_value + , subq_8.min_booking_value + , subq_8.bookers + , subq_8.average_booking_value + , subq_8.referred_bookings + , subq_8.median_booking_value + , subq_8.booking_value_p99 + , subq_8.discrete_booking_value_p99 + , subq_8.approximate_continuous_booking_value_p99 + , subq_8.approximate_discrete_booking_value_p99 + FROM ( + -- Read Elements From Semantic Model 'bookings_source' + SELECT + 1 AS bookings + , CASE WHEN is_instant THEN 1 ELSE 0 END AS instant_bookings + , bookings_source_src_10001.booking_value + , bookings_source_src_10001.booking_value AS max_booking_value + , bookings_source_src_10001.booking_value AS min_booking_value + , bookings_source_src_10001.guest_id AS bookers + , bookings_source_src_10001.booking_value AS average_booking_value + , bookings_source_src_10001.booking_value AS booking_payments + , CASE WHEN referrer_id IS NOT NULL THEN 1 ELSE 0 END AS referred_bookings + , bookings_source_src_10001.booking_value AS median_booking_value + , bookings_source_src_10001.booking_value AS booking_value_p99 + , bookings_source_src_10001.booking_value AS discrete_booking_value_p99 + , bookings_source_src_10001.booking_value AS approximate_continuous_booking_value_p99 + , bookings_source_src_10001.booking_value AS approximate_discrete_booking_value_p99 + , bookings_source_src_10001.is_instant + , DATE_TRUNC('day', bookings_source_src_10001.ds) AS ds__day + , DATE_TRUNC('week', bookings_source_src_10001.ds) AS ds__week + , DATE_TRUNC('month', bookings_source_src_10001.ds) AS ds__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds) AS ds__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds) AS ds__year + , EXTRACT(year FROM bookings_source_src_10001.ds) AS ds__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds) AS ds__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds) AS ds__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds) AS ds__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds) AS ds__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds) AS ds__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__day + , DATE_TRUNC('week', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__week + , DATE_TRUNC('month', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__year + , EXTRACT(year FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.paid_at) AS paid_at__day + , DATE_TRUNC('week', bookings_source_src_10001.paid_at) AS paid_at__week + , DATE_TRUNC('month', bookings_source_src_10001.paid_at) AS paid_at__month + , DATE_TRUNC('quarter', bookings_source_src_10001.paid_at) AS paid_at__quarter + , DATE_TRUNC('year', bookings_source_src_10001.paid_at) AS paid_at__year + , EXTRACT(year FROM bookings_source_src_10001.paid_at) AS paid_at__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.paid_at) AS paid_at__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.paid_at) AS paid_at__extract_month + , EXTRACT(day FROM bookings_source_src_10001.paid_at) AS paid_at__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.paid_at) AS paid_at__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.paid_at) AS paid_at__extract_doy + , bookings_source_src_10001.is_instant AS booking__is_instant + , DATE_TRUNC('day', bookings_source_src_10001.ds) AS booking__ds__day + , DATE_TRUNC('week', bookings_source_src_10001.ds) AS booking__ds__week + , DATE_TRUNC('month', bookings_source_src_10001.ds) AS booking__ds__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds) AS booking__ds__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds) AS booking__ds__year + , EXTRACT(year FROM bookings_source_src_10001.ds) AS booking__ds__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds) AS booking__ds__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds) AS booking__ds__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds) AS booking__ds__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds) AS booking__ds__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds) AS booking__ds__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__day + , DATE_TRUNC('week', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__week + , DATE_TRUNC('month', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__year + , EXTRACT(year FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.paid_at) AS booking__paid_at__day + , DATE_TRUNC('week', bookings_source_src_10001.paid_at) AS booking__paid_at__week + , DATE_TRUNC('month', bookings_source_src_10001.paid_at) AS booking__paid_at__month + , DATE_TRUNC('quarter', bookings_source_src_10001.paid_at) AS booking__paid_at__quarter + , DATE_TRUNC('year', bookings_source_src_10001.paid_at) AS booking__paid_at__year + , EXTRACT(year FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_month + , EXTRACT(day FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_doy + , bookings_source_src_10001.listing_id AS listing + , bookings_source_src_10001.guest_id AS guest + , bookings_source_src_10001.host_id AS host + , bookings_source_src_10001.listing_id AS booking__listing + , bookings_source_src_10001.guest_id AS booking__guest + , bookings_source_src_10001.host_id AS booking__host + FROM ***************************.fct_bookings bookings_source_src_10001 + ) subq_8 + ) subq_9 + ON + subq_10.metric_time__day - INTERVAL 14 day = subq_9.metric_time__day + ) subq_12 + ) subq_13 + GROUP BY + subq_13.metric_time__day + ) subq_14 + ) subq_15 + ON + ( + subq_7.metric_time__day = subq_15.metric_time__day + ) OR ( + ( + subq_7.metric_time__day IS NULL + ) AND ( + subq_15.metric_time__day IS NULL + ) + ) +) subq_16 diff --git a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_derived_fill_nulls_for_one_input_metric__plan0_optimized.sql b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_derived_fill_nulls_for_one_input_metric__plan0_optimized.sql new file mode 100644 index 0000000000..5d5be5399f --- /dev/null +++ b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_derived_fill_nulls_for_one_input_metric__plan0_optimized.sql @@ -0,0 +1,77 @@ +-- Compute Metrics via Expressions +SELECT + metric_time__day + , bookings_fill_0 - bookings_2_weeks_ago AS bookings_growth_2_weeks_fill_0_for_non_offset +FROM ( + -- Combine Metrics + SELECT + COALESCE(subq_24.metric_time__day, subq_32.metric_time__day) AS metric_time__day + , subq_24.bookings_fill_0 AS bookings_fill_0 + , subq_32.bookings_2_weeks_ago AS bookings_2_weeks_ago + FROM ( + -- Compute Metrics via Expressions + SELECT + metric_time__day + , COALESCE(bookings, 0) AS bookings_fill_0 + FROM ( + -- Join to Time Spine Dataset + SELECT + subq_22.ds AS metric_time__day + , subq_20.bookings AS bookings + FROM ***************************.mf_time_spine subq_22 + LEFT OUTER JOIN ( + -- Aggregate Measures + SELECT + metric_time__day + , SUM(bookings) AS bookings + FROM ( + -- Read Elements From Semantic Model 'bookings_source' + -- Metric Time Dimension 'ds' + -- Pass Only Elements: + -- ['bookings', 'metric_time__day'] + SELECT + DATE_TRUNC('day', ds) AS metric_time__day + , 1 AS bookings + FROM ***************************.fct_bookings bookings_source_src_10001 + ) subq_19 + GROUP BY + metric_time__day + ) subq_20 + ON + subq_22.ds = subq_20.metric_time__day + ) subq_23 + ) subq_24 + INNER JOIN ( + -- Join to Time Spine Dataset + -- Pass Only Elements: + -- ['bookings', 'metric_time__day'] + -- Aggregate Measures + -- Compute Metrics via Expressions + SELECT + subq_28.ds AS metric_time__day + , SUM(subq_26.bookings) AS bookings_2_weeks_ago + FROM ***************************.mf_time_spine subq_28 + INNER JOIN ( + -- Read Elements From Semantic Model 'bookings_source' + -- Metric Time Dimension 'ds' + SELECT + DATE_TRUNC('day', ds) AS metric_time__day + , 1 AS bookings + FROM ***************************.fct_bookings bookings_source_src_10001 + ) subq_26 + ON + subq_28.ds - INTERVAL 14 day = subq_26.metric_time__day + GROUP BY + subq_28.ds + ) subq_32 + ON + ( + subq_24.metric_time__day = subq_32.metric_time__day + ) OR ( + ( + subq_24.metric_time__day IS NULL + ) AND ( + subq_32.metric_time__day IS NULL + ) + ) +) subq_33 diff --git a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_0_with_categorical_dimension__plan0.sql b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_0_with_categorical_dimension__plan0.sql new file mode 100644 index 0000000000..cdc93afcf9 --- /dev/null +++ b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_0_with_categorical_dimension__plan0.sql @@ -0,0 +1,214 @@ +-- Compute Metrics via Expressions +SELECT + subq_3.booking__is_instant + , COALESCE(subq_3.bookings, 0) AS bookings_fill_0 +FROM ( + -- Aggregate Measures + SELECT + subq_2.booking__is_instant + , SUM(subq_2.bookings) AS bookings + FROM ( + -- Pass Only Elements: + -- ['bookings', 'booking__is_instant'] + SELECT + subq_1.booking__is_instant + , subq_1.bookings + FROM ( + -- Metric Time Dimension 'ds' + SELECT + subq_0.ds__day + , subq_0.ds__week + , subq_0.ds__month + , subq_0.ds__quarter + , subq_0.ds__year + , subq_0.ds__extract_year + , subq_0.ds__extract_quarter + , subq_0.ds__extract_month + , subq_0.ds__extract_day + , subq_0.ds__extract_dow + , subq_0.ds__extract_doy + , subq_0.ds_partitioned__day + , subq_0.ds_partitioned__week + , subq_0.ds_partitioned__month + , subq_0.ds_partitioned__quarter + , subq_0.ds_partitioned__year + , subq_0.ds_partitioned__extract_year + , subq_0.ds_partitioned__extract_quarter + , subq_0.ds_partitioned__extract_month + , subq_0.ds_partitioned__extract_day + , subq_0.ds_partitioned__extract_dow + , subq_0.ds_partitioned__extract_doy + , subq_0.paid_at__day + , subq_0.paid_at__week + , subq_0.paid_at__month + , subq_0.paid_at__quarter + , subq_0.paid_at__year + , subq_0.paid_at__extract_year + , subq_0.paid_at__extract_quarter + , subq_0.paid_at__extract_month + , subq_0.paid_at__extract_day + , subq_0.paid_at__extract_dow + , subq_0.paid_at__extract_doy + , subq_0.booking__ds__day + , subq_0.booking__ds__week + , subq_0.booking__ds__month + , subq_0.booking__ds__quarter + , subq_0.booking__ds__year + , subq_0.booking__ds__extract_year + , subq_0.booking__ds__extract_quarter + , subq_0.booking__ds__extract_month + , subq_0.booking__ds__extract_day + , subq_0.booking__ds__extract_dow + , subq_0.booking__ds__extract_doy + , subq_0.booking__ds_partitioned__day + , subq_0.booking__ds_partitioned__week + , subq_0.booking__ds_partitioned__month + , subq_0.booking__ds_partitioned__quarter + , subq_0.booking__ds_partitioned__year + , subq_0.booking__ds_partitioned__extract_year + , subq_0.booking__ds_partitioned__extract_quarter + , subq_0.booking__ds_partitioned__extract_month + , subq_0.booking__ds_partitioned__extract_day + , subq_0.booking__ds_partitioned__extract_dow + , subq_0.booking__ds_partitioned__extract_doy + , subq_0.booking__paid_at__day + , subq_0.booking__paid_at__week + , subq_0.booking__paid_at__month + , subq_0.booking__paid_at__quarter + , subq_0.booking__paid_at__year + , subq_0.booking__paid_at__extract_year + , subq_0.booking__paid_at__extract_quarter + , subq_0.booking__paid_at__extract_month + , subq_0.booking__paid_at__extract_day + , subq_0.booking__paid_at__extract_dow + , subq_0.booking__paid_at__extract_doy + , subq_0.ds__day AS metric_time__day + , subq_0.ds__week AS metric_time__week + , subq_0.ds__month AS metric_time__month + , subq_0.ds__quarter AS metric_time__quarter + , subq_0.ds__year AS metric_time__year + , subq_0.ds__extract_year AS metric_time__extract_year + , subq_0.ds__extract_quarter AS metric_time__extract_quarter + , subq_0.ds__extract_month AS metric_time__extract_month + , subq_0.ds__extract_day AS metric_time__extract_day + , subq_0.ds__extract_dow AS metric_time__extract_dow + , subq_0.ds__extract_doy AS metric_time__extract_doy + , subq_0.listing + , subq_0.guest + , subq_0.host + , subq_0.booking__listing + , subq_0.booking__guest + , subq_0.booking__host + , subq_0.is_instant + , subq_0.booking__is_instant + , subq_0.bookings + , subq_0.instant_bookings + , subq_0.booking_value + , subq_0.max_booking_value + , subq_0.min_booking_value + , subq_0.bookers + , subq_0.average_booking_value + , subq_0.referred_bookings + , subq_0.median_booking_value + , subq_0.booking_value_p99 + , subq_0.discrete_booking_value_p99 + , subq_0.approximate_continuous_booking_value_p99 + , subq_0.approximate_discrete_booking_value_p99 + FROM ( + -- Read Elements From Semantic Model 'bookings_source' + SELECT + 1 AS bookings + , CASE WHEN is_instant THEN 1 ELSE 0 END AS instant_bookings + , bookings_source_src_10001.booking_value + , bookings_source_src_10001.booking_value AS max_booking_value + , bookings_source_src_10001.booking_value AS min_booking_value + , bookings_source_src_10001.guest_id AS bookers + , bookings_source_src_10001.booking_value AS average_booking_value + , bookings_source_src_10001.booking_value AS booking_payments + , CASE WHEN referrer_id IS NOT NULL THEN 1 ELSE 0 END AS referred_bookings + , bookings_source_src_10001.booking_value AS median_booking_value + , bookings_source_src_10001.booking_value AS booking_value_p99 + , bookings_source_src_10001.booking_value AS discrete_booking_value_p99 + , bookings_source_src_10001.booking_value AS approximate_continuous_booking_value_p99 + , bookings_source_src_10001.booking_value AS approximate_discrete_booking_value_p99 + , bookings_source_src_10001.is_instant + , DATE_TRUNC('day', bookings_source_src_10001.ds) AS ds__day + , DATE_TRUNC('week', bookings_source_src_10001.ds) AS ds__week + , DATE_TRUNC('month', bookings_source_src_10001.ds) AS ds__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds) AS ds__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds) AS ds__year + , EXTRACT(year FROM bookings_source_src_10001.ds) AS ds__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds) AS ds__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds) AS ds__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds) AS ds__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds) AS ds__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds) AS ds__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__day + , DATE_TRUNC('week', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__week + , DATE_TRUNC('month', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__year + , EXTRACT(year FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.paid_at) AS paid_at__day + , DATE_TRUNC('week', bookings_source_src_10001.paid_at) AS paid_at__week + , DATE_TRUNC('month', bookings_source_src_10001.paid_at) AS paid_at__month + , DATE_TRUNC('quarter', bookings_source_src_10001.paid_at) AS paid_at__quarter + , DATE_TRUNC('year', bookings_source_src_10001.paid_at) AS paid_at__year + , EXTRACT(year FROM bookings_source_src_10001.paid_at) AS paid_at__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.paid_at) AS paid_at__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.paid_at) AS paid_at__extract_month + , EXTRACT(day FROM bookings_source_src_10001.paid_at) AS paid_at__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.paid_at) AS paid_at__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.paid_at) AS paid_at__extract_doy + , bookings_source_src_10001.is_instant AS booking__is_instant + , DATE_TRUNC('day', bookings_source_src_10001.ds) AS booking__ds__day + , DATE_TRUNC('week', bookings_source_src_10001.ds) AS booking__ds__week + , DATE_TRUNC('month', bookings_source_src_10001.ds) AS booking__ds__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds) AS booking__ds__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds) AS booking__ds__year + , EXTRACT(year FROM bookings_source_src_10001.ds) AS booking__ds__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds) AS booking__ds__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds) AS booking__ds__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds) AS booking__ds__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds) AS booking__ds__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds) AS booking__ds__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__day + , DATE_TRUNC('week', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__week + , DATE_TRUNC('month', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__year + , EXTRACT(year FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.paid_at) AS booking__paid_at__day + , DATE_TRUNC('week', bookings_source_src_10001.paid_at) AS booking__paid_at__week + , DATE_TRUNC('month', bookings_source_src_10001.paid_at) AS booking__paid_at__month + , DATE_TRUNC('quarter', bookings_source_src_10001.paid_at) AS booking__paid_at__quarter + , DATE_TRUNC('year', bookings_source_src_10001.paid_at) AS booking__paid_at__year + , EXTRACT(year FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_month + , EXTRACT(day FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_doy + , bookings_source_src_10001.listing_id AS listing + , bookings_source_src_10001.guest_id AS guest + , bookings_source_src_10001.host_id AS host + , bookings_source_src_10001.listing_id AS booking__listing + , bookings_source_src_10001.guest_id AS booking__guest + , bookings_source_src_10001.host_id AS booking__host + FROM ***************************.fct_bookings bookings_source_src_10001 + ) subq_0 + ) subq_1 + ) subq_2 + GROUP BY + subq_2.booking__is_instant +) subq_3 diff --git a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_0_with_categorical_dimension__plan0_optimized.sql b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_0_with_categorical_dimension__plan0_optimized.sql new file mode 100644 index 0000000000..137ea27829 --- /dev/null +++ b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_0_with_categorical_dimension__plan0_optimized.sql @@ -0,0 +1,22 @@ +-- Compute Metrics via Expressions +SELECT + booking__is_instant + , COALESCE(bookings, 0) AS bookings_fill_0 +FROM ( + -- Aggregate Measures + SELECT + booking__is_instant + , SUM(bookings) AS bookings + FROM ( + -- Read Elements From Semantic Model 'bookings_source' + -- Metric Time Dimension 'ds' + -- Pass Only Elements: + -- ['bookings', 'booking__is_instant'] + SELECT + is_instant AS booking__is_instant + , 1 AS bookings + FROM ***************************.fct_bookings bookings_source_src_10001 + ) subq_6 + GROUP BY + booking__is_instant +) subq_7 diff --git a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_0_with_non_metric_time__plan0.sql b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_0_with_non_metric_time__plan0.sql new file mode 100644 index 0000000000..46456a43c7 --- /dev/null +++ b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_0_with_non_metric_time__plan0.sql @@ -0,0 +1,214 @@ +-- Compute Metrics via Expressions +SELECT + subq_3.booking__paid_at__day + , COALESCE(subq_3.bookings, 0) AS bookings_fill_0 +FROM ( + -- Aggregate Measures + SELECT + subq_2.booking__paid_at__day + , SUM(subq_2.bookings) AS bookings + FROM ( + -- Pass Only Elements: + -- ['bookings', 'booking__paid_at__day'] + SELECT + subq_1.booking__paid_at__day + , subq_1.bookings + FROM ( + -- Metric Time Dimension 'ds' + SELECT + subq_0.ds__day + , subq_0.ds__week + , subq_0.ds__month + , subq_0.ds__quarter + , subq_0.ds__year + , subq_0.ds__extract_year + , subq_0.ds__extract_quarter + , subq_0.ds__extract_month + , subq_0.ds__extract_day + , subq_0.ds__extract_dow + , subq_0.ds__extract_doy + , subq_0.ds_partitioned__day + , subq_0.ds_partitioned__week + , subq_0.ds_partitioned__month + , subq_0.ds_partitioned__quarter + , subq_0.ds_partitioned__year + , subq_0.ds_partitioned__extract_year + , subq_0.ds_partitioned__extract_quarter + , subq_0.ds_partitioned__extract_month + , subq_0.ds_partitioned__extract_day + , subq_0.ds_partitioned__extract_dow + , subq_0.ds_partitioned__extract_doy + , subq_0.paid_at__day + , subq_0.paid_at__week + , subq_0.paid_at__month + , subq_0.paid_at__quarter + , subq_0.paid_at__year + , subq_0.paid_at__extract_year + , subq_0.paid_at__extract_quarter + , subq_0.paid_at__extract_month + , subq_0.paid_at__extract_day + , subq_0.paid_at__extract_dow + , subq_0.paid_at__extract_doy + , subq_0.booking__ds__day + , subq_0.booking__ds__week + , subq_0.booking__ds__month + , subq_0.booking__ds__quarter + , subq_0.booking__ds__year + , subq_0.booking__ds__extract_year + , subq_0.booking__ds__extract_quarter + , subq_0.booking__ds__extract_month + , subq_0.booking__ds__extract_day + , subq_0.booking__ds__extract_dow + , subq_0.booking__ds__extract_doy + , subq_0.booking__ds_partitioned__day + , subq_0.booking__ds_partitioned__week + , subq_0.booking__ds_partitioned__month + , subq_0.booking__ds_partitioned__quarter + , subq_0.booking__ds_partitioned__year + , subq_0.booking__ds_partitioned__extract_year + , subq_0.booking__ds_partitioned__extract_quarter + , subq_0.booking__ds_partitioned__extract_month + , subq_0.booking__ds_partitioned__extract_day + , subq_0.booking__ds_partitioned__extract_dow + , subq_0.booking__ds_partitioned__extract_doy + , subq_0.booking__paid_at__day + , subq_0.booking__paid_at__week + , subq_0.booking__paid_at__month + , subq_0.booking__paid_at__quarter + , subq_0.booking__paid_at__year + , subq_0.booking__paid_at__extract_year + , subq_0.booking__paid_at__extract_quarter + , subq_0.booking__paid_at__extract_month + , subq_0.booking__paid_at__extract_day + , subq_0.booking__paid_at__extract_dow + , subq_0.booking__paid_at__extract_doy + , subq_0.ds__day AS metric_time__day + , subq_0.ds__week AS metric_time__week + , subq_0.ds__month AS metric_time__month + , subq_0.ds__quarter AS metric_time__quarter + , subq_0.ds__year AS metric_time__year + , subq_0.ds__extract_year AS metric_time__extract_year + , subq_0.ds__extract_quarter AS metric_time__extract_quarter + , subq_0.ds__extract_month AS metric_time__extract_month + , subq_0.ds__extract_day AS metric_time__extract_day + , subq_0.ds__extract_dow AS metric_time__extract_dow + , subq_0.ds__extract_doy AS metric_time__extract_doy + , subq_0.listing + , subq_0.guest + , subq_0.host + , subq_0.booking__listing + , subq_0.booking__guest + , subq_0.booking__host + , subq_0.is_instant + , subq_0.booking__is_instant + , subq_0.bookings + , subq_0.instant_bookings + , subq_0.booking_value + , subq_0.max_booking_value + , subq_0.min_booking_value + , subq_0.bookers + , subq_0.average_booking_value + , subq_0.referred_bookings + , subq_0.median_booking_value + , subq_0.booking_value_p99 + , subq_0.discrete_booking_value_p99 + , subq_0.approximate_continuous_booking_value_p99 + , subq_0.approximate_discrete_booking_value_p99 + FROM ( + -- Read Elements From Semantic Model 'bookings_source' + SELECT + 1 AS bookings + , CASE WHEN is_instant THEN 1 ELSE 0 END AS instant_bookings + , bookings_source_src_10001.booking_value + , bookings_source_src_10001.booking_value AS max_booking_value + , bookings_source_src_10001.booking_value AS min_booking_value + , bookings_source_src_10001.guest_id AS bookers + , bookings_source_src_10001.booking_value AS average_booking_value + , bookings_source_src_10001.booking_value AS booking_payments + , CASE WHEN referrer_id IS NOT NULL THEN 1 ELSE 0 END AS referred_bookings + , bookings_source_src_10001.booking_value AS median_booking_value + , bookings_source_src_10001.booking_value AS booking_value_p99 + , bookings_source_src_10001.booking_value AS discrete_booking_value_p99 + , bookings_source_src_10001.booking_value AS approximate_continuous_booking_value_p99 + , bookings_source_src_10001.booking_value AS approximate_discrete_booking_value_p99 + , bookings_source_src_10001.is_instant + , DATE_TRUNC('day', bookings_source_src_10001.ds) AS ds__day + , DATE_TRUNC('week', bookings_source_src_10001.ds) AS ds__week + , DATE_TRUNC('month', bookings_source_src_10001.ds) AS ds__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds) AS ds__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds) AS ds__year + , EXTRACT(year FROM bookings_source_src_10001.ds) AS ds__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds) AS ds__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds) AS ds__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds) AS ds__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds) AS ds__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds) AS ds__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__day + , DATE_TRUNC('week', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__week + , DATE_TRUNC('month', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__year + , EXTRACT(year FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.paid_at) AS paid_at__day + , DATE_TRUNC('week', bookings_source_src_10001.paid_at) AS paid_at__week + , DATE_TRUNC('month', bookings_source_src_10001.paid_at) AS paid_at__month + , DATE_TRUNC('quarter', bookings_source_src_10001.paid_at) AS paid_at__quarter + , DATE_TRUNC('year', bookings_source_src_10001.paid_at) AS paid_at__year + , EXTRACT(year FROM bookings_source_src_10001.paid_at) AS paid_at__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.paid_at) AS paid_at__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.paid_at) AS paid_at__extract_month + , EXTRACT(day FROM bookings_source_src_10001.paid_at) AS paid_at__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.paid_at) AS paid_at__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.paid_at) AS paid_at__extract_doy + , bookings_source_src_10001.is_instant AS booking__is_instant + , DATE_TRUNC('day', bookings_source_src_10001.ds) AS booking__ds__day + , DATE_TRUNC('week', bookings_source_src_10001.ds) AS booking__ds__week + , DATE_TRUNC('month', bookings_source_src_10001.ds) AS booking__ds__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds) AS booking__ds__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds) AS booking__ds__year + , EXTRACT(year FROM bookings_source_src_10001.ds) AS booking__ds__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds) AS booking__ds__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds) AS booking__ds__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds) AS booking__ds__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds) AS booking__ds__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds) AS booking__ds__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__day + , DATE_TRUNC('week', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__week + , DATE_TRUNC('month', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__year + , EXTRACT(year FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.paid_at) AS booking__paid_at__day + , DATE_TRUNC('week', bookings_source_src_10001.paid_at) AS booking__paid_at__week + , DATE_TRUNC('month', bookings_source_src_10001.paid_at) AS booking__paid_at__month + , DATE_TRUNC('quarter', bookings_source_src_10001.paid_at) AS booking__paid_at__quarter + , DATE_TRUNC('year', bookings_source_src_10001.paid_at) AS booking__paid_at__year + , EXTRACT(year FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_month + , EXTRACT(day FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_doy + , bookings_source_src_10001.listing_id AS listing + , bookings_source_src_10001.guest_id AS guest + , bookings_source_src_10001.host_id AS host + , bookings_source_src_10001.listing_id AS booking__listing + , bookings_source_src_10001.guest_id AS booking__guest + , bookings_source_src_10001.host_id AS booking__host + FROM ***************************.fct_bookings bookings_source_src_10001 + ) subq_0 + ) subq_1 + ) subq_2 + GROUP BY + subq_2.booking__paid_at__day +) subq_3 diff --git a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_0_with_non_metric_time__plan0_optimized.sql b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_0_with_non_metric_time__plan0_optimized.sql new file mode 100644 index 0000000000..ff3673d7fc --- /dev/null +++ b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_0_with_non_metric_time__plan0_optimized.sql @@ -0,0 +1,22 @@ +-- Compute Metrics via Expressions +SELECT + booking__paid_at__day + , COALESCE(bookings, 0) AS bookings_fill_0 +FROM ( + -- Aggregate Measures + SELECT + booking__paid_at__day + , SUM(bookings) AS bookings + FROM ( + -- Read Elements From Semantic Model 'bookings_source' + -- Metric Time Dimension 'ds' + -- Pass Only Elements: + -- ['bookings', 'booking__paid_at__day'] + SELECT + DATE_TRUNC('day', paid_at) AS booking__paid_at__day + , 1 AS bookings + FROM ***************************.fct_bookings bookings_source_src_10001 + ) subq_6 + GROUP BY + booking__paid_at__day +) subq_7 diff --git a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_nulls_with_0_metric_time__plan0.sql b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_nulls_with_0_metric_time__plan0.sql new file mode 100644 index 0000000000..56babdf001 --- /dev/null +++ b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_nulls_with_0_metric_time__plan0.sql @@ -0,0 +1,228 @@ +-- Compute Metrics via Expressions +SELECT + subq_6.metric_time__day + , COALESCE(subq_6.bookings, 0) AS bookings_fill_0 +FROM ( + -- Join to Time Spine Dataset + SELECT + subq_4.metric_time__day AS metric_time__day + , subq_3.bookings AS bookings + FROM ( + -- Date Spine + SELECT + subq_5.ds AS metric_time__day + FROM ***************************.mf_time_spine subq_5 + ) subq_4 + LEFT OUTER JOIN ( + -- Aggregate Measures + SELECT + subq_2.metric_time__day + , SUM(subq_2.bookings) AS bookings + FROM ( + -- Pass Only Elements: + -- ['bookings', 'metric_time__day'] + SELECT + subq_1.metric_time__day + , subq_1.bookings + FROM ( + -- Metric Time Dimension 'ds' + SELECT + subq_0.ds__day + , subq_0.ds__week + , subq_0.ds__month + , subq_0.ds__quarter + , subq_0.ds__year + , subq_0.ds__extract_year + , subq_0.ds__extract_quarter + , subq_0.ds__extract_month + , subq_0.ds__extract_day + , subq_0.ds__extract_dow + , subq_0.ds__extract_doy + , subq_0.ds_partitioned__day + , subq_0.ds_partitioned__week + , subq_0.ds_partitioned__month + , subq_0.ds_partitioned__quarter + , subq_0.ds_partitioned__year + , subq_0.ds_partitioned__extract_year + , subq_0.ds_partitioned__extract_quarter + , subq_0.ds_partitioned__extract_month + , subq_0.ds_partitioned__extract_day + , subq_0.ds_partitioned__extract_dow + , subq_0.ds_partitioned__extract_doy + , subq_0.paid_at__day + , subq_0.paid_at__week + , subq_0.paid_at__month + , subq_0.paid_at__quarter + , subq_0.paid_at__year + , subq_0.paid_at__extract_year + , subq_0.paid_at__extract_quarter + , subq_0.paid_at__extract_month + , subq_0.paid_at__extract_day + , subq_0.paid_at__extract_dow + , subq_0.paid_at__extract_doy + , subq_0.booking__ds__day + , subq_0.booking__ds__week + , subq_0.booking__ds__month + , subq_0.booking__ds__quarter + , subq_0.booking__ds__year + , subq_0.booking__ds__extract_year + , subq_0.booking__ds__extract_quarter + , subq_0.booking__ds__extract_month + , subq_0.booking__ds__extract_day + , subq_0.booking__ds__extract_dow + , subq_0.booking__ds__extract_doy + , subq_0.booking__ds_partitioned__day + , subq_0.booking__ds_partitioned__week + , subq_0.booking__ds_partitioned__month + , subq_0.booking__ds_partitioned__quarter + , subq_0.booking__ds_partitioned__year + , subq_0.booking__ds_partitioned__extract_year + , subq_0.booking__ds_partitioned__extract_quarter + , subq_0.booking__ds_partitioned__extract_month + , subq_0.booking__ds_partitioned__extract_day + , subq_0.booking__ds_partitioned__extract_dow + , subq_0.booking__ds_partitioned__extract_doy + , subq_0.booking__paid_at__day + , subq_0.booking__paid_at__week + , subq_0.booking__paid_at__month + , subq_0.booking__paid_at__quarter + , subq_0.booking__paid_at__year + , subq_0.booking__paid_at__extract_year + , subq_0.booking__paid_at__extract_quarter + , subq_0.booking__paid_at__extract_month + , subq_0.booking__paid_at__extract_day + , subq_0.booking__paid_at__extract_dow + , subq_0.booking__paid_at__extract_doy + , subq_0.ds__day AS metric_time__day + , subq_0.ds__week AS metric_time__week + , subq_0.ds__month AS metric_time__month + , subq_0.ds__quarter AS metric_time__quarter + , subq_0.ds__year AS metric_time__year + , subq_0.ds__extract_year AS metric_time__extract_year + , subq_0.ds__extract_quarter AS metric_time__extract_quarter + , subq_0.ds__extract_month AS metric_time__extract_month + , subq_0.ds__extract_day AS metric_time__extract_day + , subq_0.ds__extract_dow AS metric_time__extract_dow + , subq_0.ds__extract_doy AS metric_time__extract_doy + , subq_0.listing + , subq_0.guest + , subq_0.host + , subq_0.booking__listing + , subq_0.booking__guest + , subq_0.booking__host + , subq_0.is_instant + , subq_0.booking__is_instant + , subq_0.bookings + , subq_0.instant_bookings + , subq_0.booking_value + , subq_0.max_booking_value + , subq_0.min_booking_value + , subq_0.bookers + , subq_0.average_booking_value + , subq_0.referred_bookings + , subq_0.median_booking_value + , subq_0.booking_value_p99 + , subq_0.discrete_booking_value_p99 + , subq_0.approximate_continuous_booking_value_p99 + , subq_0.approximate_discrete_booking_value_p99 + FROM ( + -- Read Elements From Semantic Model 'bookings_source' + SELECT + 1 AS bookings + , CASE WHEN is_instant THEN 1 ELSE 0 END AS instant_bookings + , bookings_source_src_10001.booking_value + , bookings_source_src_10001.booking_value AS max_booking_value + , bookings_source_src_10001.booking_value AS min_booking_value + , bookings_source_src_10001.guest_id AS bookers + , bookings_source_src_10001.booking_value AS average_booking_value + , bookings_source_src_10001.booking_value AS booking_payments + , CASE WHEN referrer_id IS NOT NULL THEN 1 ELSE 0 END AS referred_bookings + , bookings_source_src_10001.booking_value AS median_booking_value + , bookings_source_src_10001.booking_value AS booking_value_p99 + , bookings_source_src_10001.booking_value AS discrete_booking_value_p99 + , bookings_source_src_10001.booking_value AS approximate_continuous_booking_value_p99 + , bookings_source_src_10001.booking_value AS approximate_discrete_booking_value_p99 + , bookings_source_src_10001.is_instant + , DATE_TRUNC('day', bookings_source_src_10001.ds) AS ds__day + , DATE_TRUNC('week', bookings_source_src_10001.ds) AS ds__week + , DATE_TRUNC('month', bookings_source_src_10001.ds) AS ds__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds) AS ds__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds) AS ds__year + , EXTRACT(year FROM bookings_source_src_10001.ds) AS ds__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds) AS ds__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds) AS ds__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds) AS ds__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds) AS ds__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds) AS ds__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__day + , DATE_TRUNC('week', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__week + , DATE_TRUNC('month', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__year + , EXTRACT(year FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.paid_at) AS paid_at__day + , DATE_TRUNC('week', bookings_source_src_10001.paid_at) AS paid_at__week + , DATE_TRUNC('month', bookings_source_src_10001.paid_at) AS paid_at__month + , DATE_TRUNC('quarter', bookings_source_src_10001.paid_at) AS paid_at__quarter + , DATE_TRUNC('year', bookings_source_src_10001.paid_at) AS paid_at__year + , EXTRACT(year FROM bookings_source_src_10001.paid_at) AS paid_at__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.paid_at) AS paid_at__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.paid_at) AS paid_at__extract_month + , EXTRACT(day FROM bookings_source_src_10001.paid_at) AS paid_at__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.paid_at) AS paid_at__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.paid_at) AS paid_at__extract_doy + , bookings_source_src_10001.is_instant AS booking__is_instant + , DATE_TRUNC('day', bookings_source_src_10001.ds) AS booking__ds__day + , DATE_TRUNC('week', bookings_source_src_10001.ds) AS booking__ds__week + , DATE_TRUNC('month', bookings_source_src_10001.ds) AS booking__ds__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds) AS booking__ds__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds) AS booking__ds__year + , EXTRACT(year FROM bookings_source_src_10001.ds) AS booking__ds__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds) AS booking__ds__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds) AS booking__ds__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds) AS booking__ds__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds) AS booking__ds__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds) AS booking__ds__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__day + , DATE_TRUNC('week', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__week + , DATE_TRUNC('month', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__year + , EXTRACT(year FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.paid_at) AS booking__paid_at__day + , DATE_TRUNC('week', bookings_source_src_10001.paid_at) AS booking__paid_at__week + , DATE_TRUNC('month', bookings_source_src_10001.paid_at) AS booking__paid_at__month + , DATE_TRUNC('quarter', bookings_source_src_10001.paid_at) AS booking__paid_at__quarter + , DATE_TRUNC('year', bookings_source_src_10001.paid_at) AS booking__paid_at__year + , EXTRACT(year FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_month + , EXTRACT(day FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_doy + , bookings_source_src_10001.listing_id AS listing + , bookings_source_src_10001.guest_id AS guest + , bookings_source_src_10001.host_id AS host + , bookings_source_src_10001.listing_id AS booking__listing + , bookings_source_src_10001.guest_id AS booking__guest + , bookings_source_src_10001.host_id AS booking__host + FROM ***************************.fct_bookings bookings_source_src_10001 + ) subq_0 + ) subq_1 + ) subq_2 + GROUP BY + subq_2.metric_time__day + ) subq_3 + ON + subq_4.metric_time__day = subq_3.metric_time__day +) subq_6 diff --git a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_nulls_with_0_metric_time__plan0_optimized.sql b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_nulls_with_0_metric_time__plan0_optimized.sql new file mode 100644 index 0000000000..6eaf4ece6c --- /dev/null +++ b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_nulls_with_0_metric_time__plan0_optimized.sql @@ -0,0 +1,31 @@ +-- Compute Metrics via Expressions +SELECT + metric_time__day + , COALESCE(bookings, 0) AS bookings_fill_0 +FROM ( + -- Join to Time Spine Dataset + SELECT + subq_12.ds AS metric_time__day + , subq_10.bookings AS bookings + FROM ***************************.mf_time_spine subq_12 + LEFT OUTER JOIN ( + -- Aggregate Measures + SELECT + metric_time__day + , SUM(bookings) AS bookings + FROM ( + -- Read Elements From Semantic Model 'bookings_source' + -- Metric Time Dimension 'ds' + -- Pass Only Elements: + -- ['bookings', 'metric_time__day'] + SELECT + DATE_TRUNC('day', ds) AS metric_time__day + , 1 AS bookings + FROM ***************************.fct_bookings bookings_source_src_10001 + ) subq_9 + GROUP BY + metric_time__day + ) subq_10 + ON + subq_12.ds = subq_10.metric_time__day +) subq_13 diff --git a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_nulls_with_0_month__plan0.sql b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_nulls_with_0_month__plan0.sql new file mode 100644 index 0000000000..ef423c9c02 --- /dev/null +++ b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_nulls_with_0_month__plan0.sql @@ -0,0 +1,230 @@ +-- Compute Metrics via Expressions +SELECT + subq_6.metric_time__month + , COALESCE(subq_6.bookings, 0) AS bookings_fill_0 +FROM ( + -- Join to Time Spine Dataset + SELECT + subq_4.metric_time__month AS metric_time__month + , subq_3.bookings AS bookings + FROM ( + -- Date Spine + SELECT + DATE_TRUNC('month', subq_5.ds) AS metric_time__month + FROM ***************************.mf_time_spine subq_5 + GROUP BY + DATE_TRUNC('month', subq_5.ds) + ) subq_4 + LEFT OUTER JOIN ( + -- Aggregate Measures + SELECT + subq_2.metric_time__month + , SUM(subq_2.bookings) AS bookings + FROM ( + -- Pass Only Elements: + -- ['bookings', 'metric_time__month'] + SELECT + subq_1.metric_time__month + , subq_1.bookings + FROM ( + -- Metric Time Dimension 'ds' + SELECT + subq_0.ds__day + , subq_0.ds__week + , subq_0.ds__month + , subq_0.ds__quarter + , subq_0.ds__year + , subq_0.ds__extract_year + , subq_0.ds__extract_quarter + , subq_0.ds__extract_month + , subq_0.ds__extract_day + , subq_0.ds__extract_dow + , subq_0.ds__extract_doy + , subq_0.ds_partitioned__day + , subq_0.ds_partitioned__week + , subq_0.ds_partitioned__month + , subq_0.ds_partitioned__quarter + , subq_0.ds_partitioned__year + , subq_0.ds_partitioned__extract_year + , subq_0.ds_partitioned__extract_quarter + , subq_0.ds_partitioned__extract_month + , subq_0.ds_partitioned__extract_day + , subq_0.ds_partitioned__extract_dow + , subq_0.ds_partitioned__extract_doy + , subq_0.paid_at__day + , subq_0.paid_at__week + , subq_0.paid_at__month + , subq_0.paid_at__quarter + , subq_0.paid_at__year + , subq_0.paid_at__extract_year + , subq_0.paid_at__extract_quarter + , subq_0.paid_at__extract_month + , subq_0.paid_at__extract_day + , subq_0.paid_at__extract_dow + , subq_0.paid_at__extract_doy + , subq_0.booking__ds__day + , subq_0.booking__ds__week + , subq_0.booking__ds__month + , subq_0.booking__ds__quarter + , subq_0.booking__ds__year + , subq_0.booking__ds__extract_year + , subq_0.booking__ds__extract_quarter + , subq_0.booking__ds__extract_month + , subq_0.booking__ds__extract_day + , subq_0.booking__ds__extract_dow + , subq_0.booking__ds__extract_doy + , subq_0.booking__ds_partitioned__day + , subq_0.booking__ds_partitioned__week + , subq_0.booking__ds_partitioned__month + , subq_0.booking__ds_partitioned__quarter + , subq_0.booking__ds_partitioned__year + , subq_0.booking__ds_partitioned__extract_year + , subq_0.booking__ds_partitioned__extract_quarter + , subq_0.booking__ds_partitioned__extract_month + , subq_0.booking__ds_partitioned__extract_day + , subq_0.booking__ds_partitioned__extract_dow + , subq_0.booking__ds_partitioned__extract_doy + , subq_0.booking__paid_at__day + , subq_0.booking__paid_at__week + , subq_0.booking__paid_at__month + , subq_0.booking__paid_at__quarter + , subq_0.booking__paid_at__year + , subq_0.booking__paid_at__extract_year + , subq_0.booking__paid_at__extract_quarter + , subq_0.booking__paid_at__extract_month + , subq_0.booking__paid_at__extract_day + , subq_0.booking__paid_at__extract_dow + , subq_0.booking__paid_at__extract_doy + , subq_0.ds__day AS metric_time__day + , subq_0.ds__week AS metric_time__week + , subq_0.ds__month AS metric_time__month + , subq_0.ds__quarter AS metric_time__quarter + , subq_0.ds__year AS metric_time__year + , subq_0.ds__extract_year AS metric_time__extract_year + , subq_0.ds__extract_quarter AS metric_time__extract_quarter + , subq_0.ds__extract_month AS metric_time__extract_month + , subq_0.ds__extract_day AS metric_time__extract_day + , subq_0.ds__extract_dow AS metric_time__extract_dow + , subq_0.ds__extract_doy AS metric_time__extract_doy + , subq_0.listing + , subq_0.guest + , subq_0.host + , subq_0.booking__listing + , subq_0.booking__guest + , subq_0.booking__host + , subq_0.is_instant + , subq_0.booking__is_instant + , subq_0.bookings + , subq_0.instant_bookings + , subq_0.booking_value + , subq_0.max_booking_value + , subq_0.min_booking_value + , subq_0.bookers + , subq_0.average_booking_value + , subq_0.referred_bookings + , subq_0.median_booking_value + , subq_0.booking_value_p99 + , subq_0.discrete_booking_value_p99 + , subq_0.approximate_continuous_booking_value_p99 + , subq_0.approximate_discrete_booking_value_p99 + FROM ( + -- Read Elements From Semantic Model 'bookings_source' + SELECT + 1 AS bookings + , CASE WHEN is_instant THEN 1 ELSE 0 END AS instant_bookings + , bookings_source_src_10001.booking_value + , bookings_source_src_10001.booking_value AS max_booking_value + , bookings_source_src_10001.booking_value AS min_booking_value + , bookings_source_src_10001.guest_id AS bookers + , bookings_source_src_10001.booking_value AS average_booking_value + , bookings_source_src_10001.booking_value AS booking_payments + , CASE WHEN referrer_id IS NOT NULL THEN 1 ELSE 0 END AS referred_bookings + , bookings_source_src_10001.booking_value AS median_booking_value + , bookings_source_src_10001.booking_value AS booking_value_p99 + , bookings_source_src_10001.booking_value AS discrete_booking_value_p99 + , bookings_source_src_10001.booking_value AS approximate_continuous_booking_value_p99 + , bookings_source_src_10001.booking_value AS approximate_discrete_booking_value_p99 + , bookings_source_src_10001.is_instant + , DATE_TRUNC('day', bookings_source_src_10001.ds) AS ds__day + , DATE_TRUNC('week', bookings_source_src_10001.ds) AS ds__week + , DATE_TRUNC('month', bookings_source_src_10001.ds) AS ds__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds) AS ds__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds) AS ds__year + , EXTRACT(year FROM bookings_source_src_10001.ds) AS ds__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds) AS ds__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds) AS ds__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds) AS ds__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds) AS ds__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds) AS ds__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__day + , DATE_TRUNC('week', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__week + , DATE_TRUNC('month', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__year + , EXTRACT(year FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.paid_at) AS paid_at__day + , DATE_TRUNC('week', bookings_source_src_10001.paid_at) AS paid_at__week + , DATE_TRUNC('month', bookings_source_src_10001.paid_at) AS paid_at__month + , DATE_TRUNC('quarter', bookings_source_src_10001.paid_at) AS paid_at__quarter + , DATE_TRUNC('year', bookings_source_src_10001.paid_at) AS paid_at__year + , EXTRACT(year FROM bookings_source_src_10001.paid_at) AS paid_at__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.paid_at) AS paid_at__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.paid_at) AS paid_at__extract_month + , EXTRACT(day FROM bookings_source_src_10001.paid_at) AS paid_at__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.paid_at) AS paid_at__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.paid_at) AS paid_at__extract_doy + , bookings_source_src_10001.is_instant AS booking__is_instant + , DATE_TRUNC('day', bookings_source_src_10001.ds) AS booking__ds__day + , DATE_TRUNC('week', bookings_source_src_10001.ds) AS booking__ds__week + , DATE_TRUNC('month', bookings_source_src_10001.ds) AS booking__ds__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds) AS booking__ds__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds) AS booking__ds__year + , EXTRACT(year FROM bookings_source_src_10001.ds) AS booking__ds__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds) AS booking__ds__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds) AS booking__ds__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds) AS booking__ds__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds) AS booking__ds__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds) AS booking__ds__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__day + , DATE_TRUNC('week', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__week + , DATE_TRUNC('month', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__year + , EXTRACT(year FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.paid_at) AS booking__paid_at__day + , DATE_TRUNC('week', bookings_source_src_10001.paid_at) AS booking__paid_at__week + , DATE_TRUNC('month', bookings_source_src_10001.paid_at) AS booking__paid_at__month + , DATE_TRUNC('quarter', bookings_source_src_10001.paid_at) AS booking__paid_at__quarter + , DATE_TRUNC('year', bookings_source_src_10001.paid_at) AS booking__paid_at__year + , EXTRACT(year FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_month + , EXTRACT(day FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_doy + , bookings_source_src_10001.listing_id AS listing + , bookings_source_src_10001.guest_id AS guest + , bookings_source_src_10001.host_id AS host + , bookings_source_src_10001.listing_id AS booking__listing + , bookings_source_src_10001.guest_id AS booking__guest + , bookings_source_src_10001.host_id AS booking__host + FROM ***************************.fct_bookings bookings_source_src_10001 + ) subq_0 + ) subq_1 + ) subq_2 + GROUP BY + subq_2.metric_time__month + ) subq_3 + ON + subq_4.metric_time__month = subq_3.metric_time__month +) subq_6 diff --git a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_nulls_with_0_month__plan0_optimized.sql b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_nulls_with_0_month__plan0_optimized.sql new file mode 100644 index 0000000000..3ff964d117 --- /dev/null +++ b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_nulls_with_0_month__plan0_optimized.sql @@ -0,0 +1,38 @@ +-- Compute Metrics via Expressions +SELECT + metric_time__month + , COALESCE(bookings, 0) AS bookings_fill_0 +FROM ( + -- Join to Time Spine Dataset + SELECT + subq_11.metric_time__month AS metric_time__month + , subq_10.bookings AS bookings + FROM ( + -- Date Spine + SELECT + DATE_TRUNC('month', ds) AS metric_time__month + FROM ***************************.mf_time_spine subq_12 + GROUP BY + DATE_TRUNC('month', ds) + ) subq_11 + LEFT OUTER JOIN ( + -- Aggregate Measures + SELECT + metric_time__month + , SUM(bookings) AS bookings + FROM ( + -- Read Elements From Semantic Model 'bookings_source' + -- Metric Time Dimension 'ds' + -- Pass Only Elements: + -- ['bookings', 'metric_time__month'] + SELECT + DATE_TRUNC('month', ds) AS metric_time__month + , 1 AS bookings + FROM ***************************.fct_bookings bookings_source_src_10001 + ) subq_9 + GROUP BY + metric_time__month + ) subq_10 + ON + subq_11.metric_time__month = subq_10.metric_time__month +) subq_13 diff --git a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_nulls_without_time_spine__plan0.sql b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_nulls_without_time_spine__plan0.sql new file mode 100644 index 0000000000..53c8422ac5 --- /dev/null +++ b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_nulls_without_time_spine__plan0.sql @@ -0,0 +1,214 @@ +-- Compute Metrics via Expressions +SELECT + subq_3.metric_time__day + , COALESCE(subq_3.bookings, 0) AS bookings_fill_0_without_time_spine +FROM ( + -- Aggregate Measures + SELECT + subq_2.metric_time__day + , SUM(subq_2.bookings) AS bookings + FROM ( + -- Pass Only Elements: + -- ['bookings', 'metric_time__day'] + SELECT + subq_1.metric_time__day + , subq_1.bookings + FROM ( + -- Metric Time Dimension 'ds' + SELECT + subq_0.ds__day + , subq_0.ds__week + , subq_0.ds__month + , subq_0.ds__quarter + , subq_0.ds__year + , subq_0.ds__extract_year + , subq_0.ds__extract_quarter + , subq_0.ds__extract_month + , subq_0.ds__extract_day + , subq_0.ds__extract_dow + , subq_0.ds__extract_doy + , subq_0.ds_partitioned__day + , subq_0.ds_partitioned__week + , subq_0.ds_partitioned__month + , subq_0.ds_partitioned__quarter + , subq_0.ds_partitioned__year + , subq_0.ds_partitioned__extract_year + , subq_0.ds_partitioned__extract_quarter + , subq_0.ds_partitioned__extract_month + , subq_0.ds_partitioned__extract_day + , subq_0.ds_partitioned__extract_dow + , subq_0.ds_partitioned__extract_doy + , subq_0.paid_at__day + , subq_0.paid_at__week + , subq_0.paid_at__month + , subq_0.paid_at__quarter + , subq_0.paid_at__year + , subq_0.paid_at__extract_year + , subq_0.paid_at__extract_quarter + , subq_0.paid_at__extract_month + , subq_0.paid_at__extract_day + , subq_0.paid_at__extract_dow + , subq_0.paid_at__extract_doy + , subq_0.booking__ds__day + , subq_0.booking__ds__week + , subq_0.booking__ds__month + , subq_0.booking__ds__quarter + , subq_0.booking__ds__year + , subq_0.booking__ds__extract_year + , subq_0.booking__ds__extract_quarter + , subq_0.booking__ds__extract_month + , subq_0.booking__ds__extract_day + , subq_0.booking__ds__extract_dow + , subq_0.booking__ds__extract_doy + , subq_0.booking__ds_partitioned__day + , subq_0.booking__ds_partitioned__week + , subq_0.booking__ds_partitioned__month + , subq_0.booking__ds_partitioned__quarter + , subq_0.booking__ds_partitioned__year + , subq_0.booking__ds_partitioned__extract_year + , subq_0.booking__ds_partitioned__extract_quarter + , subq_0.booking__ds_partitioned__extract_month + , subq_0.booking__ds_partitioned__extract_day + , subq_0.booking__ds_partitioned__extract_dow + , subq_0.booking__ds_partitioned__extract_doy + , subq_0.booking__paid_at__day + , subq_0.booking__paid_at__week + , subq_0.booking__paid_at__month + , subq_0.booking__paid_at__quarter + , subq_0.booking__paid_at__year + , subq_0.booking__paid_at__extract_year + , subq_0.booking__paid_at__extract_quarter + , subq_0.booking__paid_at__extract_month + , subq_0.booking__paid_at__extract_day + , subq_0.booking__paid_at__extract_dow + , subq_0.booking__paid_at__extract_doy + , subq_0.ds__day AS metric_time__day + , subq_0.ds__week AS metric_time__week + , subq_0.ds__month AS metric_time__month + , subq_0.ds__quarter AS metric_time__quarter + , subq_0.ds__year AS metric_time__year + , subq_0.ds__extract_year AS metric_time__extract_year + , subq_0.ds__extract_quarter AS metric_time__extract_quarter + , subq_0.ds__extract_month AS metric_time__extract_month + , subq_0.ds__extract_day AS metric_time__extract_day + , subq_0.ds__extract_dow AS metric_time__extract_dow + , subq_0.ds__extract_doy AS metric_time__extract_doy + , subq_0.listing + , subq_0.guest + , subq_0.host + , subq_0.booking__listing + , subq_0.booking__guest + , subq_0.booking__host + , subq_0.is_instant + , subq_0.booking__is_instant + , subq_0.bookings + , subq_0.instant_bookings + , subq_0.booking_value + , subq_0.max_booking_value + , subq_0.min_booking_value + , subq_0.bookers + , subq_0.average_booking_value + , subq_0.referred_bookings + , subq_0.median_booking_value + , subq_0.booking_value_p99 + , subq_0.discrete_booking_value_p99 + , subq_0.approximate_continuous_booking_value_p99 + , subq_0.approximate_discrete_booking_value_p99 + FROM ( + -- Read Elements From Semantic Model 'bookings_source' + SELECT + 1 AS bookings + , CASE WHEN is_instant THEN 1 ELSE 0 END AS instant_bookings + , bookings_source_src_10001.booking_value + , bookings_source_src_10001.booking_value AS max_booking_value + , bookings_source_src_10001.booking_value AS min_booking_value + , bookings_source_src_10001.guest_id AS bookers + , bookings_source_src_10001.booking_value AS average_booking_value + , bookings_source_src_10001.booking_value AS booking_payments + , CASE WHEN referrer_id IS NOT NULL THEN 1 ELSE 0 END AS referred_bookings + , bookings_source_src_10001.booking_value AS median_booking_value + , bookings_source_src_10001.booking_value AS booking_value_p99 + , bookings_source_src_10001.booking_value AS discrete_booking_value_p99 + , bookings_source_src_10001.booking_value AS approximate_continuous_booking_value_p99 + , bookings_source_src_10001.booking_value AS approximate_discrete_booking_value_p99 + , bookings_source_src_10001.is_instant + , DATE_TRUNC('day', bookings_source_src_10001.ds) AS ds__day + , DATE_TRUNC('week', bookings_source_src_10001.ds) AS ds__week + , DATE_TRUNC('month', bookings_source_src_10001.ds) AS ds__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds) AS ds__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds) AS ds__year + , EXTRACT(year FROM bookings_source_src_10001.ds) AS ds__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds) AS ds__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds) AS ds__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds) AS ds__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds) AS ds__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds) AS ds__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__day + , DATE_TRUNC('week', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__week + , DATE_TRUNC('month', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__year + , EXTRACT(year FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.paid_at) AS paid_at__day + , DATE_TRUNC('week', bookings_source_src_10001.paid_at) AS paid_at__week + , DATE_TRUNC('month', bookings_source_src_10001.paid_at) AS paid_at__month + , DATE_TRUNC('quarter', bookings_source_src_10001.paid_at) AS paid_at__quarter + , DATE_TRUNC('year', bookings_source_src_10001.paid_at) AS paid_at__year + , EXTRACT(year FROM bookings_source_src_10001.paid_at) AS paid_at__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.paid_at) AS paid_at__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.paid_at) AS paid_at__extract_month + , EXTRACT(day FROM bookings_source_src_10001.paid_at) AS paid_at__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.paid_at) AS paid_at__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.paid_at) AS paid_at__extract_doy + , bookings_source_src_10001.is_instant AS booking__is_instant + , DATE_TRUNC('day', bookings_source_src_10001.ds) AS booking__ds__day + , DATE_TRUNC('week', bookings_source_src_10001.ds) AS booking__ds__week + , DATE_TRUNC('month', bookings_source_src_10001.ds) AS booking__ds__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds) AS booking__ds__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds) AS booking__ds__year + , EXTRACT(year FROM bookings_source_src_10001.ds) AS booking__ds__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds) AS booking__ds__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds) AS booking__ds__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds) AS booking__ds__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds) AS booking__ds__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds) AS booking__ds__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__day + , DATE_TRUNC('week', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__week + , DATE_TRUNC('month', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__year + , EXTRACT(year FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.paid_at) AS booking__paid_at__day + , DATE_TRUNC('week', bookings_source_src_10001.paid_at) AS booking__paid_at__week + , DATE_TRUNC('month', bookings_source_src_10001.paid_at) AS booking__paid_at__month + , DATE_TRUNC('quarter', bookings_source_src_10001.paid_at) AS booking__paid_at__quarter + , DATE_TRUNC('year', bookings_source_src_10001.paid_at) AS booking__paid_at__year + , EXTRACT(year FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_month + , EXTRACT(day FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_doy + , bookings_source_src_10001.listing_id AS listing + , bookings_source_src_10001.guest_id AS guest + , bookings_source_src_10001.host_id AS host + , bookings_source_src_10001.listing_id AS booking__listing + , bookings_source_src_10001.guest_id AS booking__guest + , bookings_source_src_10001.host_id AS booking__host + FROM ***************************.fct_bookings bookings_source_src_10001 + ) subq_0 + ) subq_1 + ) subq_2 + GROUP BY + subq_2.metric_time__day +) subq_3 diff --git a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_nulls_without_time_spine__plan0_optimized.sql b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_nulls_without_time_spine__plan0_optimized.sql new file mode 100644 index 0000000000..996e4c7c5f --- /dev/null +++ b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_fill_nulls_without_time_spine__plan0_optimized.sql @@ -0,0 +1,22 @@ +-- Compute Metrics via Expressions +SELECT + metric_time__day + , COALESCE(bookings, 0) AS bookings_fill_0_without_time_spine +FROM ( + -- Aggregate Measures + SELECT + metric_time__day + , SUM(bookings) AS bookings + FROM ( + -- Read Elements From Semantic Model 'bookings_source' + -- Metric Time Dimension 'ds' + -- Pass Only Elements: + -- ['bookings', 'metric_time__day'] + SELECT + DATE_TRUNC('day', ds) AS metric_time__day + , 1 AS bookings + FROM ***************************.fct_bookings bookings_source_src_10001 + ) subq_6 + GROUP BY + metric_time__day +) subq_7 diff --git a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_join_to_time_spine__plan0.sql b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_join_to_time_spine__plan0.sql new file mode 100644 index 0000000000..a987ac45e1 --- /dev/null +++ b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_join_to_time_spine__plan0.sql @@ -0,0 +1,228 @@ +-- Compute Metrics via Expressions +SELECT + subq_6.metric_time__day + , subq_6.bookings AS bookings_join_to_time_spine +FROM ( + -- Join to Time Spine Dataset + SELECT + subq_4.metric_time__day AS metric_time__day + , subq_3.bookings AS bookings + FROM ( + -- Date Spine + SELECT + subq_5.ds AS metric_time__day + FROM ***************************.mf_time_spine subq_5 + ) subq_4 + LEFT OUTER JOIN ( + -- Aggregate Measures + SELECT + subq_2.metric_time__day + , SUM(subq_2.bookings) AS bookings + FROM ( + -- Pass Only Elements: + -- ['bookings', 'metric_time__day'] + SELECT + subq_1.metric_time__day + , subq_1.bookings + FROM ( + -- Metric Time Dimension 'ds' + SELECT + subq_0.ds__day + , subq_0.ds__week + , subq_0.ds__month + , subq_0.ds__quarter + , subq_0.ds__year + , subq_0.ds__extract_year + , subq_0.ds__extract_quarter + , subq_0.ds__extract_month + , subq_0.ds__extract_day + , subq_0.ds__extract_dow + , subq_0.ds__extract_doy + , subq_0.ds_partitioned__day + , subq_0.ds_partitioned__week + , subq_0.ds_partitioned__month + , subq_0.ds_partitioned__quarter + , subq_0.ds_partitioned__year + , subq_0.ds_partitioned__extract_year + , subq_0.ds_partitioned__extract_quarter + , subq_0.ds_partitioned__extract_month + , subq_0.ds_partitioned__extract_day + , subq_0.ds_partitioned__extract_dow + , subq_0.ds_partitioned__extract_doy + , subq_0.paid_at__day + , subq_0.paid_at__week + , subq_0.paid_at__month + , subq_0.paid_at__quarter + , subq_0.paid_at__year + , subq_0.paid_at__extract_year + , subq_0.paid_at__extract_quarter + , subq_0.paid_at__extract_month + , subq_0.paid_at__extract_day + , subq_0.paid_at__extract_dow + , subq_0.paid_at__extract_doy + , subq_0.booking__ds__day + , subq_0.booking__ds__week + , subq_0.booking__ds__month + , subq_0.booking__ds__quarter + , subq_0.booking__ds__year + , subq_0.booking__ds__extract_year + , subq_0.booking__ds__extract_quarter + , subq_0.booking__ds__extract_month + , subq_0.booking__ds__extract_day + , subq_0.booking__ds__extract_dow + , subq_0.booking__ds__extract_doy + , subq_0.booking__ds_partitioned__day + , subq_0.booking__ds_partitioned__week + , subq_0.booking__ds_partitioned__month + , subq_0.booking__ds_partitioned__quarter + , subq_0.booking__ds_partitioned__year + , subq_0.booking__ds_partitioned__extract_year + , subq_0.booking__ds_partitioned__extract_quarter + , subq_0.booking__ds_partitioned__extract_month + , subq_0.booking__ds_partitioned__extract_day + , subq_0.booking__ds_partitioned__extract_dow + , subq_0.booking__ds_partitioned__extract_doy + , subq_0.booking__paid_at__day + , subq_0.booking__paid_at__week + , subq_0.booking__paid_at__month + , subq_0.booking__paid_at__quarter + , subq_0.booking__paid_at__year + , subq_0.booking__paid_at__extract_year + , subq_0.booking__paid_at__extract_quarter + , subq_0.booking__paid_at__extract_month + , subq_0.booking__paid_at__extract_day + , subq_0.booking__paid_at__extract_dow + , subq_0.booking__paid_at__extract_doy + , subq_0.ds__day AS metric_time__day + , subq_0.ds__week AS metric_time__week + , subq_0.ds__month AS metric_time__month + , subq_0.ds__quarter AS metric_time__quarter + , subq_0.ds__year AS metric_time__year + , subq_0.ds__extract_year AS metric_time__extract_year + , subq_0.ds__extract_quarter AS metric_time__extract_quarter + , subq_0.ds__extract_month AS metric_time__extract_month + , subq_0.ds__extract_day AS metric_time__extract_day + , subq_0.ds__extract_dow AS metric_time__extract_dow + , subq_0.ds__extract_doy AS metric_time__extract_doy + , subq_0.listing + , subq_0.guest + , subq_0.host + , subq_0.booking__listing + , subq_0.booking__guest + , subq_0.booking__host + , subq_0.is_instant + , subq_0.booking__is_instant + , subq_0.bookings + , subq_0.instant_bookings + , subq_0.booking_value + , subq_0.max_booking_value + , subq_0.min_booking_value + , subq_0.bookers + , subq_0.average_booking_value + , subq_0.referred_bookings + , subq_0.median_booking_value + , subq_0.booking_value_p99 + , subq_0.discrete_booking_value_p99 + , subq_0.approximate_continuous_booking_value_p99 + , subq_0.approximate_discrete_booking_value_p99 + FROM ( + -- Read Elements From Semantic Model 'bookings_source' + SELECT + 1 AS bookings + , CASE WHEN is_instant THEN 1 ELSE 0 END AS instant_bookings + , bookings_source_src_10001.booking_value + , bookings_source_src_10001.booking_value AS max_booking_value + , bookings_source_src_10001.booking_value AS min_booking_value + , bookings_source_src_10001.guest_id AS bookers + , bookings_source_src_10001.booking_value AS average_booking_value + , bookings_source_src_10001.booking_value AS booking_payments + , CASE WHEN referrer_id IS NOT NULL THEN 1 ELSE 0 END AS referred_bookings + , bookings_source_src_10001.booking_value AS median_booking_value + , bookings_source_src_10001.booking_value AS booking_value_p99 + , bookings_source_src_10001.booking_value AS discrete_booking_value_p99 + , bookings_source_src_10001.booking_value AS approximate_continuous_booking_value_p99 + , bookings_source_src_10001.booking_value AS approximate_discrete_booking_value_p99 + , bookings_source_src_10001.is_instant + , DATE_TRUNC('day', bookings_source_src_10001.ds) AS ds__day + , DATE_TRUNC('week', bookings_source_src_10001.ds) AS ds__week + , DATE_TRUNC('month', bookings_source_src_10001.ds) AS ds__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds) AS ds__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds) AS ds__year + , EXTRACT(year FROM bookings_source_src_10001.ds) AS ds__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds) AS ds__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds) AS ds__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds) AS ds__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds) AS ds__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds) AS ds__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__day + , DATE_TRUNC('week', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__week + , DATE_TRUNC('month', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds_partitioned) AS ds_partitioned__year + , EXTRACT(year FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds_partitioned) AS ds_partitioned__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.paid_at) AS paid_at__day + , DATE_TRUNC('week', bookings_source_src_10001.paid_at) AS paid_at__week + , DATE_TRUNC('month', bookings_source_src_10001.paid_at) AS paid_at__month + , DATE_TRUNC('quarter', bookings_source_src_10001.paid_at) AS paid_at__quarter + , DATE_TRUNC('year', bookings_source_src_10001.paid_at) AS paid_at__year + , EXTRACT(year FROM bookings_source_src_10001.paid_at) AS paid_at__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.paid_at) AS paid_at__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.paid_at) AS paid_at__extract_month + , EXTRACT(day FROM bookings_source_src_10001.paid_at) AS paid_at__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.paid_at) AS paid_at__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.paid_at) AS paid_at__extract_doy + , bookings_source_src_10001.is_instant AS booking__is_instant + , DATE_TRUNC('day', bookings_source_src_10001.ds) AS booking__ds__day + , DATE_TRUNC('week', bookings_source_src_10001.ds) AS booking__ds__week + , DATE_TRUNC('month', bookings_source_src_10001.ds) AS booking__ds__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds) AS booking__ds__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds) AS booking__ds__year + , EXTRACT(year FROM bookings_source_src_10001.ds) AS booking__ds__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds) AS booking__ds__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds) AS booking__ds__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds) AS booking__ds__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds) AS booking__ds__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds) AS booking__ds__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__day + , DATE_TRUNC('week', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__week + , DATE_TRUNC('month', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__month + , DATE_TRUNC('quarter', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__quarter + , DATE_TRUNC('year', bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__year + , EXTRACT(year FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_month + , EXTRACT(day FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.ds_partitioned) AS booking__ds_partitioned__extract_doy + , DATE_TRUNC('day', bookings_source_src_10001.paid_at) AS booking__paid_at__day + , DATE_TRUNC('week', bookings_source_src_10001.paid_at) AS booking__paid_at__week + , DATE_TRUNC('month', bookings_source_src_10001.paid_at) AS booking__paid_at__month + , DATE_TRUNC('quarter', bookings_source_src_10001.paid_at) AS booking__paid_at__quarter + , DATE_TRUNC('year', bookings_source_src_10001.paid_at) AS booking__paid_at__year + , EXTRACT(year FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_year + , EXTRACT(quarter FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_quarter + , EXTRACT(month FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_month + , EXTRACT(day FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_day + , EXTRACT(isodow FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_dow + , EXTRACT(doy FROM bookings_source_src_10001.paid_at) AS booking__paid_at__extract_doy + , bookings_source_src_10001.listing_id AS listing + , bookings_source_src_10001.guest_id AS guest + , bookings_source_src_10001.host_id AS host + , bookings_source_src_10001.listing_id AS booking__listing + , bookings_source_src_10001.guest_id AS booking__guest + , bookings_source_src_10001.host_id AS booking__host + FROM ***************************.fct_bookings bookings_source_src_10001 + ) subq_0 + ) subq_1 + ) subq_2 + GROUP BY + subq_2.metric_time__day + ) subq_3 + ON + subq_4.metric_time__day = subq_3.metric_time__day +) subq_6 diff --git a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_join_to_time_spine__plan0_optimized.sql b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_join_to_time_spine__plan0_optimized.sql new file mode 100644 index 0000000000..cc3c93f375 --- /dev/null +++ b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/DuckDB/test_simple_join_to_time_spine__plan0_optimized.sql @@ -0,0 +1,26 @@ +-- Join to Time Spine Dataset +-- Compute Metrics via Expressions +SELECT + subq_12.ds AS metric_time__day + , subq_10.bookings AS bookings_join_to_time_spine +FROM ***************************.mf_time_spine subq_12 +LEFT OUTER JOIN ( + -- Aggregate Measures + SELECT + metric_time__day + , SUM(bookings) AS bookings + FROM ( + -- Read Elements From Semantic Model 'bookings_source' + -- Metric Time Dimension 'ds' + -- Pass Only Elements: + -- ['bookings', 'metric_time__day'] + SELECT + DATE_TRUNC('day', ds) AS metric_time__day + , 1 AS bookings + FROM ***************************.fct_bookings bookings_source_src_10001 + ) subq_9 + GROUP BY + metric_time__day +) subq_10 +ON + subq_12.ds = subq_10.metric_time__day diff --git a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/test_cumulative_fill_nulls__plan0.xml b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/test_cumulative_fill_nulls__plan0.xml new file mode 100644 index 0000000000..419d7867c5 --- /dev/null +++ b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/test_cumulative_fill_nulls__plan0.xml @@ -0,0 +1,1265 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/test_derived_fill_nulls_for_one_input_metric__plan0.xml b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/test_derived_fill_nulls_for_one_input_metric__plan0.xml new file mode 100644 index 0000000000..c5a97449e3 --- /dev/null +++ b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/test_derived_fill_nulls_for_one_input_metric__plan0.xml @@ -0,0 +1,2072 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/test_simple_fill_0_with_categorical_dimension__plan0.xml b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/test_simple_fill_0_with_categorical_dimension__plan0.xml new file mode 100644 index 0000000000..47232c7bbc --- /dev/null +++ b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/test_simple_fill_0_with_categorical_dimension__plan0.xml @@ -0,0 +1,811 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/test_simple_fill_0_with_non_metric_time__plan0.xml b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/test_simple_fill_0_with_non_metric_time__plan0.xml new file mode 100644 index 0000000000..edb6ce1255 --- /dev/null +++ b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/test_simple_fill_0_with_non_metric_time__plan0.xml @@ -0,0 +1,811 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/test_simple_fill_nulls_with_0_metric_time__plan0.xml b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/test_simple_fill_nulls_with_0_metric_time__plan0.xml new file mode 100644 index 0000000000..4ce1915b18 --- /dev/null +++ b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/test_simple_fill_nulls_with_0_metric_time__plan0.xml @@ -0,0 +1,846 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/test_simple_fill_nulls_with_0_month__plan0.xml b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/test_simple_fill_nulls_with_0_month__plan0.xml new file mode 100644 index 0000000000..c38bed1f2b --- /dev/null +++ b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/test_simple_fill_nulls_with_0_month__plan0.xml @@ -0,0 +1,850 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/test_simple_fill_nulls_without_time_spine__plan0.xml b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/test_simple_fill_nulls_without_time_spine__plan0.xml new file mode 100644 index 0000000000..7250362905 --- /dev/null +++ b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/test_simple_fill_nulls_without_time_spine__plan0.xml @@ -0,0 +1,811 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/test_simple_join_to_time_spine__plan0.xml b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/test_simple_join_to_time_spine__plan0.xml new file mode 100644 index 0000000000..505a75b7fb --- /dev/null +++ b/metricflow/test/snapshots/test_dataflow_to_sql_plan.py/SqlQueryPlan/test_simple_join_to_time_spine__plan0.xml @@ -0,0 +1,846 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 7b54e387faaadd6636adbc780365a2204285cb22 Mon Sep 17 00:00:00 2001 From: Courtney Holcomb Date: Tue, 31 Oct 2023 15:01:02 -0700 Subject: [PATCH 09/11] Remove TODO --- metricflow/dataflow/builder/dataflow_plan_builder.py | 1 - 1 file changed, 1 deletion(-) diff --git a/metricflow/dataflow/builder/dataflow_plan_builder.py b/metricflow/dataflow/builder/dataflow_plan_builder.py index 18fee96bdc..48a13c565c 100644 --- a/metricflow/dataflow/builder/dataflow_plan_builder.py +++ b/metricflow/dataflow/builder/dataflow_plan_builder.py @@ -924,7 +924,6 @@ def _build_aggregated_measures_from_measure_source_node( break # TODO: if multiple measures and only some join to time spine, should we aggregate separately? - # TODO: what is time range constraint here? does it need to be smaller? # Only join to time spine if metric time was requested in the query. if join_aggregated_measure_to_time_spine and metric_time_dimension_requested: return JoinToTimeSpineNode( From 0c9d653f57a2663ba7a3bcfe75a2e48b3905c3e0 Mon Sep 17 00:00:00 2001 From: Courtney Holcomb Date: Tue, 31 Oct 2023 15:14:03 -0700 Subject: [PATCH 10/11] Spelling --- metricflow/test/integration/test_configured_cases.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metricflow/test/integration/test_configured_cases.py b/metricflow/test/integration/test_configured_cases.py index d88de4e567..5f63e31499 100644 --- a/metricflow/test/integration/test_configured_cases.py +++ b/metricflow/test/integration/test_configured_cases.py @@ -219,7 +219,7 @@ def test_case( extended_date_semantic_manifest_lookup: SemanticManifestLookup, scd_semantic_manifest_lookup: SemanticManifestLookup, sql_client: SqlClient, - create_source_tables: bool, # Param not used in funciton, but needed for test data to exist + create_source_tables: bool, # Param not used in function, but needed for test data to exist ) -> None: """Runs all integration tests configured in the test case YAML directory.""" case = CONFIGURED_INTEGRATION_TESTS_REPOSITORY.get_test_case(name) From 5c3d51b54836f057193d0c59432829390e5b03b8 Mon Sep 17 00:00:00 2001 From: Courtney Holcomb Date: Tue, 31 Oct 2023 15:25:37 -0700 Subject: [PATCH 11/11] Move TODO --- metricflow/dataflow/builder/dataflow_plan_builder.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/metricflow/dataflow/builder/dataflow_plan_builder.py b/metricflow/dataflow/builder/dataflow_plan_builder.py index 48a13c565c..03cd279649 100644 --- a/metricflow/dataflow/builder/dataflow_plan_builder.py +++ b/metricflow/dataflow/builder/dataflow_plan_builder.py @@ -923,9 +923,10 @@ def _build_aggregated_measures_from_measure_source_node( join_aggregated_measure_to_time_spine = True break - # TODO: if multiple measures and only some join to time spine, should we aggregate separately? # Only join to time spine if metric time was requested in the query. if join_aggregated_measure_to_time_spine and metric_time_dimension_requested: + # TODO: if multiple measures in same node and only some join to time spine, should we split them into + # separate AggregateMeasuresNodes? return JoinToTimeSpineNode( parent_node=aggregate_measures_node, requested_metric_time_dimension_specs=metric_time_dimension_specs,