Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: add macro get_query_results_as_single_value #696

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
ed499fd
feat: add query_results_as_single_value.sql macro
Sep 30, 2022
3274806
chore: update the macro definition
Oct 1, 2022
4fb7a94
chore: update test
Oct 1, 2022
a2500a5
chore: final edits
Oct 1, 2022
8ae1043
chore: remove extra model reference
Oct 1, 2022
e676190
chore: update return() to handle BigQuery
Oct 1, 2022
89980b0
chore: README.md, macro updates
CR-Lough Oct 27, 2022
957fc97
feat: factoring in first review changes
CR-Lough Oct 30, 2022
4558d78
chore: updates to testing
CR-Lough Oct 30, 2022
d78d7d8
chore: updates tests
CR-Lough Oct 30, 2022
ade6c27
chore: update test for bigquery
CR-Lough Oct 30, 2022
f6c8bf3
chore: update cast for bigquery
CR-Lough Oct 30, 2022
f170767
Merge branch 'utils-v1' into feat-get_query_results_as_single_value
CR-Lough Oct 30, 2022
2781bec
Use example with a single record in readme
joellabes Nov 23, 2022
fd9de07
Add default value when no record found
joellabes Nov 23, 2022
f044e1c
test when no results are found
joellabes Nov 23, 2022
fd7791a
Rename test file
joellabes Nov 23, 2022
c69b036
Add test definitions
joellabes Nov 23, 2022
683dc1c
Fix incorrect ref
joellabes Nov 23, 2022
c344814
And another one
joellabes Nov 23, 2022
9b03391
Update test_get_query_results_as_single_value.sql
joellabes Nov 23, 2022
2d8bc9c
cast strings as strings
joellabes Nov 23, 2022
cd99a84
Put arg in right place
joellabes Nov 23, 2022
de80a88
Update test_get_query_results_as_single_value.sql
joellabes Nov 23, 2022
641f286
switch to limit zero for BQ
joellabes Nov 23, 2022
3043aec
Update test_get_query_results_as_single_value.sql
joellabes Nov 23, 2022
65747ab
quote column name in arg
joellabes Nov 23, 2022
7bd1523
snowflake wont let you safe cast something to itself
joellabes Nov 23, 2022
33b8f3c
Merge branch 'utils-v1' into feat-get_query_results_as_single_value
joellabes Nov 23, 2022
4f2ccd1
warning to future readers [skip ci]
joellabes Nov 23, 2022
26e71a9
Add singular test to check for multi row/multi column setup
joellabes Nov 23, 2022
89570dc
Merge branch 'feat-get_query_results_as_single_value' of https://gith…
joellabes Nov 23, 2022
88026f5
forgot to save comment [skip ci]
joellabes Nov 23, 2022
2a59bb0
Rename to get_single_value
joellabes Nov 24, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
- Updated the `slugify` macro to prepend "_" to column names beginning with a number since most databases do not allow names to begin with numbers.
- Implemented an optional `group_by_columns` argument across many of the generic testing macros to test for properties that only pertain to group-level or are can be more rigorously conducted at the group level. Property available in `recency`, `at_least_one`, `equal_row_count`, `fewer_rows_than`, `not_constant`, `not_null_proportion`, and `sequential` tests [#633](https://github.com/dbt-labs/dbt-utils/pull/633)
- New feature to omit the `source_column_name` column on the `union_relations` macro ([#331](https://github.com/dbt-labs/dbt-utils/issues/331), [#624](https://github.com/dbt-labs/dbt-utils/pull/624))
- New macro `get_single_value` ([#696](https://github.com/dbt-labs/dbt-utils/pull/696))
- New feature to select fewer columns in `expression_is_true` ([#683](https://github.com/dbt-labs/dbt-utils/issues/683), [#686](https://github.com/dbt-labs/dbt-utils/pull/686))
- Add `not_empty_string` generic test that asserts column values are not an empty string. ([#632](https://github.com/dbt-labs/dbt-utils/issues/632), [#634](https://github.com/dbt-labs/dbt-utils/pull/634))

Expand Down Expand Up @@ -59,9 +60,9 @@ models:
- Fix to utilize dbt Core version of `escape_single_quotes` instead of version from dbt Utils ([[#689](https://github.com/dbt-labs/dbt-utils/issues/689)], [#692](https://github.com/dbt-labs/dbt-utils/pull/692))

## Contributors:
- [@CR-Lough] (https://github.com/CR-Lough) (#706) (#696)
- [@fivetran-catfritz](https://github.com/fivetran-catfritz)
- [@crowemi](https://github.com/crowemi)
- [@CR-Lough] (https://github.com/CR-Lough) (#706)
- [@SimonQuvang](https://github.com/SimonQuvang) (#701)
- [@christineberger](https://github.com/christineberger) (#624)
- [@epapineau](https://github.com/epapineau) (#634)
Expand Down
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Check [dbt Hub](https://hub.getdbt.com/dbt-labs/dbt_utils/latest/) for the lates
- [get_relations_by_pattern](#get_relations_by_pattern-source)
- [get_relations_by_prefix](#get_relations_by_prefix-source)
- [get_query_results_as_dict](#get_query_results_as_dict-source)
- [get_single_value](#get_single_value)

- [SQL generators](#sql-generators)
- [date_spine](#date_spine-source)
Expand Down Expand Up @@ -817,6 +818,27 @@ select
from {{ ref('users') }}
```

#### get_single_value ([source](macros/sql/get_single_value.sql))

This macro returns a single value from a sql query, so that you don't need to interact with the Agate library to operate on the result

**Usage:**

```
{% set sql_statement %}
select max(created_at) from {{ ref('processed_orders') }}
{% endset %}

{%- set newest_processed_order = dbt_utils.get_single_value(sql_statement) -%}

select

*,
last_order_at > '{{ newest_processed_order }}' as has_unprocessed_order

from {{ ref('users') }}
```

### SQL generators

These macros generate SQL (either a complete query, or a part of a query). They often implement patterns that should be easy in SQL, but for some reason are much harder than they need to be.
Expand Down
2 changes: 2 additions & 0 deletions integration_tests/data/sql/data_get_single_value.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
date_value,float_value,int_value,string_value
2017-01-01 00:00:00,3.3,19,string_a
6 changes: 6 additions & 0 deletions integration_tests/dbt_project.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ seeds:
# this.incorporate() to hardcode the node's type as otherwise dbt doesn't know it yet
+post-hook: "{% do adapter.drop_relation(this.incorporate(type='table')) %}"

data_get_single_value:
+column_types:
date_value: timestamp
float_value: float
int_value: integer

data_width_bucket:
+column_types:
num_buckets: integer
Expand Down
30 changes: 30 additions & 0 deletions integration_tests/models/sql/schema.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,36 @@
version: 2

models:
- name: test_get_single_value
tests:
- assert_equal:
actual: date_actual
expected: date_expected
- assert_equal:
actual: float_actual
expected: float_expected
- assert_equal:
actual: int_actual
expected: int_expected
- assert_equal:
actual: string_actual
expected: string_expected

- name: test_get_single_value_default
tests:
- assert_equal:
actual: date_actual
expected: date_expected
- assert_equal:
actual: float_actual
expected: float_expected
- assert_equal:
actual: int_actual
expected: int_expected
- assert_equal:
actual: string_actual
expected: string_expected

- name: test_generate_series
tests:
- dbt_utils.equality:
Expand Down
42 changes: 42 additions & 0 deletions integration_tests/models/sql/test_get_single_value.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{#
Dear future reader,
Before you go restructuring the delicate web of casts and quotes below, a warning:
I once thought as you are thinking. Proceed with caution.
#}

{% set date_statement %}
select date_value from {{ ref('data_get_single_value') }}
{% endset %}

{% set float_statement %}
select float_value from {{ ref('data_get_single_value') }}
{% endset %}

{% set int_statement %}
select int_value from {{ ref('data_get_single_value') }}
{% endset %}

{% set string_statement %}
select string_value from {{ ref('data_get_single_value') }}
{% endset %}

with default_data as (

select
cast(date_value as {{ dbt.type_timestamp() }}) as date_expected,
cast({{ dbt.string_literal(dbt_utils.get_single_value(date_statement)) }} as {{ dbt.type_timestamp() }}) as date_actual,

float_value as float_expected,
{{ dbt_utils.get_single_value(float_statement) }} as float_actual,

int_value as int_expected,
{{ dbt_utils.get_single_value(int_statement) }} as int_actual,

string_value as string_expected,
cast({{ dbt.string_literal(dbt_utils.get_single_value(string_statement)) }} as {{ dbt.type_string() }}) as string_actual

from {{ ref('data_get_single_value') }}
)

select *
from default_data
28 changes: 28 additions & 0 deletions integration_tests/models/sql/test_get_single_value_default.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{#
Dear future reader,
Before you go restructuring the delicate web of casts and quotes below, a warning:
I once thought as you are thinking. Proceed with caution.
#}

{% set false_statement = 'select 1 as id ' ~ limit_zero() %}

with default_data as (

select
cast({{ dbt.string_literal('2022-01-01') }} as {{ dbt.type_timestamp() }}) as date_expected,
cast({{ dbt.string_literal(dbt_utils.get_single_value(false_statement, '2022-01-01')) }} as {{ dbt.type_timestamp() }}) as date_actual,

1.23456 as float_expected,
{{ dbt_utils.get_single_value(false_statement, 1.23456) }} as float_actual,

123456 as int_expected,
{{ dbt_utils.get_single_value(false_statement, 123456) }} as int_actual,

cast({{ dbt.string_literal('fallback') }} as {{ dbt.type_string() }}) as string_expected,
cast({{ dbt.string_literal(dbt_utils.get_single_value(false_statement, 'fallback')) }} as {{ dbt.type_string() }}) as string_actual

from {{ ref('data_get_single_value') }}
)

select *
from default_data
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{% set query %}
with input as (
select 1 as id, 4 as di
union all
select 2 as id, 5 as di
union all
select 3 as id, 6 as di
)
{% endset %}

with comparisons as (
select {{ dbt_utils.get_single_value(query ~ " select min(id) from input") }} as output, 1 as expected
union all
select {{ dbt_utils.get_single_value(query ~ " select max(di) from input") }} as output, 6 as expected
)
select *
from comparisons
where output != expected
33 changes: 33 additions & 0 deletions macros/sql/get_single_value.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{% macro get_single_value(query, default=none) %}
{{ return(adapter.dispatch('get_single_value', 'dbt_utils')(query, default)) }}
{% endmacro %}

{% macro default__get_single_value(query, default) %}

{# This macro returns the (0, 0) record in a query, i.e. the first row of the first column #}

{%- call statement('get_query_result', fetch_result=True, auto_begin=false) -%}

{{ query }}

{%- endcall -%}

{%- if execute -%}

{% set r = load_result('get_query_result').table.columns[0].values() %}
{% if r | length == 0 %}
{% do print('Query `' ~ query ~ '` returned no rows. Using the default value: ' ~ default) %}
{% set sql_result = default %}
{% else %}
{% set sql_result = r[0] %}
{% endif %}

{%- else -%}

{% set sql_result = default %}

{%- endif -%}

{% do return(sql_result) %}

{% endmacro %}