Skip to content

Commit d04d6b3

Browse files
Benjamin Ryonclrcrl
Benjamin Ryon
authored andcommitted
Add zero_length_range_allowed arg to mutually_exclusive_ranges (#307)
Use boolean for zero range arg. Update changelog
1 parent b5c74c1 commit d04d6b3

File tree

5 files changed

+63
-8
lines changed

5 files changed

+63
-8
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
* Add new `accepted_range` test ([#276](https://github.com/fishtown-analytics/dbt-utils/pull/276) [@joellabes](https://github.com/joellabes))
44
* Make `expression_is_true` work as a column test (code originally in [#226](https://github.com/fishtown-analytics/dbt-utils/pull/226/) from [@elliottohara](https://github.com/elliottohara), merged via [#313])
55
* Add new schema test, `not_accepted_values` ([#284](https://github.com/fishtown-analytics/dbt-utils/pull/284) [@JavierMonton](https://github.com/JavierMonton))
6+
* Support a new argument, `zero_length_range_allowed` in the `mutually_exclusive_ranges` test ([#307](https://github.com/fishtown-analytics/dbt-utils/pull/307) [@zemekeng](https://github.com/zemekeneng))
7+
68

79
## Fixes
810
* Handle booleans gracefully in the unpivot macro ([#305](https://github.com/fishtown-analytics/dbt-utils/pull/305) [@avishalom](https://github.com/avishalom))

README.md

+29
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,15 @@ models:
361361
upper_bound_column: ended_at
362362
partition_by: customer_id
363363
gaps: required
364+
365+
# test that each customer can have subscriptions that start and end on the same date
366+
- name: subscriptions
367+
tests:
368+
- dbt_utils.mutually_exclusive_ranges:
369+
lower_bound_column: started_at
370+
upper_bound_column: ended_at
371+
partition_by: customer_id
372+
zero_length_range_allowed: true
364373
```
365374
**Args:**
366375
* `lower_bound_column` (required): The name of the column that represents the
@@ -372,6 +381,8 @@ upper value of the range. Must be not null.
372381
argument to indicate which column to partition by. `default=none`
373382
* `gaps` (optional): Whether there can be gaps are allowed between ranges.
374383
`default='allowed', one_of=['not_allowed', 'allowed', 'required']`
384+
* `zero_length_range_allowed` (optional): Whether ranges can start and end on the same date.
385+
`default=False`
375386

376387
**Note:** Both `lower_bound_column` and `upper_bound_column` should be not null.
377388
If this is not the case in your data source, consider passing a coalesce function
@@ -418,6 +429,24 @@ the lower bound of the next record (common for date ranges).
418429
| 2 | 3 |
419430
| 4 | 5 |
420431

432+
**Understanding the `zero_length_range_allowed` parameter:**
433+
Here are a number of examples for each allowed `zero_length_range_allowed` parameter.
434+
* `zero_length_range_allowed: false`: (default) The upper bound of each record must be greater than its lower bound.
435+
436+
| lower_bound | upper_bound |
437+
|-------------|-------------|
438+
| 0 | 1 |
439+
| 1 | 2 |
440+
| 2 | 3 |
441+
442+
* `zero_length_range_allowed: true`: The upper bound of each record can be greater than or equal to its lower bound.
443+
444+
| lower_bound | upper_bound |
445+
|-------------|-------------|
446+
| 0 | 1 |
447+
| 2 | 2 |
448+
| 3 | 4 |
449+
421450
#### unique_combination_of_columns ([source](macros/schema_tests/unique_combination_of_columns.sql))
422451
This test confirms that the combination of columns is unique. For example, the
423452
combination of month and product is unique, however neither column is unique
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
subscription_id,valid_from,valid_to
2+
3,2020-05-06,2020-05-07
3+
3,2020-05-08,2020-05-08
4+
3,2020-05-09,2020-05-10
5+
4,2020-06-06,2020-06-07
6+
4,2020-06-08,2020-06-08
7+
4,2020-06-09,2020-06-10

integration_tests/models/schema_tests/schema.yml

+8
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,14 @@ models:
112112
partition_by: subscription_id
113113
gaps: required
114114

115+
- name: data_test_mutually_exclusive_ranges_with_gaps_zero_length
116+
tests:
117+
- dbt_utils.mutually_exclusive_ranges:
118+
lower_bound_column: valid_from
119+
upper_bound_column: valid_to
120+
partition_by: subscription_id
121+
zero_length_range_allowed: true
122+
115123
- name: data_unique_combination_of_columns
116124
tests:
117125
- dbt_utils.unique_combination_of_columns:

macros/schema_tests/mutually_exclusive_ranges.sql

+17-8
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
{% macro test_mutually_exclusive_ranges(model, lower_bound_column, upper_bound_column, partition_by=None, gaps='allowed') %}
2-
{{ return(adapter.dispatch('test_mutually_exclusive_ranges', packages = dbt_utils._get_utils_namespaces())(model, lower_bound_column, upper_bound_column, partition_by, gaps)) }}
1+
{% macro test_mutually_exclusive_ranges(model, lower_bound_column, upper_bound_column, partition_by=None, gaps='allowed', zero_length_range_allowed=False) %}
2+
{{ return(adapter.dispatch('test_mutually_exclusive_ranges', packages = dbt_utils._get_utils_namespaces())(model, lower_bound_column, upper_bound_column, partition_by, gaps, zero_length_range_allowed)) }}
33
{% endmacro %}
44

5-
{% macro default__test_mutually_exclusive_ranges(model, lower_bound_column, upper_bound_column, partition_by=None, gaps='allowed') %}
6-
5+
{% macro default__test_mutually_exclusive_ranges(model, lower_bound_column, upper_bound_column, partition_by=None, gaps='allowed', zero_length_range_allowed=False) %}
76
{% if gaps == 'not_allowed' %}
87
{% set allow_gaps_operator='=' %}
98
{% set allow_gaps_operator_in_words='equal_to' %}
@@ -17,7 +16,17 @@
1716
{{ exceptions.raise_compiler_error(
1817
"`gaps` argument for mutually_exclusive_ranges test must be one of ['not_allowed', 'allowed', 'required'] Got: '" ~ gaps ~"'.'"
1918
) }}
20-
19+
{% endif %}
20+
{% if not zero_length_range_allowed %}
21+
{% set allow_zero_length_operator='<' %}
22+
{% set allow_zero_length_operator_in_words='less_than' %}
23+
{% elif zero_length_range_allowed %}
24+
{% set allow_zero_length_operator='<=' %}
25+
{% set allow_zero_length_operator_in_words='less_than_or_equal_to' %}
26+
{% else %}
27+
{{ exceptions.raise_compiler_error(
28+
"`zero_length_range_allowed` argument for mutually_exclusive_ranges test must be one of [true, false] Got: '" ~ zero_length_range_allowed ~"'.'"
29+
) }}
2130
{% endif %}
2231

2332
{% set partition_clause="partition by " ~ partition_by if partition_by else '' %}
@@ -55,9 +64,9 @@ calc as (
5564
-- Coalesce it to return an error on the null case (implicit assumption
5665
-- these columns are not_null)
5766
coalesce(
58-
lower_bound < upper_bound,
67+
lower_bound {{ allow_zero_length_operator }} upper_bound,
5968
false
60-
) as lower_bound_less_than_upper_bound,
69+
) as lower_bound_{{ allow_zero_length_operator_in_words }}_upper_bound,
6170

6271
-- For each record: upper_bound {{ allow_gaps_operator }} the next lower_bound.
6372
-- Coalesce it to handle null cases for the last record.
@@ -79,7 +88,7 @@ validation_errors as (
7988

8089
where not(
8190
-- THE FOLLOWING SHOULD BE TRUE --
82-
lower_bound_less_than_upper_bound
91+
lower_bound_{{ allow_zero_length_operator_in_words }}_upper_bound
8392
and upper_bound_{{ allow_gaps_operator_in_words }}_next_lower_bound
8493
)
8594
)

0 commit comments

Comments
 (0)