Skip to content

Commit 60a3b3c

Browse files
author
Claire Carroll
authored
Merge pull request #307 from zemekeneng/feature/exclusive_ranges_with_gaps_zero_length
Add zero length option to exclusive range test.
2 parents 6bec09b + fb0fa0f commit 60a3b3c

File tree

4 files changed

+59
-5
lines changed

4 files changed

+59
-5
lines changed

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: allowed
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` (optional): Whether ranges can start and end on the same date.
385+
`default='not_allowed', one_of=['not_allowed', 'allowed']`
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

+15-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{% macro test_mutually_exclusive_ranges(model, lower_bound_column, upper_bound_column, partition_by=None, gaps='allowed') %}
1+
{% macro test_mutually_exclusive_ranges(model, lower_bound_column, upper_bound_column, partition_by=None, gaps='allowed', zero_length_range_allowed=False) %}
22

33
{% if gaps == 'not_allowed' %}
44
{% set allow_gaps_operator='=' %}
@@ -13,7 +13,17 @@
1313
{{ exceptions.raise_compiler_error(
1414
"`gaps` argument for mutually_exclusive_ranges test must be one of ['not_allowed', 'allowed', 'required'] Got: '" ~ gaps ~"'.'"
1515
) }}
16-
16+
{% endif %}
17+
{% if not zero_length_range_allowed %}
18+
{% set allow_zero_length_operator='<' %}
19+
{% set allow_zero_length_operator_in_words='less_than' %}
20+
{% elif zero_length_range_allowed %}
21+
{% set allow_zero_length_operator='<=' %}
22+
{% set allow_zero_length_operator_in_words='less_than_or_equal_to' %}
23+
{% else %}
24+
{{ exceptions.raise_compiler_error(
25+
"`zero_length_range_allowed` argument for mutually_exclusive_ranges test must be one of [true, false] Got: '" ~ zero_length ~"'.'"
26+
) }}
1727
{% endif %}
1828

1929
{% set partition_clause="partition by " ~ partition_by if partition_by else '' %}
@@ -51,9 +61,9 @@ calc as (
5161
-- Coalesce it to return an error on the null case (implicit assumption
5262
-- these columns are not_null)
5363
coalesce(
54-
lower_bound < upper_bound,
64+
lower_bound {{ allow_zero_length_operator }} upper_bound,
5565
false
56-
) as lower_bound_less_than_upper_bound,
66+
) as lower_bound_{{ allow_zero_length_operator_in_words }}_upper_bound,
5767

5868
-- For each record: upper_bound {{ allow_gaps_operator }} the next lower_bound.
5969
-- Coalesce it to handle null cases for the last record.
@@ -75,7 +85,7 @@ validation_errors as (
7585

7686
where not(
7787
-- THE FOLLOWING SHOULD BE TRUE --
78-
lower_bound_less_than_upper_bound
88+
lower_bound_{{ allow_zero_length_operator_in_words }}_upper_bound
7989
and upper_bound_{{ allow_gaps_operator_in_words }}_next_lower_bound
8090
)
8191
)

0 commit comments

Comments
 (0)