Skip to content

Commit d279542

Browse files
add macro to get columns (#516)
* add macro to get columns * star macro should use get_columns * add adapter. * swap adapter for dbt_utils Co-authored-by: Joel Labes <joel.labes@dbtlabs.com> * update documentation * add output_lower arg * update name to get_filtered_columns_in_relation from get_columns * add tests * forgot args * too much whitespace removal ----------- Actual: ----------- --->"field_3"as "test_field_3"<--- ----------- Expected: ----------- --->"field_3" as "test_field_3"<--- * didnt mean to move a file that i did not create. moving things back. * remove lowercase logic * limit_zero Co-authored-by: Joel Labes <joel.labes@dbtlabs.com>
1 parent 9e32d9c commit d279542

9 files changed

+147
-16
lines changed

README.md

+43-3
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ For compatibility details between versions of dbt-core and dbt-utils, [see this
3030

3131
- [Introspective macros](#introspective-macros):
3232
- [get_column_values](#get_column_values-source)
33+
- [get_filtered_columns_in_relation](#get_filtered_columns_in_relation-source)
3334
- [get_relations_by_pattern](#get_relations_by_pattern-source)
3435
- [get_relations_by_prefix](#get_relations_by_prefix-source)
3536
- [get_query_results_as_dict](#get_query_results_as_dict-source)
@@ -544,7 +545,7 @@ These macros run a query and return the results of the query as objects. They ar
544545
#### get_column_values ([source](macros/sql/get_column_values.sql))
545546
This macro returns the unique values for a column in a given [relation](https://docs.getdbt.com/docs/writing-code-in-dbt/class-reference/#relation) as an array.
546547

547-
Arguments:
548+
**Args:**
548549
- `table` (required): a [Relation](https://docs.getdbt.com/reference/dbt-classes#relation) (a `ref` or `source`) that contains the list of columns you wish to select from
549550
- `column` (required): The name of the column you wish to find the column values of
550551
- `order_by` (optional, default=`'count(*) desc'`): How the results should be ordered. The default is to order by `count(*) desc`, i.e. decreasing frequency. Setting this as `'my_column'` will sort alphabetically, while `'min(created_at)'` will sort by when thevalue was first observed.
@@ -585,6 +586,28 @@ Arguments:
585586
...
586587
```
587588

589+
#### get_filtered_columns_in_relation ([source](macros/sql/get_filtered_columns_in_relation.sql))
590+
This macro returns an iterable Jinja list of columns for a given [relation](https://docs.getdbt.com/docs/writing-code-in-dbt/class-reference/#relation), (i.e. not from a CTE)
591+
- optionally exclude columns
592+
- the input values are not case-sensitive (input uppercase or lowercase and it will work!)
593+
> Note: The native [`adapter.get_columns_in_relation` macro](https://docs.getdbt.com/reference/dbt-jinja-functions/adapter#get_columns_in_relation) allows you
594+
to pull column names in a non-filtered fashion, also bringing along with it other (potentially unwanted) information, such as dtype, char_size, numeric_precision, etc.
595+
596+
**Args:**
597+
- `from` (required): a [Relation](https://docs.getdbt.com/reference/dbt-classes#relation) (a `ref` or `source`) that contains the list of columns you wish to select from
598+
- `except` (optional, default=`[]`): The name of the columns you wish to exclude. (case-insensitive)
599+
600+
**Usage:**
601+
```sql
602+
-- Returns a list of the columns from a relation, so you can then iterate in a for loop
603+
{% set column_names = dbt_utils.get_filtered_columns_in_relation(from=ref('your_model'), except=["field_1", "field_2"]) %}
604+
...
605+
{% for column_name in column_names %}
606+
max({{ column_name }}) ... as max_'{{ column_name }}',
607+
{% endfor %}
608+
...
609+
```
610+
588611
#### get_relations_by_pattern ([source](macros/sql/get_relations_by_pattern.sql))
589612
Returns a list of [Relations](https://docs.getdbt.com/docs/writing-code-in-dbt/class-reference/#relation)
590613
that match a given schema- or table-name pattern.
@@ -748,9 +771,19 @@ group by 1,2,3
748771
```
749772

750773
#### star ([source](macros/sql/star.sql))
751-
This macro generates a comma-separated list of all fields that exist in the `from` relation, excluding any fields listed in the `except` argument. The construction is identical to `select * from {{ref('my_model')}}`, replacing star (`*`) with the star macro. This macro also has an optional `relation_alias` argument that will prefix all generated fields with an alias (`relation_alias`.`field_name`).
774+
This macro generates a comma-separated list of all fields that exist in the `from` relation, excluding any fields
775+
listed in the `except` argument. The construction is identical to `select * from {{ref('my_model')}}`, replacing star (`*`) with
776+
the star macro.
777+
This macro also has an optional `relation_alias` argument that will prefix all generated fields with an alias (`relation_alias`.`field_name`).
778+
The macro also has optional `prefix` and `suffix` arguments. When one or both are provided, they will be concatenated onto each field's alias
779+
in the output (`prefix` ~ `field_name` ~ `suffix`). NB: This prevents the output from being used in any context other than a select statement.
752780

753-
The macro also has optional `prefix` and `suffix` arguments. When one or both are provided, they will be concatenated onto each field's alias in the output (`prefix` ~ `field_name` ~ `suffix`). NB: This prevents the output from being used in any context other than a select statement.
781+
**Args:**
782+
- `from` (required): a [Relation](https://docs.getdbt.com/reference/dbt-classes#relation) (a `ref` or `source`) that contains the list of columns you wish to select from
783+
- `except` (optional, default=`[]`): The name of the columns you wish to exclude. (case-insensitive)
784+
- `relation_alias` (optional, default=`''`): will prefix all generated fields with an alias (`relation_alias`.`field_name`).
785+
- `prefix` (optional, default=`''`): will prefix the output `field_name` (`field_name as prefix_field_name`).
786+
- `suffix` (optional, default=`''`): will suffix the output `field_name` (`field_name as field_name_suffix`).
754787

755788
**Usage:**
756789
```sql
@@ -767,6 +800,13 @@ from {{ ref('my_model') }}
767800

768801
```
769802

803+
```sql
804+
select
805+
{{ dbt_utils.star(from=ref('my_model'), except=["exclude_field_1", "exclude_field_2"], prefix="max_") }}
806+
from {{ ref('my_model') }}
807+
808+
```
809+
770810
#### union_relations ([source](macros/sql/union.sql))
771811

772812
This macro unions together an array of [Relations](https://docs.getdbt.com/docs/writing-code-in-dbt/class-reference/#relation),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
field_1,field_2,field_3
2+
a,b,c
3+
d,e,f
4+
g,h,i
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
field_2,field_3
2+
h,i
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{% macro assert_equal_values(actual_object, expected_object) %}
2+
{% if not execute %}
3+
4+
{# pass #}
5+
6+
{% elif actual_object != expected_object %}
7+
8+
{% set msg %}
9+
Expected did not match actual
10+
11+
-----------
12+
Actual:
13+
-----------
14+
--->{{ actual_object }}<---
15+
16+
-----------
17+
Expected:
18+
-----------
19+
--->{{ expected_object }}<---
20+
21+
{% endset %}
22+
23+
{{ log(msg, info=True) }}
24+
25+
select 'fail'
26+
27+
{% else %}
28+
29+
select 'ok' {{ limit_zero() }}
30+
31+
{% endif %}
32+
{% endmacro %}

integration_tests/models/sql/schema.yml

+10
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ models:
5050
values:
5151
- '5'
5252

53+
- name: test_get_filtered_columns_in_relation
54+
tests:
55+
- dbt_utils.equality:
56+
compare_model: ref('data_filtered_columns_in_relation_expected')
57+
5358
- name: test_get_relations_by_prefix_and_union
5459
columns:
5560
- name: event
@@ -121,6 +126,11 @@ models:
121126
- dbt_utils.equality:
122127
compare_model: ref('data_star_aggregate_expected')
123128

129+
- name: test_star_uppercase
130+
tests:
131+
- dbt_utils.equality:
132+
compare_model: ref('data_star_expected')
133+
124134
- name: test_surrogate_key
125135
tests:
126136
- assert_equal:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{% set exclude_field = 'field_1' %}
2+
{% set column_names = dbt_utils.get_filtered_columns_in_relation(from= ref('data_filtered_columns_in_relation'), except=[exclude_field]) %}
3+
4+
with data as (
5+
6+
select
7+
8+
{% for column_name in column_names %}
9+
max({{ column_name }}) as {{ column_name }} {% if not loop.last %},{% endif %}
10+
{% endfor %}
11+
12+
from {{ ref('data_filtered_columns_in_relation') }}
13+
14+
)
15+
16+
select * from data
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{% set exclude_field = 'FIELD_3' %}
2+
3+
4+
with data as (
5+
6+
select
7+
{{ dbt_utils.star(from=ref('data_star'), except=[exclude_field]) }}
8+
9+
from {{ ref('data_star') }}
10+
11+
)
12+
13+
select * from data
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{% macro get_filtered_columns_in_relation(from, except=[]) -%}
2+
{{ return(adapter.dispatch('get_filtered_columns_in_relation', 'dbt_utils')(from, except)) }}
3+
{% endmacro %}
4+
5+
{% macro default__get_filtered_columns_in_relation(from, except=[]) -%}
6+
{%- do dbt_utils._is_relation(from, 'get_filtered_columns_in_relation') -%}
7+
{%- do dbt_utils._is_ephemeral(from, 'get_filtered_columns_in_relation') -%}
8+
9+
{# -- Prevent querying of db in parsing mode. This works because this macro does not create any new refs. #}
10+
{%- if not execute -%}
11+
{{ return('') }}
12+
{% endif %}
13+
14+
{%- set include_cols = [] %}
15+
{%- set cols = adapter.get_columns_in_relation(from) -%}
16+
{%- set except = except | map("lower") | list %}
17+
{%- for col in cols -%}
18+
{%- if col.column|lower not in except -%}
19+
{% do include_cols.append(col.column) %}
20+
{%- endif %}
21+
{%- endfor %}
22+
23+
{{ return(include_cols) }}
24+
25+
{%- endmacro %}

macros/sql/star.sql

+2-13
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,9 @@
1111
{{ return('') }}
1212
{% endif %}
1313

14-
{%- set include_cols = [] %}
15-
{%- set cols = adapter.get_columns_in_relation(from) -%}
16-
{%- set except = except | map("lower") | list %}
17-
{%- for col in cols -%}
14+
{%- for col in dbt_utils.get_filtered_columns_in_relation(from, except) %}
1815

19-
{%- if col.column|lower not in except -%}
20-
{% do include_cols.append(col.column) %}
21-
22-
{%- endif %}
23-
{%- endfor %}
24-
25-
{%- for col in include_cols %}
26-
27-
{%- if relation_alias %}{{ relation_alias }}.{% else %}{%- endif -%}{{ adapter.quote(col)|trim }} {%- if prefix!='' or suffix!='' -%} as {{ adapter.quote(prefix ~ col ~ suffix)|trim }} {%- endif -%}
16+
{%- if relation_alias %}{{ relation_alias }}.{% else %}{%- endif -%}{{ adapter.quote(col)|trim }} {%- if prefix!='' or suffix!='' %} as {{ adapter.quote(prefix ~ col ~ suffix)|trim }} {%- endif -%}
2817
{%- if not loop.last %},{{ '\n ' }}{% endif %}
2918

3019
{%- endfor -%}

0 commit comments

Comments
 (0)