Update in Database

Action action.database_update

Changes the rows that match your conditions in a local SQLite database. Rows that don't match are left exactly as they were.

Update in Database sets new values on existing rows. It never creates a row and it never touches a row your conditions don't match; pair it with Find in Database first if you want to see what a change will hit before you run it for real.

Each database is one ordinary .sqlite file under Watchflows' Application Support folder. Open it in any SQLite tool you like; it is your file. Naming, privacy and deletion live in Databases.

Ports

DirectionNameData TypeDescription
InputInputAnyIncoming payload, passed through, and the source of every {{variable}} the node resolves
OutputOutputAnyThe incoming payload merged with this node's result keys

No conditions means the node refuses to run. A blank condition list would reach every row, so this node stops instead of guessing what you meant. If that really is what you want, turn on No conditions — reach EVERY row; it is a deliberate switch, and the node is badged destructive while it is on.

Conditions

A list of conditions (a column, an operator, and usually a value) decides which rows change. The ALL / ANY switch beside the Where heading decides whether every condition has to match or just one.

The eighteen operatorsThe value cell holds
equals · does not equalone value
contains · does not contain · starts with · ends withone value
is greater than · is at least · is less than · is at mostone value
older than (days) · within last (days)a number of days, see below
is one of · is not one ofa list, see below
is null · is not null · is empty · is not emptynothing; the cell shows a

There is no matches regex and no between: the first belongs in a Condition node on the payload, the second is two conditions. Three operators behave more helpfully than plain SQL would:

A list is a list, however it got there

is one of and is not one of compare against several values at once, and a list reaches them by exactly three routes:

  • You type them. The cell reads red, blue, green as three values: commas separate, spaces around them don't matter, and blanks are dropped.
  • One variable holds them. Write {{tags}} on its own and the variable's real array is unpacked, the parts a Split produced, an array a JSON Parse pulled out, a list an AI node returned. A variable holding a single plain value counts as a list of one. What does not work is a variable holding the text "red, blue": that is one value with a comma in it, and it matches a row only if a cell literally reads red, blue.
  • The AI Flow Builder writes them. A drafted flow supplies a real JSON array, for the same reason: a comma-joined string would match nothing.

An empty list is an answer rather than a mistake: is one of nothing matches no row, is not one of nothing matches every row.

Comparisons against a number compare numerically even when the column holds text; imported data is full of numbers stored as strings, and a text comparison would rank "10" below "9". Match capitalisation exactly is off by default, so equals, contains and friends ignore case.

older than (days) and within last (days) read a date column against a window of time; the value is a number of days, never a date. created_at works out of the box, and a column of your own works too as long as it holds an ISO-8601 timestamp.

Fields to change

Each field is a column and the value to set it to. A field's value is either a variable or a literal, and the two are read differently on purpose.

You writeStored asWhy
{{text}}whatever type it already wasA variable on its own keeps the upstream value's real type; a number stays a number.
{{first}} {{last}}TEXTA mixed template renders to text, like every other value field in the app.
42INTEGERA literal that reads as a whole number is one.
3.5REAL…and one with a decimal point is a real number.
"00123"TEXTQuotes are the escape hatch. A serial number that happens to be all digits keeps its leading zero.
trueINTEGER 1See the note below.

SQLite has no boolean type. true is stored as 1 and reads back downstream as the number 1, not as true. That asymmetry is real and permanent, so match a flag with the condition equals 1, and test it downstream with {{done}} equals 1.

A field or condition that doesn't resolve

If a field to change is exactly {{something}} and that variable isn't in the node's input, the field is left out of the update rather than writing an empty value. If nothing resolves there is nothing to set, and the node fails rather than running a no-op update.

A condition behaves differently: a where-rule that cannot be worked out stops the run outright, because a condition that quietly vanished would change which rows an update reaches. That covers three ways a rule can be unfinished: a missing variable, a value like clip-{{n}} where n isn't there (never rendered as clip-), and a rule left without a column or without a value.

What this node hands downstream

KeyTypeDescription
rowsAffectedNumberHow many rows the update changed. 0 is a normal answer; it means the conditions matched nothing.

Sharing a flow that uses a database

This node finds its database by id, not by name, so renaming a database in Settings never breaks a flow. That id is per-machine, though: when a flow arrives from somebody else (a Community install, an emailed .watchflow), its Database field is blanked and marked needs setup, and the flow can't be enabled until you pick a database. It keeps the author's name for it, so you can see what it was. Watchflows never invents a database for an id it doesn't recognise.

Asking the Flow Builder for one

The AI Flow Builder can author this node, including the table, the field list, and the conditions and their operators. It refers to a database by name, the way you do, and Watchflows turns that name into the id. It is never handed an id, and it never invents one.