Running SQL with pbSQL
pbSQL is the Connection Explorer's free-form SQL workspace, for reading data with SQL rather than the
visual Query Builder. Where a Saved View or the Query Builder works over a single table, a pbSQL query
can span multiple tables and joins on one connection. The same SQL works on any connection type —
databases, APIs and CSV files — so joins, grouping and aggregation are available even for sources that
are not databases (see How Queries Run on Different Connections).
pbSQL is read-only: it runs SELECT queries and rejects anything that would change data or schema.
A pbSQL query opens as a workspace tab in the explorer's right pane, alongside table, record and relationship tabs.
Opening a pbSQL Query
- New query — in the navigator's Workspaces panel, click New pbSQL query to open an empty editor tab.
- Select Top 1000 — a table's quick action opens a pbSQL tab prefilled with
SELECT TOP 1000 * FROM [Connection].[table]and runs it, as a fast way to peek at a table. - Saved queries — queries you save (see Saving a Query) are listed in the Workspaces panel and reopen as tabs.
Writing a Query
pbSQL has one canonical form regardless of the connection. You write the query once, in the same syntax, whatever the connection is; how it runs then depends on the connection type (see How Queries Run on Different Connections).
The editor toolbar includes an Info button (top-left, beside Save) that opens pbSQL Help
without leaving the workspace. It has two tabs: Syntax and Rules, a reference for the conventions
below, and Examples, a set of starter queries. Each example can be copied or opened as a new pbSQL
query. On the Examples tab, selecting a connection and table writes them into every example in place of
the [connection_name] and [table_name] placeholders.
Qualifying Tables by Connection
Every table is qualified by the connection it belongs to, using bracket-quoted identifiers so connection and table names can contain spaces:
SELECT number, short_description
FROM [ServiceNow Prod].[incident]
WHERE priority = '1'
A schema-qualified table adds the schema between the connection and the table:
SELECT * FROM [Sales DB].[dbo].[customers]
Where a connection names a schema (see Schemas), a table written without one resolves in that schema. Naming a schema in the query overrides the connection's, so the form above is how to read a different schema through the same connection.
Multiple tables on the same connection can be joined:
SELECT c.name, o.total
FROM [Sales DB].[customers] c
JOIN [Sales DB].[orders] o ON o.customer_id = c.id
A single query may reference only one connection. Cross-connection (federated) queries are not supported.
Syntax Conventions
The canonical form follows SQL Server (T-SQL) conventions:
- Bracket-quoted identifiers —
[Connection].[table], and[column]where a name needs quoting. SELECT TOP nto limit rows (translated to each engine's own form, e.g.LIMITon PostgreSQL / MySQL / SQLite).- Single quotes for string literals:
WHERE status = 'open'.
You write this one form for every connection. On a database connection it is transpiled to that
engine's SQL before running — for example, SELECT TOP 3 * FROM [MySQL Prod].[orders] runs against
MySQL as SELECT * FROM orders LIMIT 3. On an API or CSV connection it runs in an embedded engine, as
described next.
How Queries Run on Different Connections
The connection type determines how a pbSQL query runs, but not how you write it.
- Database connections — the query is translated to the database's own SQL dialect and run by the database itself.
- API and CSV connections — these sources cannot run SQL themselves, so Precision Bridge reads the
referenced tables from the connection and runs the query — joins, grouping and aggregation included —
in an embedded engine inside the app. The result matches running it against a database, apart from how
LIKEhandles case (see below).
Because an API or CSV table is read into the app to be queried, each referenced table is subject to two
size limits: a row limit (100,000 rows by default) and a total-cell limit (rows × fields read,
5,000,000 by default) that bounds a wide table even when its row count is within range — a table of 200
fields, for example, reaches the cell limit at 25,000 rows. A query over a table that exceeds either
limit is rejected with a message rather than returning a partial result. Add a WHERE filter, or a
TOP/LIMIT, to bring the table under the limits; a filter that can be applied at the source reduces
how many rows are read.
Text matching with LIKE ignores case on CSV connections, so LIKE '%incident%' also matches
Incident. On an API connection it depends on the source's own matching, and on a database connection
it follows the database's rules.
Most column types — text, numbers, dates, times and durations — are read with their native type, so sorting, comparison and aggregation behave as they do on a database connection. File, List and Currency columns are read as text: they are composite or heterogeneous values, so ordering and comparison on them follow text rules rather than a type-aware order.
Running a Query
Click Run to execute the query against its connection. The editor is at the top of the tab and the results grid below it; the panes are independently resizable. While a query is running, Run becomes Stop; click it to cancel the query.
Results are paged by default: the grid fetches one page at a time, so a query that matches many rows still opens quickly and only the current page is held in the browser. For database connections the paging happens at the database; for API and CSV connections the app pages the pulled result. Use the grid's page navigation to move through the result, and its Page Size control to change how many rows a page holds.
Total Row Count
The total number of rows the query returns is counted in the background after each run and shown next to the results as "N rows total".
How that count is worked out depends on the query and the connection:
- On a database connection it runs as a
COUNTat the database. - On an API or CSV connection, a plain single-table query (and any
SELECT COUNT(*)) is counted directly at the source, so the total returns quickly even for very large tables. A query the source cannot count on its own — a join, aGROUP BYor other aggregate, or a filter that has to be applied in the app (such asLIKE) — is counted by reading its rows instead, which is bounded by the per-table row limit and can take longer.
While a count is running, a Stop counting control appears beside it to cancel it. If a count is stopped or does not complete, a Count Rows button lets you retry it on demand.
Loading a Whole Result (Client-Side Mode)
The settings cog beside Run opens Query settings, with a Server-side pagination toggle (on by default). Turning it off switches to client-side mode: the whole result — up to a Rows limit you choose (from 1,000 up to 1,000,000) — is loaded into the browser at once, so the grid can sort, filter and page all of it locally without further round-trips. The results line then reads "Fetched N rows" with "out of M total" beneath it.
Client-side mode is best for a bounded result you want to slice locally. A very large result loads every row into the browser, which can be slow and may make the tab unresponsive, so the dialog shows a warning while it is off and the Rows limit bounds how much is loaded. For large or open-ended results, leave server-side pagination on.
Saving a Query
Click Save to store the editor's SQL as a reusable .pbsql query. The first save prompts for a name; after that, Save writes straight back to the same query. Saved queries are listed in the navigator's Workspaces panel and reopen as tabs, and are stored on the server rather than tied to a single connection.
The dropdown beside Save holds two more actions:
- Save As stores the current SQL as a new query, leaving the original unchanged. Use it to make a copy rather than overwrite the query you opened.
- Rename changes a saved query's name only. Its SQL and settings are left as they are, so any unsaved edits in the editor are kept. It is available once the query has been saved.
A saved query also stores its Query settings — the server-side pagination mode and, in client-side mode, the row limit — so reopening it restores the mode you last saved.
Read-Only Enforcement
pbSQL only reads data. Enforcement is layered:
- The query is validated before it runs. Only
SELECTqueries are allowed; inserts, updates, deletes and schema changes are rejected — includingSELECT ... INTO, which would create a table. - The query runs in a read-only database transaction on every engine that supports one (PostgreSQL, CockroachDB, MySQL, MariaDB, Oracle, SQLite), so the connection refuses writes for the duration of the query even though the same connection may be used for writes elsewhere (for example by a Migrate Records step).
- A statement timeout cuts off a runaway query on engines that support one, as a safeguard against a query that never returns.
- For API and CSV connections, the query runs in an embedded engine that can see only the data read from the connection. It has no access to the host's filesystem or network, and each query is bounded by a memory limit and a timeout, so a query that would use too much memory or run too long is stopped rather than left to exhaust resources.
For SQL Server connections, there is no session-level read-only mode, so pbSQL relies on the query validator rather than a database-enforced read-only transaction. For query-heavy use, connect using a read-only database login, and keep
xp_cmdshelland similar extended procedures disabled (they are disabled by default). This ensures the database itself refuses anything beyond reading, regardless of the query submitted.
Related Documentation
- Connection Explorer — the explorer layout, the visual Query Builder, records, relationships and saved views.
- Filtering — the visual filter language, an alternative to SQL for single-table queries.
Comments
0 comments
Please sign in to leave a comment.