# Google BigQuery

> Query, read and add to the data in Google BigQuery.

App · Databases · 15 actions. Web page: https://docs.lodol.com/docs/integrations/google-bigquery

## Actions

### Add a row

Add one row to a table. Pick the table and its columns appear as fields to fill in. The row is queryable within seconds.

**Inputs**

- Project (`project_id`, text, required): The Google Cloud project holding the data.
- Dataset (`dataset_id`, text, required): The dataset the table is in.
- Table (`table_id`, text, required): The table to work with.
- Row (`row`, object, required): The values to store. Pick a table and its columns appear as fields to fill in; if the table comes from an earlier step, enter the column-and-value pairs yourself (for example, {"country": "NO", "visits": 42}).

**Outputs**

- `dataset_id` (text)
- `inserted` (number): How many rows were added
- `project_id` (text)
- `table` (text): The table written to, as project.dataset.table
- `table_id` (text)

**Example output**

```json
{
  "dataset_id": "web",
  "inserted": 1,
  "project_id": "acme-analytics",
  "table": "acme-analytics.web.sessions",
  "table_id": "sessions"
}
```

### Add rows

Add many rows to a table at once, usually the rows an earlier step produced. All of them land or none of them do.

**Inputs**

- Project (`project_id`, text, required): The Google Cloud project holding the data.
- Dataset (`dataset_id`, text, required): The dataset the table is in.
- Table (`table_id`, text, required): The table to work with.
- Rows (`rows`, list, required): The rows to add, as a list of objects keyed by column name, usually the rows a previous step produced. Every key has to be a column of the table.

**Outputs**

- `dataset_id` (text)
- `inserted` (number): How many rows were added
- `project_id` (text)
- `table` (text): The table written to, as project.dataset.table
- `table_id` (text)

**Example output**

```json
{
  "dataset_id": "web",
  "inserted": 24,
  "project_id": "acme-analytics",
  "table": "acme-analytics.web.sessions",
  "table_id": "sessions"
}
```

### Check a query

Ask BigQuery what a query would do without running it: whether it works, how much data it would read, roughly what that costs, and which columns it would return. Costs nothing, and never fails the step for a broken query; it reports the problem.

**Inputs**

- Project (`project_id`, text, required): The project the query runs in, and the one it is billed to. Tables in other projects can still be named in full.
- Query (`sql`, text, required): The SQL to run, in standard SQL. Write ? wherever a value goes and put the values in the box below, so a quote in the data can't change the query.
- Values (`parameters`, list): One value for each ? in the query, in the order they appear. A plain number is sent as a number and true or false as a yes/no; everything else is sent as text.

**Outputs**

- `columns` (list): The columns it would return, in order
- `error` (text): What is wrong with the query, when it is not valid
- `estimated_cost_usd` (number): Rough on-demand cost of running it, in US dollars
- `gigabytes_scanned` (number): How much data the query would read, in GB
- `referenced_tables` (list): The tables it reads, each as project.dataset.table
- `statement_type` (text): What kind of statement it is, e.g. SELECT or INSERT
- `valid` (true/false): True when the query would run; false when it has an error

**Example output**

```json
{
  "columns": [
    {
      "description": "Two-letter country code.",
      "mode": "NULLABLE",
      "name": "country",
      "type": "STRING"
    },
    {
      "description": null,
      "mode": "NULLABLE",
      "name": "visits",
      "type": "INT64"
    },
    {
      "description": null,
      "mode": "NULLABLE",
      "name": "last_seen",
      "type": "TIMESTAMP"
    }
  ],
  "error": null,
  "estimated_cost_usd": 0.002571,
  "gigabytes_scanned": 0.4213,
  "referenced_tables": [
    "acme-analytics.web.sessions"
  ],
  "statement_type": "SELECT",
  "valid": true
}
```

### Copy a table

Copy a whole table, rows and all, to another name or another dataset in the same project. Copying doesn't scan the data, so it costs nothing to run.

**Inputs**

- Project (`project_id`, text, required): The Google Cloud project holding the data.
- Dataset (`dataset_id`, text, required): The dataset the table is in.
- Table (`table_id`, text, required): The table to work with.
- Name for the copy (`destination_table_id`, text, required): What to call the copy.
- Dataset for the copy (`destination_dataset_id`, text): Where to put the copy. Leave empty to keep it beside the original.
- If the table already has data (`write_mode`, text): What to do when the table you're writing to isn't empty. One of: `only_if_empty` (Only if it's empty (refuse rather than overwrite)), `append` (Add to what's already there), `replace` (Replace everything that's there). Default: `only_if_empty`.

**Outputs**

- `dataset_id` (text)
- `destination` (text): The table copied to, as project.dataset.table
- `job_id` (text): BigQuery's id for this run
- `project_id` (text)
- `source` (text): The table copied from, as project.dataset.table
- `table_id` (text)
- `total_rows` (number): How many rows the destination holds now
- `write_mode` (text): How the copy was written: append, replace or only_if_empty

**Example output**

```json
{
  "dataset_id": "web",
  "destination": "acme-analytics.web.sessions_backup",
  "job_id": "job_2Qw7ZxN5rL8kJ3hB",
  "project_id": "acme-analytics",
  "source": "acme-analytics.web.sessions",
  "table_id": "sessions_backup",
  "total_rows": 48211934,
  "write_mode": "only_if_empty"
}
```

### Create a dataset

Create an empty dataset (BigQuery's folder of tables) for tables to live in. Where it lives is fixed from the moment it is made, and a query can't read across two locations, so put it where your other data is.

**Inputs**

- Project (`project_id`, text, required): The Google Cloud project holding the data.
- Name for the new dataset (`dataset_id`, text, required): What to call it. Letters, numbers and underscores; it has to be a name no other dataset in the project already has.
- Where its data lives (`location`, text): Pick where your other data already is: a query can't read across two locations, and this can never be changed afterwards. Leave empty for the United States.
- Description (`description`, text): What the dataset is for, shown in BigQuery.

**Outputs**

- `created` (text): When it was created (ISO 8601)
- `dataset_id` (text): The dataset id
- `description` (text): What it is for, when it was given a description
- `full_name` (text): The dataset as project.dataset
- `location` (text): Where its data lives, e.g. US or europe-west2
- `project_id` (text): The project it belongs to

**Example output**

```json
{
  "created": "2026-09-15T11:02:18Z",
  "dataset_id": "web",
  "description": "Website analytics tables.",
  "full_name": "acme-analytics.web",
  "location": "US",
  "project_id": "acme-analytics"
}
```

### Create a table

Create an empty table with the columns you describe, ready for rows to be added to it.

**Inputs**

- Project (`project_id`, text, required): The Google Cloud project holding the data.
- Dataset (`dataset_id`, text, required): The dataset the table is in.
- Name for the new table (`table_id`, text, required): What to call it. Letters, numbers and underscores; it has to be a name no other table in the dataset already has.
- Columns (`columns`, list, required): The columns the table will have, in order.
- Description (`description`, text): What the table is for, shown in BigQuery.
- Date column to split by day (`partition_by`, text): The name of one of the date or time columns above. BigQuery then stores the table a day at a time, so a query filtered to a few days reads only those days and costs a fraction as much. Leave empty for a plain table.

**Outputs**

- `columns` (list): The columns it was created with
- `created` (text): When it was created (ISO 8601)
- `dataset_id` (text): The dataset it belongs to
- `full_name` (text): The table as project.dataset.table, ready to paste into SQL
- `kind` (text): table, view, materialized view, external table or snapshot
- `partitioned_by` (text): The column it is partitioned by day on, when it is
- `project_id` (text): The project it belongs to
- `table_id` (text): The table id

**Example output**

```json
{
  "columns": [
    {
      "description": null,
      "mode": "REQUIRED",
      "name": "email",
      "type": "STRING"
    },
    {
      "description": "When they signed up.",
      "mode": "REQUIRED",
      "name": "signed_up_at",
      "type": "TIMESTAMP"
    },
    {
      "description": null,
      "mode": "NULLABLE",
      "name": "plan",
      "type": "STRING"
    }
  ],
  "created": "2026-09-15T11:04:52Z",
  "dataset_id": "web",
  "full_name": "acme-analytics.web.signups",
  "kind": "table",
  "partitioned_by": "signed_up_at",
  "project_id": "acme-analytics",
  "table_id": "signups"
}
```

### Delete a dataset

Delete a dataset. One that still holds tables is left alone unless you say to delete those too. This can't be undone.

**Inputs**

- Project (`project_id`, text, required): The Google Cloud project holding the data.
- Dataset (`dataset_id`, text, required): The dataset the table is in.
- Delete the tables in it too (`delete_tables`, true/false): A dataset that still holds tables is left alone unless you turn this on. Everything in it goes, and none of it can be brought back. Default: `false`.

**Outputs**

- `dataset` (text): The dataset that was deleted, as project.dataset
- `dataset_id` (text)
- `deleted` (true/false): True once the dataset is gone
- `project_id` (text)
- `tables_deleted` (true/false): True when the tables inside it were deleted along with it

**Example output**

```json
{
  "dataset": "acme-analytics.scratch",
  "dataset_id": "scratch",
  "deleted": true,
  "project_id": "acme-analytics",
  "tables_deleted": false
}
```

### Delete a table

Delete a table and every row in it. This can't be undone, so point it at a table the workflow made rather than one people rely on.

**Inputs**

- Project (`project_id`, text, required): The Google Cloud project holding the data.
- Dataset (`dataset_id`, text, required): The dataset the table is in.
- Table (`table_id`, text, required): The table to work with.

**Outputs**

- `dataset_id` (text)
- `deleted` (true/false): True once the table is gone
- `project_id` (text)
- `table` (text): The table that was deleted, as project.dataset.table
- `table_id` (text)

**Example output**

```json
{
  "dataset_id": "web",
  "deleted": true,
  "project_id": "acme-analytics",
  "table": "acme-analytics.web.sessions_backup",
  "table_id": "sessions_backup"
}
```

### Get table details

Everything about one table: its columns and their types, how many rows it holds, how big it is, and when it last changed. The step to put in front of a query when the columns aren't known yet.

**Inputs**

- Project (`project_id`, text, required): The Google Cloud project holding the data.
- Dataset (`dataset_id`, text, required): The dataset the table is in.
- Table (`table_id`, text, required): The table to work with.

**Outputs**

- `clustered_by` (list): The columns it is clustered by, in order
- `columns` (list): Every column, in order. A column inside a record reads as parent.child
- `created` (text): When it was created (ISO 8601)
- `dataset_id` (text): The dataset it belongs to
- `description` (text): The table's description, when it has one
- `full_name` (text): The table as project.dataset.table, ready to paste into SQL
- `gigabytes` (number): How much it stores, in GB
- `kind` (text): table, view, materialized view, external table or snapshot
- `last_modified` (text): When it last changed (ISO 8601)
- `location` (text): Where its data lives, e.g. US or europe-west2
- `partitioned_by` (text): The column it is partitioned by day on, when it is
- `project_id` (text): The project it belongs to
- `row_count` (number): How many rows it holds
- `size_bytes` (number): How much it stores, in bytes
- `table_id` (text): The table id

**Example output**

```json
{
  "clustered_by": [
    "country"
  ],
  "columns": [
    {
      "description": "Two-letter country code.",
      "mode": "NULLABLE",
      "name": "country",
      "type": "STRING"
    },
    {
      "description": null,
      "mode": "NULLABLE",
      "name": "visits",
      "type": "INT64"
    },
    {
      "description": null,
      "mode": "NULLABLE",
      "name": "last_seen",
      "type": "TIMESTAMP"
    }
  ],
  "created": "2026-01-08T09:30:00Z",
  "dataset_id": "web",
  "description": "One row per website session.",
  "full_name": "acme-analytics.web.sessions",
  "gigabytes": 7.2078,
  "kind": "table",
  "last_modified": "2026-09-15T04:02:11Z",
  "location": "US",
  "partitioned_by": "session_date",
  "project_id": "acme-analytics",
  "row_count": 48211934,
  "size_bytes": 7739383808,
  "table_id": "sessions"
}
```

### List datasets

The datasets in a project, with where each one's data lives. A dataset is BigQuery's folder of tables.

**Inputs**

- Project (`project_id`, text, required): The Google Cloud project holding the data.
- Most to return (`limit`, number): How many to bring back at most. From 1 to 1000. Default: `200`.

**Outputs**

- `count` (number): How many datasets came back
- `datasets` (list): The datasets, by id
- `project_id` (text): The project they belong to

**Example output**

```json
{
  "count": 1,
  "datasets": [
    {
      "dataset_id": "web",
      "full_name": "acme-analytics.web",
      "location": "US",
      "name": "Website analytics",
      "project_id": "acme-analytics"
    }
  ],
  "project_id": "acme-analytics"
}
```

### List projects

The Google Cloud projects this account can run BigQuery in, with the project id every other step asks for.

**Inputs**

- Most to return (`limit`, number): How many to bring back at most. From 1 to 1000. Default: `200`.

**Outputs**

- `count` (number): How many projects came back
- `projects` (list): The projects, by display name

**Example output**

```json
{
  "count": 1,
  "projects": [
    {
      "name": "Acme Analytics",
      "numeric_id": "418293047551",
      "project_id": "acme-analytics"
    }
  ]
}
```

### List tables

The tables and views in a dataset, each with the full project.dataset.table name a query needs.

**Inputs**

- Project (`project_id`, text, required): The Google Cloud project holding the data.
- Dataset (`dataset_id`, text, required): The dataset the table is in.
- Most to return (`limit`, number): How many to bring back at most. From 1 to 1000. Default: `200`.

**Outputs**

- `count` (number): How many came back
- `dataset_id` (text)
- `project_id` (text)
- `tables` (list): The tables and views, by id

**Example output**

```json
{
  "count": 1,
  "dataset_id": "web",
  "project_id": "acme-analytics",
  "tables": [
    {
      "created": "2026-01-08T09:30:00Z",
      "dataset_id": "web",
      "full_name": "acme-analytics.web.sessions",
      "kind": "table",
      "partitioned_by": "session_date",
      "project_id": "acme-analytics",
      "table_id": "sessions"
    }
  ]
}
```

### Preview rows

Read rows straight out of a table without running a query. This reads storage rather than scanning data, so it costs nothing, so reach for it instead of SELECT * when all you want is a look at what's in there.

**Inputs**

- Project (`project_id`, text, required): The Google Cloud project holding the data.
- Dataset (`dataset_id`, text, required): The dataset the table is in.
- Table (`table_id`, text, required): The table to work with.
- Columns to return (`columns`, list): Which columns to bring back. Leave empty for all of them.
- Most rows to return (`max_rows`, number): How many rows to bring back at most, so a wider result than expected can't flood the workflow. From 1 to 50000. Default: `1000`.
- Start at row (`start_row`, number): Skip this many rows first, to read the next page of a big table. Starts at 0. At least 0. Default: `0`.

**Outputs**

- `columns` (list): The columns of the rows, in order
- `next_start_row` (number): The 'start at row' that reads the next page, when there is one
- `row_count` (number): How many rows came back
- `rows` (list): The rows that came back, keyed by column name
- `table` (text): The table read, as project.dataset.table
- `total_rows` (number): How many rows there were in total, before any limit
- `truncated` (true/false): True when there were more rows than the limit allowed

**Example output**

```json
{
  "columns": [
    {
      "description": "Two-letter country code.",
      "mode": "NULLABLE",
      "name": "country",
      "type": "STRING"
    },
    {
      "description": null,
      "mode": "NULLABLE",
      "name": "visits",
      "type": "INT64"
    },
    {
      "description": null,
      "mode": "NULLABLE",
      "name": "last_seen",
      "type": "TIMESTAMP"
    }
  ],
  "next_start_row": 2,
  "row_count": 2,
  "rows": [
    {
      "country": "NO",
      "last_seen": "2026-09-14T22:15:00Z",
      "visits": 1284
    },
    {
      "country": "SE",
      "last_seen": "2026-09-14T21:52:11Z",
      "visits": 970
    }
  ],
  "table": "acme-analytics.web.sessions",
  "total_rows": 48211934,
  "truncated": true
}
```

### Run a query

Run a SQL query and get the rows back, each value already the type its column says it is. Also runs INSERT, UPDATE, DELETE and MERGE, and reports how many rows changed.

**Inputs**

- Project (`project_id`, text, required): The project the query runs in, and the one it is billed to. Tables in other projects can still be named in full.
- Query (`sql`, text, required): The SQL to run, in standard SQL. Write ? wherever a value goes and put the values in the box below, so a quote in the data can't change the query.
- Values (`parameters`, list): One value for each ? in the query, in the order they appear. A plain number is sent as a number and true or false as a yes/no; everything else is sent as text.
- Most rows to return (`max_rows`, number): How many rows to bring back at most, so a wider result than expected can't flood the workflow. From 1 to 50000. Default: `1000`.
- Maximum data to scan (GB) (`maximum_gigabytes_scanned`, number): Refuse to run the query if it would read more than this. A safety net for a mistyped query against a very large table. Leave empty for no limit. From 0.01 to 1000000.

**Outputs**

- `columns` (list): The columns of the rows, in order
- `estimated_cost_usd` (number): Rough on-demand cost of this run, in US dollars
- `from_cache` (true/false): True when BigQuery served a cached result, free of charge
- `gigabytes_scanned` (number): How much data the query read, in GB
- `job_id` (text): BigQuery's id for this run
- `project_id` (text): The project it ran in
- `row_count` (number): How many rows came back
- `rows` (list): The rows that came back, keyed by column name
- `rows_changed` (number): Rows the statement inserted, updated or deleted. Empty for a SELECT, which changes nothing
- `statement_type` (text): What kind of statement ran, e.g. SELECT or INSERT
- `total_rows` (number): How many rows there were in total, before any limit
- `truncated` (true/false): True when there were more rows than the limit allowed

**Example output**

```json
{
  "columns": [
    {
      "description": "Two-letter country code.",
      "mode": "NULLABLE",
      "name": "country",
      "type": "STRING"
    },
    {
      "description": null,
      "mode": "NULLABLE",
      "name": "visits",
      "type": "INT64"
    },
    {
      "description": null,
      "mode": "NULLABLE",
      "name": "last_seen",
      "type": "TIMESTAMP"
    }
  ],
  "estimated_cost_usd": 0.002571,
  "from_cache": false,
  "gigabytes_scanned": 0.4213,
  "job_id": "job_9Hs2KpQ1vB4nR7xT",
  "project_id": "acme-analytics",
  "row_count": 2,
  "rows": [
    {
      "country": "NO",
      "last_seen": "2026-09-14T22:15:00Z",
      "visits": 1284
    },
    {
      "country": "SE",
      "last_seen": "2026-09-14T21:52:11Z",
      "visits": 970
    }
  ],
  "rows_changed": null,
  "statement_type": "SELECT",
  "total_rows": 2,
  "truncated": false
}
```

### Save query results to a table

Run a query and write its rows straight into a table, creating the table if it isn't there. The way to rebuild a summary table on a schedule without pulling the rows through the workflow.

**Inputs**

- Project (`project_id`, text, required): The project the query runs in, and the one it is billed to. Tables in other projects can still be named in full.
- Query (`sql`, text, required): The SQL to run, in standard SQL. Write ? wherever a value goes and put the values in the box below, so a quote in the data can't change the query.
- Values (`parameters`, list): One value for each ? in the query, in the order they appear. A plain number is sent as a number and true or false as a yes/no; everything else is sent as text.
- Dataset to save into (`dataset_id`, text, required): The dataset the results table lives in.
- Table to save into (`table_id`, text, required): Where to put the results. Pick a table to write over or add to, or type a name and it will be created.
- If the table already has data (`write_mode`, text): What to do when the table you're writing to isn't empty. One of: `only_if_empty` (Only if it's empty (refuse rather than overwrite)), `append` (Add to what's already there), `replace` (Replace everything that's there). Default: `only_if_empty`.
- Maximum data to scan (GB) (`maximum_gigabytes_scanned`, number): Refuse to run the query if it would read more than this. A safety net for a mistyped query against a very large table. Leave empty for no limit. From 0.01 to 1000000.

**Outputs**

- `dataset_id` (text)
- `estimated_cost_usd` (number): Rough on-demand cost of this run, in US dollars
- `gigabytes_scanned` (number): How much data the query read, in GB
- `job_id` (text): BigQuery's id for this run
- `project_id` (text)
- `table` (text): The table written to, as project.dataset.table
- `table_id` (text)
- `total_rows` (number): How many rows the table holds now
- `write_mode` (text): How the results were written: append, replace or only_if_empty

**Example output**

```json
{
  "dataset_id": "web",
  "estimated_cost_usd": 0.007352,
  "gigabytes_scanned": 1.2044,
  "job_id": "job_4Tn8LcV2wY6mF1sD",
  "project_id": "acme-analytics",
  "table": "acme-analytics.web.sessions_daily",
  "table_id": "sessions_daily",
  "total_rows": 365,
  "write_mode": "replace"
}
```
