Create tables

In Dataform, a table is one of the types of objects that make up a workflow. You can create tables that reference data from the data sources declared for your workflow or from other tables in your workflow. Dataform compiles your table definitions into SQL in real time. When you trigger execution, Dataform runs the SQL code and creates your defined tables in BigQuery.

You can create the following table types in a type: "table" SQLX file:

You can also define table partitions and clusters.

To keep a record of the purpose of a table or its relation to other tables in your workflow, you can add documentation to the table or its selected columns.

To test the data in a table against specific conditions, you can create data quality test queries called assertions. Dataform runs assertions every time it updates your workflow and alerts you if any assertions fail.

To override the default schema, database, and name of a selected table, you can override table settings.

To disable table creation or run a SQL statement before or after table creation, you can configure additional actions.

To organize your tables in BigQuery after you run them, you can add BigQuery labels. To learn more, see Introduction to labels.

To restrict data access at the table column level, you can add BigQuery policy tags. To learn more, see Introduction to column-level access control.

In addition to defining tables in a type: "table" SQLX file, you can create empty tables by defining a custom SQL query in a type: "operations" SQLX file. You might want to create an empty table so that a different service can populate it with data.

Before you begin

  1. In the Google Cloud console, go to the Dataform page.

    Go to Dataform

  2. Create and initialize a development workspace in your repository.

  3. Optional: Declare a data source.

Required roles

To get the permissions that you need to complete the tasks in this document, ask your administrator to grant you the following IAM roles:

For more information about granting roles, see Manage access to projects, folders, and organizations.

You might also be able to get the required permissions through custom roles or other predefined roles.

Create a table

This section shows you how to create tables with Dataform core in Dataform.

About table definitions

To define a table, you define the table type and write a SELECT statement in a type: "table" SQLX file. Dataform then compiles your Dataform core code into SQL, runs the SQL code, and creates your defined tables in BigQuery.

In a Dataform core SELECT statement, you define the table structure and reference other objects of your workflow.

In addition to defining tables in a type: "table" SQLX file, you can create empty tables by defining a custom SQL query in a type: "operations" SQLX file. For more information, see Create an empty table.

Reference dependencies with ref

To reference a workflow action in a SELECT statement and automatically add it as a dependency, use the ref function. Dataform runs dependencies before tables that depend on them to verify the correct pipeline ordering.

The ref function is a built-in Dataform core function that is critical to dependency management in Dataform. The ref function lets you reference and automatically depend on the following objects defined in your Dataform workflow, instead of hard coding the schema and table names:

Dataform uses the ref function to build a dependency tree of all the tables to be created or updated.

After compiling, Dataform adds boilerplate statements to the SQL statement, such as CREATE, REPLACE, INSERT, or MERGE.

The following code sample shows a table definition with the use of the ref function:

config { type: "table" }

SELECT
  order_date AS date,
  order_id AS order_id,
  order_status AS order_status,
  SUM(item_count) AS item_count,
  SUM(amount) AS revenue

FROM ${ref("store_clean")}

GROUP BY 1, 2

In the ref function, you provide the name of the table or data source declaration that you want to depend on. This is typically the filename of the SQLX file in which that table or data source declaration is defined.

If a table name is overridden, use the overridden name in the ref function. For example, reference a table with config { name: "overridden_name" } as ref("overridden_name"). For more information, see Override table settings and Reference a table with an overridden table name.

When you have multiple tables of the same name in different schemas, you can reference a specific table by providing two arguments to the ref function: schema name and table name.

The following code sample shows the ref function with two arguments to specify a table within a specific schema:

config { type: "table" }
SELECT * FROM ${ref("schema", "store_clean")}

You can also add table dependencies manually to the config block for tables, assertions, data source declarations, or custom SQL operations that are not referenced in a ref function in the SELECT statement. Dataform runs these dependencies before dependent tables.

The following code sample shows a table dependency in the config block:

config { dependencies: [ "unreferenced_table" ] }
SELECT * FROM ...

For more information on dependency management in your workflow, see Set dependencies.

Reference other tables with resolve

The resolve function lets you reference a table or data source declaration in a SELECT statement like the ref function, but it doesn't add the reference as a dependency. This means that the object referenced using the resolve function does not affect the execution of the table that uses the resolve function.

For more information on built-in Dataform core functions, see Dataform core reference.

Create a SQLX file for a table definition

Store table definition SQLX files in the definitions/ directory. To create a new SQLX file in the definitions/ directory, follow these steps:

  1. In Google Cloud console, go to the Dataform page.

    Go to Dataform

  2. To open a repository, click the repository name.

  3. To open a development workspace, click the workspace name.

  4. In the Files pane, next to definitions/, click More.

  5. Click Create file.

  6. In the Add a file path field, enter the name of the file followed by .sqlx after definitions/. For example, definitions/my-table.sqlx.

    Filenames can only include numbers, letters, hyphens, and underscores.

  7. Click Create file.

Define the table type

To create a new table type definition, follow these steps:

  1. In your development workspace, in the Files pane, expand the definitions/ directory.
  2. Select the table definition SQLX file that you want to edit.
  3. In the file, enter the following code snippet:

    config { type: "TABLE_TYPE" }
    

    Replace TABLE_TYPE with one of the following table types:

    • table
    • incremental
    • view
  4. Optional: To define a materialized view, enter the materialized property under type: "view" in the following format:

    config {
      type: "view",
      materialized: true
    }
    

    For more information, see ITableConfig.

  5. Optional: Click Format.

Define table structure and dependencies

To write a table definition SELECT statement and define the table structure and dependencies, follow these steps:

  1. In your development workspace, in the Files pane, expand the definitions/ directory.
  2. Select the table definition SQLX file that you want to edit.
  3. Below the config block, write a SELECT statement.
  4. Optional: Click Format.

The following code sample shows a table definition with a SELECT statement and the ref function:

config { type: "table" }
SELECT
  customers.id AS id,
  customers.first_name AS first_name,
  customers.last_name AS last_name,
  customers.email AS email,
  customers.country AS country,
  COUNT(orders.id) AS order_count,
  SUM(orders.amount) AS total_spent
FROM
  dataform-samples.dataform_sample.crm_customers AS customers
  LEFT JOIN ${ref('order_stats')} orders
    ON customers.id = orders.customer_id

WHERE
  customers.id IS NOT NULL
  AND customers.first_name <> 'Internal account'
  AND country IN ('UK', 'US', 'FR', 'ES', 'NG', 'JP')

GROUP BY 1, 2, 3, 4, 5

Add manual table dependencies

To add table dependencies that are not referenced in the SELECT statement but that need to be run before the current table, follow these steps:

  1. In your development workspace, in the Files pane, expand the definitions/ directory.
  2. Select the table definition SQLX file that you want to edit.
  3. In the config block of the table, enter the following code snippet:

    dependencies: [ "DEPENDENCY_TABLE", ]
    

    Replace DEPENDENCY_TABLE with the filename of the table you want to add as a dependency. You can enter multiple filenames.

  4. Optional: Click Format.

The following code sample shows two tables added as manual table dependencies to the config block of a table definition file:

config { dependencies: [ "some_table", "some_other_table" ] }

Override table settings

You can override the default schema, database, and name of a selected table.

By default, a table follows the schema and database configuration you set in workflow_settings.yaml. The name of a table is the same as the name of the table definition SQLX file.

To override the schema and name of a selected table, follow these steps:

  1. Go to your development workspace.

  2. In the Files pane, expand definitions/.

  3. Open a SQLX table definition file.

  4. In the config block, enter the following code snippet:

     {
       schema: "OVERRIDDEN_SCHEMA",
       database: "OVERRIDDEN_DATABASE",
       name: "OVERRIDDEN_NAME"
     }
    

    Replace the following:

    • OVERRIDDEN_SCHEMA: the BigQuery dataset in which you want to create the table.

    • OVERRIDDEN_DATABASE: the ID of the BigQuery project in which you want to create the table.

    • OVERRIDDEN_NAME: the name for the table, which is different from the SQLX table definition filename.

  5. Optional: Click Format.

For more information, see Reference a table with an overridden table name.

Create Apache Iceberg managed tables

Use Dataform to create BigQuery tables in Iceberg table format. These tables are known as Iceberg managed tables. For more information about required roles and other setup tasks, see the Before you begin steps for Iceberg managed table workflows.

Create the Iceberg table definition

To create the tables, define the Iceberg configuration in the bigquery block of a table definition file.

To define the tables, follow these steps:

  1. Go to your development workspace.
  2. In the Files pane, expand definitions/.
  3. Open a table definition SQLX file.
  4. In the config block, add a bigquery block that contains an iceberg block in the following format:

    config {
      type: "table",
      name: "my_table",
      uniqueKey: ["uniquekey"],
      bigquery: {
        iceberg: {
          bucket_name: "BUCKET_NAME"
        }
      }
    }
    

    Replace BUCKET_NAME with the name of the Cloud Storage bucket where the table data is stored.

    Other properties are optional and have default values if not specified. For more information, see the iceberg properties.

    You can provide the values for each table or set workflow-level defaults in workflow_settings.yaml. You can override workflow-level defaults by explicitly setting the properties in the iceberg block.

Create table partitions and clusters

This section shows you how to use Dataform core to create table partitions and clusters. BigQuery supports partitioned tables and table clustering. For more information, see Introduction to partitioned tables and Creating and using clustered tables.

Create a table partition

To create a table partition, follow these steps:

  1. Go to your development workspace.
  2. In the Files pane, expand definitions/.
  3. Open a table definition SQLX file.
  4. In the config block, add the bigquery block below the table type declaration in the following format:

    config {
      type: "table",
      bigquery: {
      }
    }
    
  5. In the bigquery block, enter the following code snippet:

        partitionBy: "PARTITION_EXPRESSION"
    

    Replace PARTITION_EXPRESSION with an expression for partitioning the table.

  6. Optional: Click Format.

The following code sample shows partitioning a table by hour in a table definition SQLX file:

config {
  type: "table",
  bigquery: {
    partitionBy: "DATETIME_TRUNC(<timestamp_column>, HOUR)"
  }
}

The following code sample shows partitioning a table by an integer value in a table definition SQLX file:

config {
  type: "table",
  bigquery: {
    partitionBy: "RANGE_BUCKET(<integer_column>, GENERATE_ARRAY(0, 1000000, 1000))"
  }
}

Set a partition filter

To set a partition filter, follow these steps:

  1. Go to your development workspace.
  2. In the Files pane, expand definitions/.
  3. Open a partitioned table definition SQLX file.
  4. In the bigquery block, enter the following code snippet:

    requirePartitionFilter : true
    
  5. Optional: Click Format.

The following code sample shows a partition filter set in the bigquery block of a partitioned table SQLX file:

config {
  type: "table",
  bigquery: {
    partitionBy: "DATE(ts)",
    requirePartitionFilter : true
  }
}
SELECT CURRENT_TIMESTAMP() AS ts

For more information on the partition filter in BigQuery, see Setting the require partition filter attribute on a partitioned table.

Set a retention period for partitions

To control the retention of all partitions in a partitioned table, follow these steps:

  1. Go to your development workspace.
  2. In the Files pane, expand definitions/.
  3. Open a partitioned table definition SQLX file.
  4. In the bigquery block, enter the following code snippet:

    partitionExpirationDays: NUMBER_OF_DAYS
    

    Replace NUMBER_OF_DAYS with the number of days that you want to retain the partitions for.

  5. Optional: Click Format.

The following code sample shows a retention period for partitions set to 14 days in the bigquery block of a partitioned table SQLX file:

config {
  type: "table",
  bigquery: {
    partitionBy: "DATE(ts)",
    partitionExpirationDays: 14,
  }
}
SELECT CURRENT_TIMESTAMP() AS ts

Create a table cluster

To create a table cluster, follow these steps:

  1. Go to your development workspace.
  2. In the Files pane, expand definitions/.
  3. Open a table definition SQLX file.
  4. In the bigquery block, enter the following code snippet:

        clusterBy: ["CLUSTER_COLUMN"]
    

    Replace CLUSTER_COLUMN with the name of the column by which you want to cluster the table. For more information, see clustering_column_list.

  5. Optional: Click Format.

The following code sample shows a partitioned table clustered by name and revenue columns:

config {
  type: "table",
  bigquery: {
    partitionBy: "DATE(ts)",
    clusterBy: ["name", "revenue"]
  }
}
SELECT CURRENT_TIMESTAMP() as ts, name, revenue

Configure an incremental table

This section shows you how to use Dataform core to configure an incremental table.

About incremental tables

Dataform updates tables differently based on the table type. During each execution of a table or a view, Dataform rebuilds the whole table or view from scratch.

When you define an incremental table, Dataform builds the incremental table from scratch only for the first time. During subsequent executions, Dataform only inserts or merges new rows into the incremental table according to the conditions that you configure.

Dataform inserts new rows only into columns that already exist in the incremental table. If you make changes to the incremental table definition query—for example, by adding a new column—you must decide if you should rebuild the table from scratch. To rebuild the table, the next time you trigger a table run, select the Run with full refresh option. For other options, see Change an incremental table schema without a full refresh.

Here are some common use cases for incremental tables:

Performance optimization
For some kinds of data, such as web logs or analytics data, you might want to only process new records instead of reprocessing the entire table.
Latency reduction
You can use incremental tables to run workflows quickly but frequently, reducing the downstream latency of the output tables.
Daily snapshots
You can configure an incremental table to create daily snapshots of the table data, for example, for longitudinal analysis of user settings stored in a production database.

Process a subset of rows in an incremental table

To determine a subset of rows for Dataform to process during each execution, add a conditional WHERE clause to the incremental table SQLX definition file. In the WHERE clause, you can specify an incremental condition and a non-incremental condition. Dataform applies the incremental condition during table execution without a full refresh, and the non-incremental condition during execution with a full refresh.

To configure an incremental table, follow these steps:

  1. Go to your development workspace.
  2. In the Files pane, expand definitions/.
  3. Open an incremental table definition SQLX file.
  4. Enter a WHERE clause in the following format:

    config { type: "incremental" }
    
    SELECT_STATEMENT
    
    ${when(incremental(), `WHERE INCREMENTAL_CONDITION`, `WHERE NON_INCREMENTAL_CONDITION`) }
    

    Replace the following:

    • SELECT_STATEMENT: the SELECT statement that defines your table.
    • INCREMENTAL_CONDITION: the condition you specify in the WHERE clause to select rows for Dataform to process during table execution without a full refresh.
    • NON_INCREMENTAL_CONDITION: the condition you specify in the WHERE clause to select rows for Dataform to process during table execution with a full refresh.
  5. Optional: Click Format.