Skip to main content
The INSERT statement adds rows to an existing dataset. NQL supports two forms: INSERT ... VALUES (...) for literal rows, and INSERT ... SELECT ... for inserting the result of a query.
INSERT runs against datasets you own. To create a new dataset from a query, use CREATE MATERIALIZED VIEW. To upsert into an existing materialized view during refresh, use the MERGE ON clause.

Syntax

VALUES form

INSERT INTO table_reference (column1, column2, ...)
VALUES
  (value1, value2, ...)
  [, (value1, value2, ...), ...]

SELECT form

INSERT INTO table_reference (column1, column2, ...)
SELECT
  column1,
  column2,
  ...
FROM source_reference
[WHERE condition]

Explicit column list is required

The column list after INSERT INTO table_reference is required. NQL does not infer columns from the dataset schema, and wildcards (SELECT *) are not supported in the SELECT form either.
-- Correct
INSERT INTO company_data."123" (user_id, email)
VALUES (42, '[email protected]')

-- Not supported — missing column list
INSERT INTO company_data."123"
VALUES (42, '[email protected]')
See Explicit column selection for the rationale and how it applies across NQL.

Column matching and types

  • The number of expressions in each VALUES row (or columns in the SELECT clause) must match the column list.
  • Expressions are matched to columns by position, not by name.
  • Each expression must be assignable to the target column’s data type. Implicit coercions follow the same rules as comparisons elsewhere in NQL.

Examples

Single-row VALUES:
INSERT INTO company_data."123" (dataset_id, status, created_at)
VALUES (456, 'created', CURRENT_TIMESTAMP)
Multi-row VALUES:
INSERT INTO company_data.pipeline_log (dataset_id, status, created_at)
VALUES
  (101, 'created', CURRENT_TIMESTAMP),
  (102, 'created', CURRENT_TIMESTAMP),
  (103, 'skipped', CURRENT_TIMESTAMP)
INSERT from a SELECT:
INSERT INTO company_data."456" (user_id, email, created_at)
SELECT
  company_data."123".user_id,
  company_data."123".email,
  company_data."123".created_at
FROM company_data."123"
WHERE company_data."123".created_at >= CURRENT_DATE - INTERVAL '7' DAY
INSERT with transformations and a join:
INSERT INTO company_data."789" (user_id, email, region)
SELECT
  u.user_id,
  LOWER(u.email) AS email,
  g.region
FROM company_data."123" AS u
JOIN company_data."456" AS g
  ON u.user_id = g.user_id
WHERE u.is_active = TRUE

Execution context

INSERT statements can run in two places:
Execution surfaceWhen to use
NQL query endpointAd-hoc inserts from an API client or Data Studio
ExecuteDml workflow taskScheduled or event-driven inserts inside a workflow
INSERT does not create datasets. The target dataset must already exist. Use CREATE MATERIALIZED VIEW to create datasets from a query, then INSERT to add rows afterward.
For bulk loads from one dataset to another, INSERT ... SELECT is almost always faster and cheaper than issuing many single-row INSERT ... VALUES statements.

UPDATE

Modify existing rows in a dataset

DELETE

Remove rows from a dataset

Incremental Upserts with MERGE ON

Update or insert rows during materialized view refresh

CREATE MATERIALIZED VIEW

Create a new dataset from a query

ExecuteDml workflow task

Run DML statements from a workflow

Data Types

Type rules for INSERT expressions