---
title: "SQL"
---

{/* Auto-generated by scripts/generate_core_action_docs.py; do not edit by hand. */}

## `core.sql.execute_query`

Execute a parameterized SQL query on an external database.

### Secrets

Required secrets:

- `sql`: required values `CONNECTION_URL`.

### Inputs

<ParamField path="query" type="string" required>

SQL query to execute. Use :param_name syntax for bound parameters. Do NOT use Tracecat expressions in the query string.

</ParamField>

<ParamField path="bound_params" type="object | null">

Bound query parameters as a dictionary (injected with :param_name syntax). Supply dynamic values here, NOT within the query string. This is required for safe, parameterized SQL queries.

Default: `null`.

</ParamField>

<ParamField path="fetch_one" type="boolean">

Return a single row instead of a list of rows. Defaults to False, which fetches all rows.

Default: `false`.

</ParamField>

<ParamField path="max_rows" type="integer">

Maximum number of rows to return. Default 200. Prevents accidentally returning huge result sets.

Default: `200`.

</ParamField>

### Examples

**Query an external database**

```yaml
- ref: recent_logins
  action: core.sql.execute_query
  args:
    query: |
      SELECT user_email, last_login_at
      FROM user_logins
      WHERE last_login_at >= :cutoff
      ORDER BY last_login_at DESC
    bound_params:
      cutoff: ${{ TRIGGER.cutoff }}
    max_rows: 100
```

## `core.duckdb.execute_sql`

Execute SQL in an in-process DuckDB engine

### Inputs

<ParamField path="sql" type="string" required>

SQL to execute in an in-process DuckDB connection.

</ParamField>

### Examples

**Run in-process SQL**

```yaml
- ref: aggregate_results
  action: core.duckdb.execute_sql
  args:
    sql: |
      WITH findings(id, severity) AS (
        VALUES (1, 'high'), (2, 'low'), (3, 'high')
      )
      SELECT severity, count(*) AS count
      FROM findings
      GROUP BY severity
      ORDER BY count DESC
```
