diff --git a/datajunction-server/datajunction_server/construction/build_v2.py b/datajunction-server/datajunction_server/construction/build_v2.py index b7d7a85d8..7800aec16 100644 --- a/datajunction-server/datajunction_server/construction/build_v2.py +++ b/datajunction-server/datajunction_server/construction/build_v2.py @@ -887,11 +887,19 @@ async def build(self) -> ast.Query: ctes=parent_ctes + metric_ctes, select=ast.Select( projection=[ - ast.Column( - name=ast.Name(proj.alias, namespace=initial_cte.alias), # type: ignore - _type=proj.type, # type: ignore - semantic_entity=proj.semantic_entity, # type: ignore - semantic_type=proj.semantic_type, # type: ignore + ast.Function( + ast.Name("COALESCE"), + args=[ + ast.Column( + name=ast.Name(proj.alias, namespace=join_cte.alias), # type: ignore + _type=proj.type, # type: ignore + semantic_entity=proj.semantic_entity, # type: ignore + semantic_type=proj.semantic_type, # type: ignore + ) + for join_cte in metric_ctes + ], + ).set_alias( + proj.alias, # type: ignore ) for proj in initial_cte.select.projection ], diff --git a/datajunction-server/datajunction_server/sql/parsing/ast.py b/datajunction-server/datajunction_server/sql/parsing/ast.py index 576b77d9b..95555e6c3 100644 --- a/datajunction-server/datajunction_server/sql/parsing/ast.py +++ b/datajunction-server/datajunction_server/sql/parsing/ast.py @@ -546,8 +546,8 @@ class Alias(Aliasable, Generic[AliasedType]): child: AliasedType = field(default_factory=Node) def __str__(self) -> str: - as_ = " AS " if self.as_ else " " - return f"{self.child}{as_}{self.alias}" + as_ = " AS " if self.as_ and self.alias else " " + return f"{self.child}{as_}{self.alias if self.alias else ''}" def is_aggregation(self) -> bool: return isinstance(self.child, Expression) and self.child.is_aggregation() diff --git a/datajunction-server/tests/api/cubes_test.py b/datajunction-server/tests/api/cubes_test.py index 02edfa68d..9d28ffe33 100644 --- a/datajunction-server/tests/api/cubes_test.py +++ b/datajunction-server/tests/api/cubes_test.py @@ -555,7 +555,7 @@ async def test_create_cube_similar_dimensions( @pytest.mark.asyncio -async def test_create_cube( +async def test_create_cube1( client_with_repairs_cube: AsyncClient, ): """ @@ -762,18 +762,54 @@ async def test_create_cube( default_DOT_hard_hat_to_delete.hire_date ) SELECT - default_DOT_repair_orders_fact_metrics.default_DOT_hard_hat_DOT_country, - default_DOT_repair_orders_fact_metrics.default_DOT_hard_hat_DOT_postal_code, - default_DOT_repair_orders_fact_metrics.default_DOT_hard_hat_DOT_city, - default_DOT_repair_orders_fact_metrics.default_DOT_hard_hat_DOT_state, - default_DOT_repair_orders_fact_metrics.default_DOT_dispatcher_DOT_company_name, - default_DOT_repair_orders_fact_metrics.default_DOT_municipality_dim_DOT_local_region, - default_DOT_repair_orders_fact_metrics.default_DOT_hard_hat_to_delete_DOT_hire_date, - default_DOT_repair_orders_fact_metrics.default_DOT_discounted_orders_rate, - default_DOT_repair_orders_fact_metrics.default_DOT_num_repair_orders, - default_DOT_repair_orders_fact_metrics.default_DOT_avg_repair_price, - default_DOT_repair_orders_fact_metrics.default_DOT_total_repair_cost, - default_DOT_repair_orders_fact_metrics.default_DOT_total_repair_order_discounts, + COALESCE( + default_DOT_repair_orders_fact_metrics.default_DOT_hard_hat_DOT_country, + default_DOT_repair_order_details_metrics.default_DOT_hard_hat_DOT_country + ) default_DOT_hard_hat_DOT_country, + COALESCE( + default_DOT_repair_orders_fact_metrics.default_DOT_hard_hat_DOT_postal_code, + default_DOT_repair_order_details_metrics.default_DOT_hard_hat_DOT_postal_code + ) default_DOT_hard_hat_DOT_postal_code, + COALESCE( + default_DOT_repair_orders_fact_metrics.default_DOT_hard_hat_DOT_city, + default_DOT_repair_order_details_metrics.default_DOT_hard_hat_DOT_city + ) default_DOT_hard_hat_DOT_city, + COALESCE( + default_DOT_repair_orders_fact_metrics.default_DOT_hard_hat_DOT_state, + default_DOT_repair_order_details_metrics.default_DOT_hard_hat_DOT_state + ) default_DOT_hard_hat_DOT_state, + COALESCE( + default_DOT_repair_orders_fact_metrics.default_DOT_dispatcher_DOT_company_name, + default_DOT_repair_order_details_metrics.default_DOT_dispatcher_DOT_company_name + ) default_DOT_dispatcher_DOT_company_name, + COALESCE( + default_DOT_repair_orders_fact_metrics.default_DOT_municipality_dim_DOT_local_region, + default_DOT_repair_order_details_metrics.default_DOT_municipality_dim_DOT_local_region + ) default_DOT_municipality_dim_DOT_local_region, + COALESCE( + default_DOT_repair_orders_fact_metrics.default_DOT_hard_hat_to_delete_DOT_hire_date, + default_DOT_repair_order_details_metrics.default_DOT_hard_hat_to_delete_DOT_hire_date + ) default_DOT_hard_hat_to_delete_DOT_hire_date, + COALESCE( + default_DOT_repair_orders_fact_metrics.default_DOT_discounted_orders_rate, + default_DOT_repair_order_details_metrics.default_DOT_discounted_orders_rate + ) default_DOT_discounted_orders_rate, + COALESCE( + default_DOT_repair_orders_fact_metrics.default_DOT_num_repair_orders, + default_DOT_repair_order_details_metrics.default_DOT_num_repair_orders + ) default_DOT_num_repair_orders, + COALESCE( + default_DOT_repair_orders_fact_metrics.default_DOT_avg_repair_price, + default_DOT_repair_order_details_metrics.default_DOT_avg_repair_price + ) default_DOT_avg_repair_price, + COALESCE( + default_DOT_repair_orders_fact_metrics.default_DOT_total_repair_cost, + default_DOT_repair_order_details_metrics.default_DOT_total_repair_cost + ) default_DOT_total_repair_cost, + COALESCE( + default_DOT_repair_orders_fact_metrics.default_DOT_total_repair_order_discounts, + default_DOT_repair_order_details_metrics.default_DOT_total_repair_order_discounts + ) default_DOT_total_repair_order_discounts, default_DOT_repair_order_details_metrics.default_DOT_double_total_repair_cost FROM default_DOT_repair_orders_fact_metrics FULL JOIN default_DOT_repair_order_details_metrics diff --git a/datajunction-server/tests/api/sql_v2_test.py b/datajunction-server/tests/api/sql_v2_test.py index d29f868b7..b47089238 100644 --- a/datajunction-server/tests/api/sql_v2_test.py +++ b/datajunction-server/tests/api/sql_v2_test.py @@ -765,13 +765,27 @@ async def test_metrics_sql_different_parents( FROM default_DOT_repair_orders_fact INNER JOIN default_DOT_hard_hat ON default_DOT_repair_orders_fact.hard_hat_id = default_DOT_hard_hat.hard_hat_id GROUP BY default_DOT_hard_hat.first_name, default_DOT_hard_hat.last_name ) -SELECT default_DOT_hard_hat_metrics.default_DOT_hard_hat_DOT_last_name, +SELECT + COALESCE( + default_DOT_hard_hat_metrics.default_DOT_hard_hat_DOT_last_name, + default_DOT_repair_orders_fact_metrics.default_DOT_hard_hat_DOT_last_name + ) default_DOT_hard_hat_DOT_last_name, + COALESCE( default_DOT_hard_hat_metrics.default_DOT_hard_hat_DOT_first_name, + default_DOT_repair_orders_fact_metrics.default_DOT_hard_hat_DOT_first_name + ) default_DOT_hard_hat_DOT_first_name, + COALESCE( default_DOT_hard_hat_metrics.default_DOT_avg_length_of_employment, - default_DOT_repair_orders_fact_metrics.default_DOT_total_repair_cost - FROM default_DOT_hard_hat_metrics FULL JOIN default_DOT_repair_orders_fact_metrics ON default_DOT_hard_hat_metrics.default_DOT_hard_hat_DOT_first_name = default_DOT_repair_orders_fact_metrics.default_DOT_hard_hat_DOT_first_name AND default_DOT_hard_hat_metrics.default_DOT_hard_hat_DOT_last_name = default_DOT_repair_orders_fact_metrics.default_DOT_hard_hat_DOT_last_name - ORDER BY default_DOT_hard_hat_metrics.default_DOT_hard_hat_DOT_last_name - LIMIT 5""" + default_DOT_repair_orders_fact_metrics.default_DOT_avg_length_of_employment + ) default_DOT_avg_length_of_employment, + default_DOT_repair_orders_fact_metrics.default_DOT_total_repair_cost +FROM default_DOT_hard_hat_metrics FULL JOIN default_DOT_repair_orders_fact_metrics ON default_DOT_hard_hat_metrics.default_DOT_hard_hat_DOT_first_name = default_DOT_repair_orders_fact_metrics.default_DOT_hard_hat_DOT_first_name AND default_DOT_hard_hat_metrics.default_DOT_hard_hat_DOT_last_name = default_DOT_repair_orders_fact_metrics.default_DOT_hard_hat_DOT_last_name +ORDER BY + COALESCE( + default_DOT_hard_hat_metrics.default_DOT_hard_hat_DOT_last_name, + default_DOT_repair_orders_fact_metrics.default_DOT_hard_hat_DOT_last_name + ) +LIMIT 5""" assert str(parse(str(data["sql"]))) == str(parse(expected_sql)) response = await module__client_with_roads.get(