Skip to main content

Microsoft SQL

Read and write a Microsoft SQL Server database from a workflow: Azure SQL Database, SQL Server on premises or in a VM, and Azure SQL Managed Instance.

The Module adds four Tasks: Query rows, Run statement, Call stored procedure and Insert rows. Each Environment points at one database with its own credentials, so a Test Environment can work on a copy while Production works on the real one.

What you can build​

  • Look a customer up: Query rows with First row finds the record whose email matches {email} and gives you {rows.Name} and {rows.Status} for the conversation.
  • Save a lead: Run statement inserts the form's fields and returns the new id with OUTPUT INSERTED.Id, or Insert rows writes {form.fields} straight into the table.
  • Load a spreadsheet: File Query reads an uploaded Excel file as rows, Insert rows writes them into a staging table in one transaction.
  • Report: Query rows with All rows feeds a list to File Query (an Excel attachment) or to an AI task (a summary).
  • Business logic that lives in the database: Call stored procedure runs it with named parameters and reads its output parameters and return value.

Requirements​

  • A SQL Server the engine can reach over the network (TCP 1433, or the port you choose). Azure SQL: allow the engine's outbound addresses in the server firewall, or use a private endpoint.
  • A login with the least rights the flows need (db_datareader for queries, INSERT/UPDATE on the tables they change, EXECUTE on the procedures they call).

Configuration​

SettingRequiredNotes
ServeryesHost name, with the port after a comma when it is not 1433 (sql.example.com,1434) or a named instance after a backslash (sql.example.com\SALES).
DatabaseyesThe database to open.
Sign in withyesSQL Server login: a user name and password created in the database. Microsoft Entra ID user: an Entra account (name@domain) and its password. Microsoft Entra ID service principal: an Entra application's client id and client secret.
User name / PasswordyesThe credentials of the chosen sign-in. Stored securely.
EncryptionnoRequired (TLS) is the default and what Azure SQL needs. Strict (TDS 8.0) for SQL Server 2022+ configured for it. If the server offers it only for an old server on a private network.
Trust the server certificatenoOn for a self-signed certificate on a server you control. Keep it off for Azure SQL.
Command timeout (seconds)no1–300. Empty: 30. A Task can set its own.
Connection stringnoAdvanced: a full connection string instead of the fields above. It must carry its own credentials: Integrated Security and managed identities are refused, since they would use the engine's own identity.

The Module never uses the engine's identity to sign in, and refuses servers on private networks (loopback, 10.x, 192.168.x, 172.16–31.x, link-local) and local instances unless the platform operator allows them (MsSql:AllowPrivateNetworks in the engine's configuration; MsSql:AllowIntegratedSecurity for LocalDB in local development).

Variables in SQL​

Write {variables} in the SQL where a value goes. They are passed to the server as parameters, never pasted into the text, so whatever a visitor typed can only be compared, never executed:

SELECT Id, Name, Email FROM dbo.Customers WHERE City = {form.fields.city} AND Created >= {since}
  • A value keeps its type: a number stays a number, a date a date, text stays text. Text from a form is text; the server converts it to the column's type when it compares ('12' to an int column works).
  • A variable that is not set is NULL. WHERE Email = {email} matches nothing when {email} is empty; use WHERE ({email} IS NULL OR Email = {email}) for an optional filter.
  • A variable holding a list of plain values becomes a list of parameters: WHERE Id IN ({ids}) runs as IN (@p0, @p1, @p2). An empty list runs as IN (NULL), which matches nothing.
  • A variable holding a record or a list of records travels as JSON text: SELECT * FROM OPENJSON({order.lines}) WITH (Sku nvarchar(50), Qty int).
  • {execution.now} and {execution.today} work too: WHERE Due < {execution.today}.
  • Braces that are not a variable ('{"a":1}', {{) are left alone.

Names (tables, columns, procedures) cannot be parameters. The Task fields that take a name (Table, Procedure) accept {variables} as templates and quote the result as [schema].[name]; the SQL fields do not substitute names.

Query rows​

Runs a SELECT (or any query that returns rows) and puts the rows in a variable.

PropertyNotes
SQLThe query, in T-SQL, with {variables} as parameters.
Variable getsAll rows: a list of records ({rows.0.Name}, or a loop). First row: one record ({rows.Name}). Single value: the first column of the first row (SELECT COUNT(*) …).
Timeout (seconds)1–300. Empty: the configuration's default.
Empty resultOk: the variable is an empty list, an empty record or null. Error: the Task takes its error flow with {sqlError.code} = no_rows.
Rows variableWhere the rows go. Default {rows}.
Store result in{result.rows} (the count), {result.columns} (the names, comma separated), {result.durationMs}.

At most 20,000 rows and 8 MB go into variables; a larger result fails with too_many_rows / too_large: add TOP or a filter. Unnamed columns are called column1, column2…; a duplicate name gets a number.

Values: numbers, text, bit as true/false, dates as date-times; uniqueidentifier as text, varbinary as base64, time as hh:mm:ss.

Run statement​

Runs an INSERT, UPDATE, DELETE, MERGE or a whole batch, and reports the rows affected.

PropertyNotes
SQLThe statement(s), with {variables} as parameters. Several statements separated by ; run as one batch.
Run in a transactionOn (default): the batch is committed at the end or rolled back on an error. Off: each statement commits as it runs.
Timeout (seconds)1–300. Empty: the configuration's default.
No row affectedOk: {result.affected} is 0. Error: the Task takes its error flow with no_rows (an UPDATE whose WHERE matched nothing).
Returned rowThe first row the batch returns, when it returns one: INSERT … OUTPUT INSERTED.Id VALUES (…) gives {returned.Id}; SELECT SCOPE_IDENTITY() AS Id works too. Empty: not kept.
Store result in{result.affected} (the rows changed; -1 when the batch starts with SET NOCOUNT ON), {result.durationMs}.
INSERT INTO dbo.Leads (Name, Email, Source)
OUTPUT INSERTED.Id, INSERTED.Created
VALUES ({form.fields.name}, {form.fields.email}, 'website')

Call stored procedure​

Calls a procedure with named parameters and collects its first result set, its output parameters and its return value.

PropertyNotes
Procedureschema.name (dbo.usp_GetOrders); supports {variables}.
ParametersOne row per parameter: Name (with or without @), Value (supports {variables}; a value that is exactly one {variable} keeps its type, other text is a template; empty is NULL), Direction (In, Out, In/Out), Type (Auto, Text, Integer, Number, True/false, Date & time, Guid). Set the Type on output parameters so the value comes back as the right type.
Rows variable getsWhat to keep of the first result set: All rows, First row, Single value, or Nothing.
Outputs variableThe output parameters by name ({outputs.Total}) and the return value ({outputs.returnValue}).
Store result in{result.returnValue}, {result.rows}, {result.affected}, {result.durationMs}.

Insert rows​

Inserts the records a variable holds into a table, by bulk copy, in one transaction: either every row goes in or none does.

PropertyNotes
Tableschema.table; supports {variables}.
RowsOne {variable} holding one record ({form.fields}) or a list of records ({rows} from Query rows, the rows of a Data query, a CSV read with File Query).
ColumnsOnly these fields of the records are inserted, comma separated. Empty: every field. A field missing from a record is NULL (or the column's default).
Ignore unknown fieldsOn: a field the table has no column for is skipped. Off (default): it fails the Task with invalid_input, naming the fields.
Keep identity valuesOn: a record's value for an IDENTITY column is written as it is. Off: the server numbers the rows.
Fire triggersOn: the table's INSERT triggers run. Off (default): a bulk insert skips them.
Batch sizeRows sent at a time, 1–10000. Empty: 1000.
Store result in{result.inserted}, {result.columns}, {result.durationMs}.

Fields are matched to columns by name, any case. Values are converted to the column's type by the server's rules ("12" into an int column, an ISO date into a datetime2); a value that does not fit fails the whole insert with invalid_data. Check constraints are always enforced. At most 100,000 rows per Task.

Errors​

Every Task takes its error flow when something goes wrong, and writes {sqlError}:

{sqlError.code}When
invalid_inputThe Task is not set up correctly: empty SQL, a bad variable, an invalid table name, an unknown field.
missing_configurationThe Environment's connection is missing or not allowed.
connection_failedThe server could not be reached, or is on a private network the engine may not reach.
login_failedThe server refused the login, or the database does not exist for it.
permission_deniedThe login may not do this.
invalid_sqlThe SQL could not be parsed, or names an object or column that does not exist.
invalid_dataA value could not be converted to the column's type, or is too long for it.
duplicateA unique or primary key already holds the value.
constraintA foreign key, check or NOT NULL constraint refused the change.
deadlockThe statement was chosen as a deadlock victim; it can be retried.
timeoutThe command took longer than its timeout.
raisedThe procedure or statement raised its own error (THROW / RAISERROR with a number of 50000 or more); {sqlError.detail} is its text.
no_rowsNo row came back, or no row was changed, and the Task was told to fail then.
too_many_rows, too_largeThe result does not fit in a variable.
sql_errorAny other server error.

{sqlError.message} is a short sentence safe to show an end user; {sqlError.detail} is the server's own text, for the author; {sqlError.number} is the SQL Server error number (0 when the error did not come from the server). A Switch on {sqlError.code} sends duplicate to a "you are already registered" reply and everything else to a generic failure.

Log messages never include the password or the connection string.