Database Query
Ask your organisation's own database a question no node quite answers. Read versus write mode, how results come back, and the safety rails worth keeping on.
The CRM Manager node answers most questions about your data: find a contact, list deals, search tasks. What it cannot do is the question with a shape — contacts with no activity in sixty days who still have an open deal, counted by owner.
Database Query runs SQL against your organisation's own database and hands the rows back to the workflow.
Try CRM Manager first, every time. A search there is safer, readable by anyone, and survives a schema change. Reach for SQL when the question genuinely needs a join, an aggregate, or a filter on a custom field — which crm_manager search cannot do.
A worked example
Monday morning, a list
The builder composes the SQL; this node runs it.
Scroll for all 4 steps →
The query here is not typed into the node — it is {{sqlbuilder_1.final_sql}}, coming from the node before it. That pairing is the subject of the next lesson; this one is about what happens when the SQL runs.
The fields worth setting deliberately
| Field | What it holds |
|---|---|
use_current_org_db | Use current organization's database Defaults to true. |
query | SQL query to execute (supports template variables) |
query_mode | Query execution mode: read (SELECT only) or write (INSERT/UPDATE/DELETE) One of: read · write Defaults to read. |
parameters | Query parameters for safe binding |
result_format | Format for query results One of: array_of_objects · array_of_arrays · single_value · raw Defaults to array_of_objects. |
max_rows | Maximum number of rows to return Defaults to 1000. |
empty_result_handling | How to handle empty query results One of: null · empty_array · error Defaults to empty_array. |
timeout | Query timeout in seconds Defaults to 30. |
use_current_org_db
- What it holds
- Use current organization's database Defaults to
true.
query
- What it holds
- SQL query to execute (supports template variables)
query_mode
- What it holds
- Query execution mode: read (SELECT only) or write (INSERT/UPDATE/DELETE) One of:
read · writeDefaults toread.
parameters
- What it holds
- Query parameters for safe binding
result_format
- What it holds
- Format for query results One of:
array_of_objects · array_of_arrays · single_value · rawDefaults toarray_of_objects.
max_rows
- What it holds
- Maximum number of rows to return Defaults to
1000.
empty_result_handling
- What it holds
- How to handle empty query results One of:
null · empty_array · errorDefaults toempty_array.
timeout
- What it holds
- Query timeout in seconds Defaults to
30.
| Setting | Why it matters |
|---|---|
query_mode | Read is SELECT only and is the default. Write is a deliberate act, and the moment a workflow can change data it cannot un-change. |
max_rows | A ceiling, 1000 by default. A query that quietly returns 40,000 rows will take everything downstream with it. |
empty_result_handling | What “nothing matched” looks like: an empty array, null, or a raised error. Pick the one your next node can actually handle. |
timeout | Thirty seconds. A slow query holds the run open, so this is worth lowering for anything on a busy trigger. |
query_mode
- Why it matters
- Read is SELECT only and is the default. Write is a deliberate act, and the moment a workflow can change data it cannot un-change.
max_rows
- Why it matters
- A ceiling, 1000 by default. A query that quietly returns 40,000 rows will take everything downstream with it.
empty_result_handling
- Why it matters
- What “nothing matched” looks like: an empty array,
null, or a raised error. Pick the one your next node can actually handle.
timeout
- Why it matters
- Thirty seconds. A slow query holds the run open, so this is worth lowering for anything on a busy trigger.
Results arrive at {{databasequery_1.data}} — an array of objects in the default format, which is the shape a Loop wants. So “run a query, do something per row” is these two nodes and nothing else.
Parameters, not string-building
The query field takes templates, so it is tempting to paste a value straight into the SQL. Use parameters instead — it binds values safely rather than splicing them into the statement.
-- Fragile: whatever the trigger holds becomes part of the statement
SELECT id, email FROM contacts WHERE lead_status = '{{trigger.new_data.status}}'
-- Bound: the value stays a value
SELECT id, email FROM contacts WHERE lead_status = :statusThe risk is not only the classic injection one. A value containing an apostrophe — a company called O'Brien & Co — breaks a spliced query outright, and that is the version you will actually meet.
Write mode
Write mode bypasses everything the CRM does around a change. An UPDATE here does not fire database triggers the way the app does, does not update the vector index, and does not leave the audit trail a CRM Manager write leaves. Use crm_manager for changes to CRM records, and keep write mode for the rare thing that genuinely has no node — a bulk correction, a table the CRM does not own.
If you do use it, set use_transaction so a half-finished statement rolls back rather than leaving the data halfway.
Try it
- Add a Database Query with
use_current_org_dbon and aSELECTthat returns a handful of rows. - Read
{{databasequery_1.data}}in a Loop and print one field per row. - Change the filter so nothing matches, and see what
empty_result_handlinggives your next node. - Set
max_rowsto 2 against a query you know returns more, and confirm it truncates rather than failing.
Next: SQL Builder — composing that query without writing SQL by hand.
Related lessons
Base rates — what a piece of evidence is actually worth
A face-recognition system that is 99.9% accurate and almost entirely wrong, and a number that sent an innocent woman to prison. Both are the same arithmetic, and it is the arithmetic that decides what any piece of evidence is worth.
ReadConfirmation and survivorship — what you never looked for
Two questions about evidence you did not go looking for. One is a rule you have to discover, and one is a pattern in five famous people — and in both, the thing that would have told you the truth is the thing nobody checks.
ReadLoss aversion, sunk cost and regression — what it costs you
Four questions you answer about yourself rather than about a scenario, and your own answers are the finding. Then the pattern that makes praise look useless and criticism look like it works, whatever you actually do.
Read
