> For the complete documentation index, see [llms.txt](https://v2.dataos.info/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://v2.dataos.info/build/stage-2-productize/define-models-and-logic/data-models/statements.md).

# Statements

Statements let you run SQL commands at specific points during model execution. You can run code before the main query, after it completes, or when views are created or updated.

Use statements for tasks that cannot go inside the main `SELECT` query: setting session parameters, creating indexes, granting permissions, logging anomalies, or running cleanup.

`orders-analytics` does not use statements. The models are straightforward FULL rebuilds that do not require pre- or post-execution steps. Statements become relevant when you need session setup, clustering, access control, or custom post-run validation.

***

## Statement types

| Type              | When it runs                      | Common uses                                                   |
| ----------------- | --------------------------------- | ------------------------------------------------------------- |
| Pre-statement     | Before the main query             | Setting session variables, loading UDFs, creating temp tables |
| Post-statement    | After the main query completes    | Creating indexes, granting permissions, logging anomalies     |
| On-virtual-update | When a view is created or updated | Granting SELECT on views, applying column masking policies    |

***

## Pre-statements

Pre-statements run before the main model query. They are useful for setting up the environment the query needs.

Avoid creating or altering physical tables in pre-statements when multiple models may run concurrently. Use them for session settings, UDFs, and temporary objects.

**SQL example:**

```sql
MODEL (
  name analytics.orders,
  kind INCREMENTAL_BY_TIME_RANGE (
    time_column order_date
  ),
  cron '@daily',
  start '2020-01-01'
);

/* Pre-statement: create anomaly tracking table if it does not exist */
CREATE TABLE IF NOT EXISTS analytics._orders_anomalies (
  anomaly_id BIGINT GENERATED ALWAYS AS IDENTITY,
  order_id   VARCHAR,
  anomaly_type VARCHAR,
  details    VARCHAR,
  captured_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

/* Main query */
SELECT
  order_id::VARCHAR    AS order_id,
  order_date::DATE     AS order_date,
  customer_id::VARCHAR AS customer_id,
  total_amount::FLOAT  AS total_amount
FROM raw.orders
WHERE order_date BETWEEN @start_date AND @end_date;
```

**Python equivalent** - pass pre-statements as a list in the `@model` decorator:

```python
@model(
    "analytics.orders_py",
    kind=dict(
        name=ModelKindName.INCREMENTAL_BY_TIME_RANGE,
        time_column="order_date",
    ),
    pre_statements=[
        "SET query_timeout = 300000",
        """CREATE TABLE IF NOT EXISTS analytics._orders_anomalies (
            order_id VARCHAR, anomaly_type VARCHAR, details VARCHAR
        )""",
    ],
)
def execute(context, start, end, **kwargs):
    ...
```

***

## Post-statements

Post-statements run after the model query completes. When using post-statements in SQL models, the main query must end with a semicolon so Vulcan knows where it ends.

**SQL example:**

```sql
MODEL (
  name analytics.orders,
  kind INCREMENTAL_BY_TIME_RANGE (
    time_column order_date
  )
);

SELECT
  order_id, order_date, customer_id, quantity, total_amount
FROM raw.orders
WHERE order_date BETWEEN @start_date AND @end_date;

/* Post-statement: set retention policy only on first table creation */
@IF(
  @runtime_stage IN ('creating'),
  ALTER TABLE @this_model SET DATA_RETENTION_TIME_IN_DAYS = 30
);

/* Post-statement: log negative quantities as anomalies */
INSERT INTO analytics._orders_anomalies (order_id, anomaly_type, details)
SELECT
  order_id,
  'NEGATIVE_QUANTITY',
  CONCAT('quantity=', quantity)
FROM @this_model
WHERE quantity < 0;
```

***

## On-virtual-update statements

On-virtual-update statements run when Vulcan creates or updates a view in the virtual layer. Use them for access control and column masking.

Note: `@this_model` resolves to the view name in this context, not the physical table.

**SQL example:**

```sql
MODEL (
  name analytics.customers,
  kind INCREMENTAL_BY_UNIQUE_KEY (
    unique_key customer_id
  )
);

SELECT customer_id, full_name, email, customer_segment
FROM raw.customers;

/* Grant read access when the view is created or updated */
ON_VIRTUAL_UPDATE_BEGIN;
JINJA_STATEMENT_BEGIN;
GRANT SELECT ON VIEW {{ this_model }} TO ROLE analyst_role;
JINJA_END;
ON_VIRTUAL_UPDATE_END;
```

***

## Project-level statements (`modelDefaults`)

You can define statements that run for every model by setting them in `modelDefaults` inside `config.yaml`. Model-level statements run after project-level ones.

```yaml
modelDefaults:
  dialect: snowflake
  preStatements:
    - "SET query_timeout = 300000"
  postStatements:
    - "@IF(@runtime_stage = 'evaluating', ANALYZE @this_model)"
  onVirtualUpdate:
    - "GRANT SELECT ON @this_model TO ROLE analyst_role"
```

***

## Useful variables in statements

| Variable                    | Description                                                                       |
| --------------------------- | --------------------------------------------------------------------------------- |
| `@this_model`               | The current model's table or view name.                                           |
| `@runtime_stage`            | Current execution stage: `creating`, `evaluating`, or `testing`.                  |
| `@IF(condition, statement)` | Runs a statement only when the condition is true.                                 |
| `@start_date`, `@end_date`  | Time range macros for incremental models.                                         |
| `{{ this_model }}`          | Jinja equivalent of `@this_model`, for use inside `JINJA_STATEMENT_BEGIN` blocks. |

***

## Full reference

For parameterized statements, conditional logic with `@runtime_stage`, and engine-specific examples, see [Statements](/concepts/resources/vulcan/components/model/statements.md).

***

## Next step

With data models and statements understood, see [Macros](/build/stage-2-productize/define-models-and-logic/data-models/macros.md) to write reusable SQL fragments like `@safe_ratio` and `@revenue_order_filter`.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://v2.dataos.info/build/stage-2-productize/define-models-and-logic/data-models/statements.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
